16

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Matrix
  • 3,458
  • 6
  • 40
  • 76

2 Answers2

11

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

Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
  • this solution can't work. You don't do the whole job. I thought to use event JS and wait end of one iteration to reboot animation with reduce speed (increase duration), but it's a fake good idea: because i want reduce the rotation to 0, so when you reduce the speed, the time to end one iteration (for reduce more) is increase. Test it... you will understand. – Matrix Dec 03 '13 at 16:08
  • i do it for show you the problem: http://jsfiddle.net/52xLK/31/. It never stop and more time pass, more time to reduce speed. – Matrix Dec 03 '13 at 16:58
  • @Matrix Updated my answer with a more appropriate CSS only solution – Zach Saucier Dec 03 '13 at 17:43
  • your new exemple have bad end: it finish on first keyframe (its not continuou animation, it jump to). Furthemore, if i want random endding position, i can't. – Matrix Dec 03 '13 at 18:31
  • 1
    @Matrix I updated again. Hopefully this time it is what you need. Next time be a lot more clear with your end goal so that we can help you with what you are actually trying to solve – Zach Saucier Dec 03 '13 at 23:40
  • yes it works, but it's not realy control speed method. With another animation, i'm not sure this "cheat" can be used. thx for help. – Matrix Dec 06 '13 at 13:18
4

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)

InSync
  • 4,851
  • 4
  • 8
  • 30
vals
  • 61,425
  • 11
  • 89
  • 138