Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I change the speed of a CSS animation?

I have animation:

img.rotate {
  animation-name: rotate;
  animation-duration: 1.5s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

I have an rotating image at infinite. Now I want change the speed of rotation with JS. I tried:

$('img').css('animation-duration', '2s');

The duration changes the speed, but the animation reboot to framekey 0% (the first), but I just want the animation continue like nothing happened; I don't want to go back to 0%.

How can I do that?

like image 544
Matrix Avatar asked Dec 02 '13 23:12

Matrix


2 Answers

It's a very difficult process to do so. Currently the only way to change an animation mid-animation is to use a setInterval to approximate the current keyframes percentage, save the properties of that keyframe, update the animation (in your case just the speed) then apply the new version of the animation starting from the saved point. I created a similar example in the article I wrote for CSS Tricks called Controlling CSS Animations & Transitions with Javascript

As the article described, it'd be easier to change the speed on animationIteration, in your case that would look like this demo, but it is still far from pretty

 var pfx = ["webkit", "moz", "MS", "o", ""],
    rotates = $('.rotate'),
    prevent = false;

function PrefixedEvent(element, type, callback) {
    for (var p = 0; p < pfx.length; p++) {
        if (!pfx[p]) type = type.toLowerCase();
        element.addEventListener(pfx[p]+type, callback, false);
    }
}

function listen() {
    for(var i = 0, j = rotates.length; i < j; i ++) {
        PrefixedEvent(rotates[i], "AnimationIteration", function() {
            if(!$('#faster').is(':checked') && !prevent) {
                var el = $(this),  
                   newOne = el.clone(true)
                            .css({'animation':'rotate 2s linear infinite'});
                el.before(newOne);        
                $("." + el.attr("class") + ":last").remove();
                rotates = $('.rotate');
                listen();
                prevent = true;
            }
            else if($('#faster').is(':checked') && prevent) {
                prevent = false;
                var el = $(this),
                   newOne = el.clone(true)
                            .css({'animation':'rotate 1.5s linear infinite'});
                el.before(newOne);
                $("." + el.attr("class") + ":last").remove();
                rotates = $('.rotate');
                listen();
            }
        });
    }
}
listen();

At the moment we cannot simply restart the animation with a different timing function (even if you pause it and run it again like Michael said). As a result you either have to use a setInterval (which gives a higher delay) or clone the element with the changed values. For more information on the approach I used look at the demo, I added comments to all the javascript I added

EDIT based on your linked fiddle in comments

Since you're trying to simulate a roulette, you don't need to use javascript at all! Simply change your rotation animation to change speed over time (: Here's an rough version of how you'd do so, but you should add more keyframes to make it appear more smooth than it currently behaves

@-webkit-keyframes rotate {
    0%   { -webkit-transform: rotate(   0deg); }
    30%  { -webkit-transform: rotate(2600deg); }
    60%  { -webkit-transform: rotate(4000deg); }
    80%  { -webkit-transform: rotate(4500deg); }
    100% { -webkit-transform: rotate(4780deg); }
}

EDIT 2

Since you need apparently need have a random ending position and likely run it multiple times you can do something like this which is much simpler, only uses CSS transforms, and incredibly useful

var img = document.querySelector('img');
img.addEventListener('click', onClick, false);    
var deg = 0;

function onClick() {
    this.removeAttribute('style');        
    deg += 5 * Math.abs(Math.round(Math.random() * 500));        
    var css = '-webkit-transform: rotate(' + deg + 'deg);';        
    this.setAttribute(
        'style', css
    );
}

and the CSS

img { -webkit-transition: -webkit-transform 2s ease-out; }

Edit 3

This isn't fully relevant or irrelevant, but you can do very similar things using GSAP like I did in this project to make it more interactive/dynamic

like image 169
Zach Saucier Avatar answered Nov 01 '22 11:11

Zach Saucier


You can do that by setting a wrapper, and counter-rotating it.

CSS

.wrapper, .inner {
    position: absolute;
    width: 100px;
    height: 100px;
  -webkit-animation-name: rotate;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
  animation-name: rotate;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}
.wrapper {
    left: 40px;
    top: 40px;
  -webkit-animation-duration: 2.5s;
    -webkit-animation-play-state: paused;
    -webkit-animation-direction: reverse;
  animation-duration: 2.5s;
    animation-play-state: paused;
    animation-direction: reverse;
}
.wrapper:hover {
    -webkit-animation-play-state: running;
    animation-play-state: running;
}
.inner {
    display: block;
    background-color: red;
  -webkit-animation-duration: 1.5s;
  animation-duration: 1.5s;
}
@keyframes rotate {
  from { transform: rotate(0deg);  }
  to {    transform: rotate(360deg);  }
}    
@-webkit-keyframes rotate {
  from { -webkit-transform: rotate(0deg);  }
  to {    -webkit-transform: rotate(360deg);  }
}    

demo

(hover the square to slow it)

like image 42
vals Avatar answered Nov 01 '22 12:11

vals