简体   繁体   中英

Reverse Keyframe animation with Javascript

I animate a block with a keyframe, then trigger the reversed animation in javascript, like this :

$('#block').removeClass('animate1').addClass('animate2');

With animate1 and animate2 corresponding to two CSS classes calling the animation (for the demo, in webkit only), and #block a simple div :

CSS

.animate1 {
    -webkit-animation: shift 1s ease-in-out forwards;
}
.animate2 {
    -webkit-animation: shift 1s ease-in-out backwards reverse;
}
@-webkit-keyframes shift {
    from {
        left: 0px;
    }
    to {
        left: 200px;
    }
}

HTML

<div id="block" class="animate2"></div>

It simply doesn't work. See this fiddle for demo (Chrome 30).


Facts

If I trigger the animations the other way around, I mean the reversed one first, then the normal one, it's working properly ( demo ):

$('#block').removeClass('animate2').addClass('animate1'); //Works.

It's working too if I remove the current class and add the next class with independent functions triggered by click on buttons.

Can someone help me understand this strange behavior? I'm stuck!


Edit

For future visitors, I'm not looking for a workaround anymore, just trying to find out what's happening here:

  • What is this "reset time" required by browser?
  • Why does it works in a way, and not in the other?
  • Why triggering the animation within an inner closure works?

I'll change the accepted answer if appropriate.

When you are reusing an animation, sometimes you inherit the state of it.

The way this happens is a little difficult to forecast, because it isn't always consistent (I guess also that the w3c standard doesn't set exactly what should happen in this cases).

In your case, you are reusing an animation that should happen once (animation-iteration-count : initial, ie 1), and that has already happened once . So, nothing happens. (It is true that the other way round works, but as I said before that issue is not always consistent.)

One way to solve it is to reset the transition.

setTimeout(function () {
    $('#block').removeClass('animate1');
}, 1950);
setTimeout(function () {
    $('#block').addClass('animate2');
}, 2000);

That leaves the div without any animation for a moment, and so reset efectively the transition. You have now the problem that the div jumps to the initial state, so this is not usable right as it is; but I am trying to explain why you have the problem.

A working solution can be going thru transitions, as Jacob says, or creating to different animations.

jumping demo

CSS3 animations appear to be intended for one-off or infinitely looping animations, as suggested by MDN's "Using CSS animations" developer guide .

To keep the logic as you have it, I'd switch to CSS3 transitions .

First, in CSS, let's define two alternate "states" our cat photo can be in (on the left, or on the right):

.transition1 {
    transition-property: left;
    transition-duration: 1s;
    left: 0px;
}
.transition2 {
    transition-property: left;
    transition-duration: 1s;
    left: 200px;
}

Now, using your JS, with our renamed CSS classes:

$('#swap1').click(function () {
    $('#block').removeClass('transition1').addClass('transition2');
});
$('#swap2').click(function () {
    $('#block').removeClass('transition2').addClass('transition1');
});

On button click, the (cat photo) element's left property will transition from 0px to 200px (or vice-versa) over one second time. Here's my code: http://jsfiddle.net/u6jsC/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM