September 7, 2011 2

Accessing & Modifying CSS3 Animations with Javascript

By

Yesterday Chris Heilmann wrote a post on the Mozilla blog about detecting and generating CSS animations using JavaScript. The article features a small example from myself demonstrating the rather verbose technique required to access a CSS animation with code.

Chris ends his article with the following:

I’d love to have a CSSAnimations collection for example where you could store different animations in JSON or as a string and have their name as the key. Right now, creating a new rule dynamically and adding it either to the document or append it to the ruleset seems to be the only cross-browser way. Thoughts?

This seemed like a great idea so I set about creating a small JavaScript shim to provide this interface.

CSS Animation Store

The idea is to create a simple interface for accessing CSS Animations from within JavaScript. This is something I’ve done a couple of times in the past with Morf.js and CSSA so had most of the code written already, it just needed wrapping up in the suggested JSON style interface that Chris described.

Download CSS Animation Store from GitHub

Here is the basic interface for the CSS Animation Store (for information on how the script works see below). Include the JavaScript just before the closing </html> and it will expose a new global object called CSSAnimations. The object contains each available CSS Animation currently available.

So, to get an animation named ‘spin’ you would do the following:

var spin = CSSAnimations.spin;

The variable spin now holds an instance of a KeyframeAnimation object, which has the following properties and functions:

  • keyframes array of KeyframeRule objects
  • original gives access to the native WebKitCSSKeyframesRule or MozCSSKeyframesRule object this object wraps

  • getKeyframeTexts() returns an array of all available keyframe texts, e.g. ['0%', '50%', '100%']

  • getKeyframe(text) returns a KeyframeRule based on the provided text, e.g. getKeyframe('0%')
  • setKeyframe(text, css) sets the CSS for a keyframe the given text, e.g. setKeyframe('10%', {background: 'red', 'font-size': '2em'})

As you can see, a few of these methods/properties also deal with another type of object called KeyframeRule. This is a wrapping around the WebKitCSSKeyframeRule and MozCSSKeyframeRule (note frame not frames). The KeyframeRule object has the following properties:

  • css object with the CSS for this keyframe, e.g. {background: 'red', 'font-size': '2em'}
  • keyText the text name for this frame, e.g. 10%
  • original gives access to the native WebKitCSSKeyframeRule or MozCSSKeyframeRule object this object wraps

Examples of use

Getting the CSS for each frame in an animation

var spin = CSSAnimations.spin;

for(var i=0; i<spin.keyframes.length; i++)
    console.log(spin.keyframes[i].css);

Adding/modifying a keyframe in an animation

var spin = CSSAnimations.spin;
spin.setKeyframe('10%', {background: 'red', 'font-size': '2em'});

Feedback

Let me know what you think in the comments or on GitHub, I’m also open to suggestions for modifications or additions!

Accessing/creating CSS Animations with native Javascript

Here’s a quick overview for those not wishing to use the CSS Animation Store, or those that want to know how it works under the hood.

Accessing CSS Animations

Accessing keyframe animations is done using the CSSOM. Here’s the function from my CSSA script that I use to access a given CSS Animation:

CSSAnimation.find = function(a) {
    var ss = document.styleSheets;
    for (var i = ss.length - 1; i >= 0; i--) {
        try {
            var s = ss[i],
                rs = s.cssRules ? s.cssRules : 
                     s.rules ? s.rules : 
                     [];

            for (var j = rs.length - 1; j >= 0; j--) {
                if ((rs[j].type === window.CSSRule.WEBKIT_KEYFRAMES_RULE || rs[j].type === window.CSSRule.MOZ_KEYFRAMES_RULE) && rs[j].name == a){
                    return rs[j];
                }
            }
        }
        catch(e) { /* Trying to interrogate a stylesheet from another domain will throw a security error */ }
    }
    return null;
};

You can then use this in code like this:

var animation = CSSAnimation.find("spin"); // Get the animation named 'spin'

The returned object will either be of type WebKitCSSKeyframesRule or MozCSSKeyframesRule depending on the browser you’re using.

Creating CSS Animations

The only way I’ve found to create animations on the fly is a little hacky. It involves crafting the CSS string in code and either inserting it into the CSSOM or appending a style tag to the current HTML document.

Here’s how I do this in my library Morf.js:

// Adds the CSS to the current page
addKeyframeRule = function(rule) {
    if (document.styleSheets && document.styleSheets.length)
        // Insert the style into the first attached stylesheet
        document.styleSheets[0].insertRule(rule, 0);
    else
    {
        // No attached stylesheets so append to the DOM
        var style = document.createElement('style');
        style.innerHTML = rule;
        document.head.appendChild(style);           
    }
}

Once you’ve crafted your CSS animation string you can then call the above function like this:

var css = '@-webkit-keyframes spin { ... }';
addKeyframeRule(css);