从JS以编程方式使用CSS转换的干净方法

Clean way to programmatically use CSS transitions from JS?

本文关键字:转换 方法 CSS JS 编程 方式使      更新时间:2023-09-26

正如标题所示,是否有适当的方法来设置一些初始CSS属性(或类)并告诉浏览器将其转换为另一个值?

例如(fiddle):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
st.transition = 'opacity 2s';
st.opacity = 1;

这将不会在Chrome 29/FFirefox 23中设置元素不透明度的动画。这是因为(来源):

[…]你会发现,如果你同时应用这两组属性,就会立即应用一组然后,浏览器尝试优化属性更改,忽略初始属性并阻止转换。在幕后,浏览器在绘制之前会批量处理属性更改这通常会加快渲染速度,但有时会产生不利影响影响。

解决方案是在应用两组属性。一个简单的方法就是访问DOM元素的offsetHeight属性〔…〕

事实上,黑客攻击在当前的Chrome/Firefox版本中确实有效。更新代码(fiddle-打开fiddle后点击Run再次运行动画):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
el.offsetHeight; //force a redraw
st.transition = 'opacity 2s';
st.opacity = 1;

然而,这是相当黑客,据报道在一些安卓设备上不起作用。

另一个答案建议使用setTimeout,这样浏览器就有时间执行重绘,但它也失败了,因为我们不知道重绘需要多长时间。猜测一个适当的毫秒数(30-100?)以确保重新绘制发生意味着牺牲性能,不必要地空转,希望浏览器在这段时间内发挥一些魔力。

通过测试,我发现了另一个在最新Chrome上运行良好的解决方案,使用requestAnimationFrame(fiddle):

var el = document.querySelector('div'),
    st = el.style;
st.opacity = 0;
requestAnimationFrame(function() {
    st.transition = 'opacity 2s';
    st.opacity = 1;
});

我假设requestAnimationFrame在执行回调之前要等到下一次重新绘制开始之前,因此浏览器不会批量处理属性更改。这里不完全确定,但在Chrome 29上运行良好。

更新:经过进一步的测试,requestAnimationFrame方法在Firefox 23上运行得不太好——它似乎在大多数时候都失败了。(小提琴)

是否有适当的或推荐的(跨浏览器)方式来实现这一点?

目前没有一种干净的方法(不使用CSS动画——请参阅James Dinsdale的回答,了解使用CSS动画的示例)。有一个规范错误14617,不幸的是,自2011年提交以来,该错误一直没有得到执行。

setTimeout在Firefox中不能可靠地工作(这是故意的)。

我不确定requestAnimationFrame——对原始问题的编辑表明它也不可靠,但我没有进行调查。(更新:至少有一位Firefox核心开发人员认为requestAnimationFrame是可以进行更多更改的地方,而不一定能看到以前更改的效果。)

强制回流(例如通过访问offsetHeight)是一种可能的解决方案,但为了使转换工作,它应该足以强制重新样式(即getComputedStyle:https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/

window.getComputedStyle(elem).opacity;

请注意,仅仅运行getComputedStyle(elem)是不够的,因为它是延迟计算的。我相信无论你从getComputedStyle中请求哪个属性,restyle仍然会发生。请注意,要求几何图形相关的属性可能会导致更昂贵的回流。

有关回流/重新造型/重新喷漆的更多信息:http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/

自2013年以来,情况发生了变化,因此有一个新的答案:

您可以使用Web动画。它们在Chrome 36和Firefox 40中原生实现,并且所有其他浏览器都有polyfill。

示例代码:

var player = snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

您不需要太多的JavaScript来实现您想要的效果,只需使用CSS关键帧和动画即可获得相同的效果。

div {
    opacity: 0;
}
div.fadeIn {
    -webkit-animation: fadeIn 2s forwards;
    animation: fadeIn 2s forwards;
}
@keyframes fadeIn {
    0% {opacity: 0;}
    100% {opacity: 1;}
}
@-webkit-keyframes fadeIn {
    0% {opacity: 0;}
    100% {opacity: 1;}
}

如JsFiddle所示,它可以在页面加载(已经添加了类)时工作,也可以在动态添加类以触发动画时工作。


  1.   <script  src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
       <script>
         $(document).ready(function(){
    $('a.hiw*').click(function(){	
    	
    	id = this.id;
    	dval = $('#'+id).attr('data-value');
    	if (dval == 0) {
    		$('a.hiw*').attr('data-value','0');
    		$( ".hiw-popup" ).remove();
    		$('#'+id).attr('data-value','1');
    		$('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id);
    	}else{
    		$('#'+id).attr('data-value','0');
    		$( ".hiw-popup" ).remove();
    	}	
    });
    });
    var ahiw = function(id){	
    	dval = $('#'+id).attr('data-value');
    	if (dval == 0) {
    		$('a.hiw*').attr('data-value','0');
    		$( ".hiw-popup" ).remove();
    		$('#'+id).attr('data-value','1');
    		$('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id);
    	}else{
    		$('#'+id).attr('data-value','0');
    		$( ".hiw-popup" ).remove();
    	}
    }
       
    </script>
    /* Chrome, Safari, Opera */
    @-webkit-keyframes animation-hiw-icon {
      from {
        background-color: #d9d9d9;
      }
      to {
        background-color: #4ad18f;
      }
    }
    /* Standard syntax */
    @keyframes animation-hiw-icon {
      from {
        background-color: #d9d9d9;
      }
      to {
        background-color: #4ad18f;
      }
    }
    /* Chrome, Safari, Opera */
    @-webkit-keyframes animation-hiw-prog {
      from {
       background-color: #d9d9d9;
        width: 0%
      }
      to {
        width: 100%;
    	 background-color: #4ad18f;
      }
    }
    /* Standard syntax */
    @keyframes animation-hiw-prog {
      from {
        width: 0%
      }
      to {
        width: 100%;
      }
    }
    /* Chrome, Safari, Opera */
    @-webkit-keyframes animation-hiw-pop {
      from {
        opacity: 0.5;
    	background-color: #d9d9d9;
        -ms-transform: scale(0.8); /* IE 9 */
        -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
        transform: scale(0.8);
      }
      to {
       background-color: #4ad18f;
        opacity: 1;
        font-weight: normal;
        -ms-transform: scale(.8); /* IE 9 */
        -webkit-transform: scale(.8); /* Chrome, Safari, Opera */
        transform: scale(.8);
      }
    }
    /* Standard syntax */
    @keyframes animation-hiw-pop {
      from {
      background-color: #d9d9d9;
        opacity: 0.5;
        -ms-transform: scale(0.8); /* IE 9 */
        -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
        transform: scale(0.8);
      }
      to {
      background-color: #4ad18f;
        opacity: 1;
        font-weight: normal;
        -ms-transform: scale(.8); /* IE 9 */
        -webkit-transform: scale(.8); /* Chrome, Safari, Opera */
        transform: scale(.8);
      }
    }
    /*Animation Trigger*/
    .run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 {
      -webkit-animation-play-state: running; /* Safari and Chrome */ 
      animation-play-state: running;
    }
    .run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 {
      -webkit-animation-play-state: running;
      animation-play-state: running;
    }
    .hiw-progress:after {
      content: "";
      width: 0%;
      height: 5px;
      background: #4ad18f;
      display: inline-block;
      position: absolute;
      top: 0;
      left: 0;
      -webkit-animation: animation-hiw-prog 5s linear forwards;
      animation: animation-hiw-prog 5s linear forwards;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .white-well {
      background-color: #fff;
      padding: 10px 15px;  border-radius: 5px;
      border: 1px solid #f1f1f1;
    }
    .hiw-popup {
      position: absolute;
       width: 100%;
      z-index: 9;
      margin: 30px 0 0 -15px;
      padding: 0px 15px !important;
      border-color: rgba(0, 0, 0, 0.25) !important;
      box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
      -webkit-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
      -mz-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
    }
    .hiw-popup .arrow {
      position: absolute;
      display: block;
      width: 0;
      height: 0;  border-color: transparent;
      border-style: solid;
      border-width: 11px;
      left:90%;
      margin-left: -11px;
      border-top-width: 0;
      border-bottom-color: rgba(0, 0, 0, 0.25);
      top: -11px;
    }
    .hiw-popup .glyphicon {
      margin-bottom: 10px;
      margin-right: 0px !important;font-weight:bold;
      background-color: #ffffff;color:#222222 !important;
    }
    .white-well .glyphicon {
       background-color: #ffffff!important;
      border-radius: 76px;margin-top: -3px;color:#d9d9d9 !important;
      padding: 5px 9px 8px;
      color: #fff;
      box-shadow: 0px 0px 3px #222222;
      border: 3px solid #ffffff;
    }
    .glyphicon {
      position: relative;
      top: 1px;
      display: inline-block;
      font-family: 'Glyphicons Halflings';
      font-style: normal;
      font-weight: normal;
      line-height: 1;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    .clearfix:before, .clearfix:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .modal-footer:before, .modal-footer:after, .review:before, .review:after, .panel-body:before, .panel-body:after {
      content: " ";
      display: table;
    }
    .animation-hiw:nth-child(1) {
      -webkit-animation-delay: .2s;
      animation-delay: .2s;
    }
    .hiw-icon5 {
      -webkit-animation: animation-hiw-icon 0.2s forwards;
      animation: animation-hiw-icon 0.2s forwards;
      -webkit-animation-delay: 5s;
      animation-delay: 5s;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .hiw-icon4 {
      -webkit-animation: animation-hiw-icon 0.2s forwards;
      animation: animation-hiw-icon 0.2s forwards;
      -webkit-animation-delay: 3.75s;
      animation-delay: 3.75s;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .hiw-icon3 {
      -webkit-animation: animation-hiw-icon 0.2s forwards;
      animation: animation-hiw-icon 0.2s forwards;
      -webkit-animation-delay: 2.25s;
      animation-delay: 2.25s;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .hiw-icon2 {
      -webkit-animation: animation-hiw-icon 0.2s forwards;
      animation: animation-hiw-icon 0.2s forwards;
      -webkit-animation-delay: 1s;
      animation-delay: 1s;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .hiw-icon1 {
      -webkit-animation: animation-hiw-icon 0.2s forwards;
      animation: animation-hiw-icon 0.2s forwards;
      -webkit-animation-delay: .2s;
      animation-delay: .2s;
      -webkit-animation-play-state: paused;
      animation-play-state: paused;
    }
    .animation-hiw {
      -webkit-animation: animation-hiw-pop 0.2s forwards; /* Chrome, Safari, Opera */
      animation: animation-hiw-pop 0.2s forwards;
      -webkit-animation-play-state: paused; /* Safari and Chrome */
      animation-play-state: paused;
      opacity: 0.5;
      -ms-transform: scale(0.8); /* IE 9 */
      -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
      transform: scale(0.8);
        background: #d9d9d9;
       width: 15%;
      padding: 2% 1%;
      height: 140px;
      color: #ffffff;  float: left;
    }
    .animation-hiw:nth-child(1){ -webkit-animation-delay: .2s; animation-delay: .2s; }
    .animation-hiw:nth-child(2){ -webkit-animation-delay: 1s; animation-delay: 1s; }
    .animation-hiw:nth-child(3){ -webkit-animation-delay: 2.25s; animation-delay: 2.25s; }
    .animation-hiw:nth-child(4){ -webkit-animation-delay: 3.75s; animation-delay: 3.75s; }
    .animation-hiw:nth-child(5){ -webkit-animation-delay: 5s; animation-delay: 5s; }
    hiw {
      visibility: hidden;
      font-size: 12px;
      font-style: italic;
      text-align: right;
      float: right;
    }
    <body>
    <a href="javascript:void(0);" class="hiw hidden-xs" id="hiw_1" data-value="1" style="float:LEFT;margin-right:10px;color: #4ad18f;font-size: 12px;padding:0px 0px 5px 0px;">How it works</a>
    </body>

    #

这是一个工作版本。你自己看看。

在Chrome、Firefox、Opera上测试。

在我的firefox版本中,它不支持style.transition,所以如果标准名称不可用,我会回退到供应商特定的名称。

http://jsfiddle.net/eNCBz/5/

var el = document.querySelector('div');
var VENDORS = ['Moz', 'Webkit', 'Ms', 'O'];
function getVendorSpecificName(el, prop) {
    var style = el.style;
    if (prop in style) {
        return prop;
    }
    prop = ucfirst(prop);
    for (var i = 0, l = VENDORS.length, name; i < l; i++) {
        name = VENDORS[i] + prop;
        if (name in style) {
            return name;
        }
    }
    return null;
}
function ucfirst(str) {
    return str && str.charAt(0).toUpperCase() + str.substring(1);
}
function toCamelCase(str) {
    return str.split('-').map(function (str, i) {
        return i > 0 ? ucfirst(str) : str;
    }).join('');
}
function animateCss(el, prop, from, to, duration) {
    var style = el.style,
        camel = toCamelCase(prop),
        vendorSpecific = getVendorSpecificName(el, camel);
    if (!vendorSpecific) {
        console.log(prop + ' is not supported by this browser');
        return false;
    }
    var transitionPropName = getVendorSpecificName(el, 'transition');
    if (!(transitionPropName in style)) {
        console.log('transition is not supported by this browser');
        return false;
    }
    style[vendorSpecific] = from;
    setTimeout(function () {
        style[transitionPropName] = prop + ' ' + duration + 's ease';
        setTimeout(function () {
            style[vendorSpecific] = to;
        }, 1);
    }, 1);
    return true;
}
animateCss(el, 'opacity', 0, 1, 2);

让我解释一下发生了什么:

  • 制作了一些辅助功能,如ucfirst、toCamelCase

    • style属性使用大小写名称
  • 如果标准名称不可用,请尝试查找供应商特定的样式属性名称

  • 利用setTimeout功能确保浏览器重新绘制

    • 据我所知,所有浏览器总是在超时时重新绘制

我试着让它更通用,这样其他属性也可以应用,比如颜色或背景。

希望这能有所帮助!