Clean way to programmatically use CSS transitions from JS?

五迷三道 提交于 2019-11-27 13:12:22

There isn't a clean way at this moment (without using CSS Animations -- see the nearby answer by James Dinsdale for an example using CSS Animations). There is a spec bug 14617, which unfortunately wasn't acted upon since it was filed in 2011.

setTimeout does not work reliably in Firefox (this is by design).

I'm not sure about requestAnimationFrame -- an edit to the original question says it doesn't work reliably either, but I did not investigate. (Update: it looks like requestAnimationFrame is considered at least by one Firefox core developer to be the place where you can make more changes, not necessarily see the effect of the previous changes.)

Forcing reflow (e.g. by accessing offsetHeight) is a possible solution, but for transitions to work it should be enough to force restyle (i.e. getComputedStyle): https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/

window.getComputedStyle(elem).opacity;

Note that just running getComputedStyle(elem) is not enough, since it's computed lazily. I believe it doesn't matter which property you ask from the getComputedStyle, the restyle will still happen. Note that asking for geometry-related properties might cause a more expensive reflow.

More information on reflow/restyle/repaint: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/

The situation has changed since 2013, so here's a new answer:

You could use Web Animations. They are implemented natively in Chrome 36 and Firefox 40 and there is a polyfill for all the other browsers.

Example code:

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();

You shouldn't need too much JavaScript to achieve what you want, just use CSS keyframes and animations for the same effect.

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;}
}

As shown in this JsFiddle it works either from page load (with class already added) or when dynamically adding the class to trigger the animation.


  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>

    #

Here's a working version. See it yourself.

Tested on Chrome, Firefox, Opera.

On my version of firefox, it does not support style.transition so I made it to fallback to vendor specific name if standard name is not available.

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);

Let me explain what is going on:

  • made some helper functions like ucfirst, toCamelCase

    • style property uses camel case names
  • try to find vendor specific style property name if standard name is not available

  • utilize setTimeout function to make sure browser to redraw

    • as far as I know, all browsers always redraw on timeout

I tried to make it more generic function so other properties can be applied as well, such as color or background.

Hope this helps!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!