May 17, 2011 5

Keyframe events for CSS3 Animations

By

Update (2011/05/21)
A feature request has been added to WebKit #61208


Update (2011/05/20)
I’ve created a Javascript shim to provide cssAnimationKeyframe events for each keyframe in a CSS3 Animation (read about it here).


I’m a big fan of CSS3 animations, they offer a really lightweight way to create very compelling effects. They also degrade really well, which is a massive plus!

Faruk Ates (@kurafire) recently introduced me to his awesome jQuery Runloop Plugin. The runloop plugin builds on the jQuery animate() loop and provides the ability to execute custom code at a pre-defined point during the animation. Here’s a trivial example from the documentation:

var loop = jQuery.runloop();

// Note: only use 5% intervals (10% for <500ms durations)!
loop.addKey('25%', function(){ // Some code or animations here  });
loop.addKey('50%', function(){ // Different code/animations  });
loop.addKey('75%', function(){ // Even more different code/animations!  });

loop.play(1000); // duration set in milliseconds

This essentially provides an alternative mechanism for producing keyframe based animations. However, where this differs from CSS3 animations (aside from the fact that its using Javascript for timing instead of the browsers own hardware accelerated code) is its ability to trigger any arbitrary Javascript code/function at each keyframe. This is incredibly powerful and is definately a feature I’d like to see added to the CSS3 Animation spec.

Suggested Implementation

WebKit based browsers that support animation expose the WebKitAnimationEvent object. From the WebKitAnimationEvent API Docs we can see this event currently only has three types:

  • webkitAnimationStart Occurs at the start of an animation.
  • webkitAnimationEnd Occurs when the animation finishes
  • webkitAnimationIteration Occurs at the end of each iteration of an animation when the -webkit-animation-iteration-count is greater than 1.

What I propose is an extra webkitAnimationKeyframe or similar event that is fired at the start of each defined keyframe. This event would have an extra property called progress or keyframeTitle keyText, as well as the standard animationName & elapsedTime properties. If this event were to be implemented you could then do something like the following (using jQuery/Zepto.js syntax for convenience):

$('div#animate').bind('webkitAnimationKeyframe', function(event){
    switch(event.originalEvent.keyText) {
        case '10%':
            // Execute code that is important at 10% progress
            break;
        case '75%':
            // Execute code that is important at 75% progress
            break;
    }
});

NB: I’m using the WebKit prefixes here only as an example, as WebKit browsers are currently the only browsers to support CSS3 Animation. Ideally something like this would be built into the core spec’s and as such would also be possible in Firefox, Opera, IE etc.

  • Anonymous

    What I’m thinking after reading your article that this maybe connected to requestAnimationFrame (http://paulirish.com/2011/requestanimationframe-for-smart-animating/) but instead of using CPU, this would use GPU with CSS being hardware accelerated and all that. Beyond that, if this is helpful, you can create a polylifill for it. You can pull the duration from the CSS before animation starts, and count it with requestAnimationFrame yourself, and then fire events accordingly. No?

  • http://www.joelambert.co.uk Joe Lambert

    I wasn’t aware of requestAnimationFrame() & it does look like a better solution to using setTimeout() but it still adds an extra layer of complexity to getting notified when a keyframe occurs.

    We’re essentially talking about a version of Faruk’s runloop that uses requestAnimationFrame() rather than using jQuery’s .animate(). Which would be a great thing & I may have a go at hacking it together. BUT it strikes me as an obvious omission to not provide event’s for keyframe execution?

  • Anonymous

     jquery’s animate uses requestAnimationFrame when supported since version 1.6 I think…so runloop extends animate and probably uses it too

  • http://www.joelambert.co.uk Joe Lambert

    Yeah noticed that after I wrote the reply, definitely a good move on jQuery’s part!

    I’ve actually started a small poly-filler function to add event triggering via requestAnimationFrame() as you suggested.

    Will let you know when I’ve got an example online.

  • Pingback: CSS Animation Keyframe Events (Javascript solution) » Joe Lambert