Manually stepping through CSS animation with JavaScript

亡梦爱人 提交于 2019-12-03 11:42:12

With the built-in animation:

Unfortunately, no

The internals of the transition isn't exposed for JavaScript so you cannot tap into it to set or get the data. And this is for a purpose - if the data were exposed it would mean reduced efficiency as the JavaScript event queue had to be updated. As JS is single-threaded and the animation goes on a separate thread you'll would soon loose the benefit of it running in compiled code internally on a separate thread.

You can however make your own transitions. This involve calculation transitions yourselves.

This is not as complicated as it sounds like as you simply use an interpolation formula for what you want to animate:

current = source + (destination - source) * fraction;

For example, for color you can use it with the color component. Lets assume we have color objects with properties r, g, b:

var color1 = {r: 100, g: 200, b: 55};  //some random color
var color2 = {r: 0,   g: 100, b: 100};

var fraction = 0.5; //0-1

Here the current RGB would be:

r = color1.r + (color2.r - color1.r) * fraction;
g = color1.g + (color2.g - color1.g) * fraction;
b = color1.b + (color2.b - color1.b) * fraction;

For positions:

 var pos1x = 100;
 var pos1y = 100;
 var pos2x = 500;
 var pos2y = 250;

var fraction = 1; //0-1

posX = pos1x + (pos2x - pos1x) * fraction;
posY = pos1y + (pos2y - pos1y) * fraction;

And so forth.

By making wrapper functions you can easily calculate these and even put them in a loop to animate them.

Example function for setting transition between color 1 and color 2. Style can be ie. backgroundColor, color etc.:

function setColor(element, style, color1, color2, fraction) {

    var r = color1.r + (color2.r - color1.r) * fraction;
    var g = color1.g + (color2.g - color1.g) * fraction;
    var b = color1.b + (color2.b - color1.b) * fraction;

    element.style[style] = 'rgb(' + (r|0) + ',' + (g|0) + ',' + (b|0) + ')';
}

(the r|0 is simply cutting off the decimal part).

And for position, for example:

var pos1 = {x: 0, y: 0};
var pos2 = {x: 200, y: 0};

function setPosition(element, pos1, pos2, fraction) {

    var x = pos1.x + (pos2.x - pos1.x) * fraction;
    var y = pos1.y + (pos2.y - pos1.y) * fraction;

    element.style.left = x + 'px';
    element.style.top = y + 'px';
}

A simple demo (use Chrome or Aurora 23 to see sliders, slider comes in next version of FF 23).

Fiddle

Manually set transition at any point between source and destiny, or animate them.

You can't do that as you want it.

Your only posibility is to change play-state after a given delay.

In your case, since the animation lasts 0.5 seconds, to get the animation at 50% you should set a timeout of 0.25 seconds and then set animation-play-state : paused.

Of course that won't be exactly at 50%, don't trust the precision of this method.

editing

Added demo for webkit:

fiddle

The HTML is trivial

<div id="goflash">TEST</div>
<input type="button" value="animate" onclick="animate()">

And the CSS easy

#goflash {
    width: 200px;
    height: 200px;
    left: 35px;
    top: 35px;
    position: absolute;
    background-color: red;
}
.anm-flash {
    -webkit-animation-name: flash;
    -webkit-animation-duration: 5s;
    -webkit-animation-timing-function: linear;
    -webkit-animation-iteration-count: 1;
}

@-webkit-keyframes flash {
    from {   -webkit-transform: rotate(0deg);
             background-color: red;        }
    50% {    -webkit-transform: rotate(120deg);
             background-color: yellow;}
    to {    -webkit-transform: rotate(360deg);
            background-color: red;
    }
}

And the javascript is an extension from what you supplied:

function animate () {
    var gf = document.querySelector("#goflash");
    gf.classList.remove("anm-flash");
    setTimeout(function() {
        gf.classList.add("anm-flash");
        gf.style.webkitAnimationPlayState = "running";
    }, 50);
    setTimeout(function() {
        gf.style.webkitAnimationPlayState = "paused";
    }, 2550);
}

You reset the class, after a small pause start the animation, and a calculated delay after the start, you stop it. Since the animation time was 5s,and the initial delay 50 ms, the second delay has to be (5000/2) + 50. Since you have set now the play state to paused, to de able to re-run the animation you have to set the state to running again.

say you have only one animation over your element gf, you can simply control it with animation-delay and animation-play-state:

gf.__proto__.animate = function(percent) {
  this.style["animation-play-state"] = "paused";
  this.style["animation-delay"] = (
    (parseFloat(this.style["animation-duration"] || 1) * -percent) + "s"
  );
};

and you can get the computed style as following:

window.getComputedStyle(gf).background

to step through at any speed:

(function animation(time) {
  gf.animate( ((time || 0) % desireSpeed ) / desireSpeed );
  requestAnimationFrame(animation);
})();

note: this will override animation-delay from css so you'll probably want to keep it in a vairable and add it as an offset in gf.__proto__.animate().

Perhaps using CSS DOM to parse animation's intent (if that's even possible?) and then reconstructing everything in JavaScript.

But that's no mean feat!

I wonder if a CSS preprocessor would help constructing code like this. This is very much all in theory.

Yes, You can just overide the duration or timing of an animation. Hope I understood what you want to do:

http://jsfiddle.net/SEHyW/

var gf = document.querySelector("#goflash"),
    animationDuration = '1s'

gf.classList.remove("anm-flash");
setTimeout(function() {
  gf.classList.add("anm-flash");
  gf.style["-webkit-animation-duration"] = animationDuration;
}, 1000);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!