How to smoothly change the execution time of an infinite animation (in CSS3) with Javascript

advertisements

I have searched through a lot of questions related to my question in stackoverflow but i haven't found one yet that answers my question with plain JavaScript (not using libraries of any kind).

My problem is that I have an infinite animation with CSS3 i.e.:

.clockwiseAnimation {
    top: 270px;
    left: 200px;
    position: absolute;

    -webkit-animation: clockwise 4s linear infinite; /* Chrome, Safari 5 */
    -moz-animation: clockwise 4s linear infinite; /* Firefox 5-15 */
    -o-animation: clockwise 4s linear infinite; /* Opera 12+ */
    animation: clockwise 4s linear infinite; /* Chrome, Firefox 16+, IE 10+, Safari 5 */
}
@-webkit-keyframes clockwise {
    from { -webkit-transform: rotate(0deg) translateX(150px) rotate(0deg); }
    to { -webkit-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
@-moz-keyframes clockwise {
    from { -moz-transform: rotate(0deg) translateX(150px) rotate(0deg); }
    to { -moz-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
@-o-keyframes clockwise {
    from { -o-transform: rotate(0deg) translateX(150px) rotate(0deg); }
    to { -o-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
@keyframes clockwise {
    from { transform: rotate(0deg) translateX(150px) rotate(0deg); }
    to { transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}

This animation allows me to spin (clockwise) whatever tag that has the class "clockwiseAnimation".

What I want to do is to change the time of execution (I'll call it speed) of the animation with javascript like:

HTML:

<span id="someID" class="clockwiseAnimation">sometext</span>

JavaScript:

var style = document.getElementById("someID").style,
speed = 6;
//obviously the speed is dynamic within my site (through an `<input type="range">`)
//for the purposes of this example I set the speed to a different value(6seconds) than the original value(4seconds).
style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = speed + "s";

It works when I pause and then play(by play I mean UNPAUSE not restart) the animation, i.e.:

var style = document.getElementById("someID").style;
some = 6; //it is dynamic (as I pointed out before)

//pause
style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "paused";

//change speed
style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = speed + "s";  

//play (== UNPAUSE) //UPDATE: Added the timeout because I can't get it to work any other way.
setTimeout(function(){
            style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "running";
        },1);

UPDATED:

And it works! BUT, it has a big RANDOM jump in the animation, meaning that when I change the "speed" with the "<input type="range"> slider" the element jumps to a random location (not the beginning nor the end of the animation just a random location).

NOTE: Pause and play works very smooth without changing the "speed" of the animation.

My question(s): Can I change the "speed" of the animation smoothly WITH JavaScript? (WITHOUT the jumping) If the answer is: "There is not a way to do it smoothly throughout the animation execution", then: Is there a way to change it in the next iteration of the infinite animation? If so: Then how can I tell it to start in the next iteration and how to know which is the next iteration if I set the animation to infinite (animation-iteration-count property of the element that is doing the animation always returns "infinite").

Here is an example. I hope it helps.


What may be occurring is that the animation-duration "change" could be "jumping" to the point in the animation corresponding to the "changed" @keyframes - based on the total "changed" animation duration..

If the animation-duration began from (or 0%) proceeded to to (or 100%), the corresponding @keyframes "position" may be changed as well.

For example if the original animation-duration was 4s (or, 4000ms) at approximately 2s (or, 2000ms), the corresponding keyframes may be at approximately 50%, or

at 2 seconds into 4 second animation 50% { -webkit-transform: rotate(180deg) translateX(150px) rotate(-180deg); }

when the animation-duration is dynamically changed, the corresponding keyframes may be "changed" to the matching % point, or, a larger duration span for the same effect. The animated element may appear to go forwards or backwards, or hve a "jumping" due to it re-positioning itself within the "changed" corresponding keyframes and animations.

There is also 1s setTimeout function, that may or may not actually have a 1s duration.

It may be possible to "smoothly" "jump" to the newly "changed" position within the lengthier animation-duration the suggested transition effect or requestAnimationFrame (http://www.w3.org/TR/animation-timing/).

..

Try this:

html

<input type="range" id="speedSlider" min="2000" max="6000" value="4000">

css

    input {
    -webkit-transform: rotate(180deg);
    top : 50px;
    position : absolute;
}

.clockwiseAnimation {
 /* top: 270px;
    left: 200px; */
    left : 50%;
    top : 50%;
    position:absolute;

   /* css animation (no change) */
}

js

var speedSlider = document.getElementById("speedSlider");
    speedSlider.addEventListener("change", changeSpeed, false);
    function changeSpeed(e){
        var speed = Math.floor(speedSlider.value);
        var element = document.getElementById("span");
        var style = element.style;
        style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "paused";
        style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = String(speed) + "ms";
            style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "running";
    }