September 7, 2011 5

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);
  • Felipe Nascimento de Moura

    hey! great thing you get here! Really interesting idea and project…congrats! Looks quite useful, indeed!

  • Pingback: yCoder » 通过JavaScript访问和修改CSS3动画

  • iwonder

    Hello Joe,

    Please, coud you help me managing with CSSanimationstore .js ? I’d like to switch between the default keyframe rule for an animation called ‘spinlateau’ : @-webkit-keyframes spinplateau { 0%{-webkit-transform: rotateX(80deg) rotateZ(0deg)} 100%{-webkit-transform: rotateX(80deg) rotateZ(360deg)} } and others like this one (and always similar to this one) : @-webkit-keyframes spinplateau2 { 0%{-webkit-transform: rotateX(86deg) rotateZ(0deg)} 100%{-webkit-transform: rotateX(86deg) rotateZ(360deg)} } for the same element.

    I can’t make it work… probably I didn’t understand very well how to do it.

    I’m new in CSS and JS too…

    Could you, if you have time, tell me what parameters to write in the function, so that I could understand how it works ?

    Maybe I’m completely wrong and your JS is not appropriated to do what I’d like… In that cas, I’m sorry for having bothered you.

    function toto () {

    var spinPlateau = CSSAnimations.spinplateau; spinPlateau.setKeyframe (…..); }

    Thanks vey much

  • Drclue

    The folks who pay for the kibble here have me writing a library that uses CSS/3D extensively and every possible bit of the w3c baked in the browsers to keep the size down. Just had the pleasure of shrinking the ATrule stuff down to a single line of code.(seems to work with the other prefixed Atrules too). Basically we get to totally ignore prefixing as the < 100 lines dealing with CSS of the ~700 line uncompressed lib finish remap just barely after the first readystate and then during the application make it transparent. That same 700 lines has also allowed us to dump jQuery which used to be around to support the business and database aspects. As a sorta free-bee side affect of hugging the W3, it’s actually starting to be it’s own editor as purely an unintentional side affect. Been doing this stuff for decades but the W3 is really starting to be entertaining

  • Pingback: 通过JavaScript访问和修改CSS3动画 |