Friday, April 23, 2010

Why declarative animation should be in the DOM and not in CSS

Note: This blog post did reflect my opinion at the time of writing. Since then technology has evolved and now this has become less of an issue. I will leave the post online, but if you came to learn about how to do animation today, reading will be a waste of your time.

A little more than a year ago Safari introduced experimental support for CSS based animations, to compliment transformations and transitions. I have no gripe with transitions and transformations, but I think animations belong in the DOM and not in CSS. My main argument is that animations will most of the time be triggered by DOM-events, and during at least the next five years most CSS-based animations will be duplicated using classic DOM-methods anyway. The purported separation of animation (for designers) who are supposedly scared away from scripting is not a valid argument, since they are going to use libraries anyway. (And, frankly, the CSS animation syntax in itself looks quite scary to most people!)

Originally, this started out mostly as a gut feeling, and the arguments that I've made on the W3C mailing list are varied and admittedly a bit confused. I was thinking out loud more than I was presenting a coherent argument. Hopefully this blog post will come across as more reasonable!

I also believe that this discussion needs to be known outside of the CSS working group and the participants on the www-style mailing list. Specifically, it needs the input of developers of JavaScript libraries and normal web developers. These are the people who will be affected the most by any decision. (I am providing an abundance of links to the discussion on the mailing list for context.)

CSS animation – the good parts

A few aspects of the CSS animation proposal are brilliant and not of dispute:

  • Declarative syntax. Designers specify what effect they want, not how the browser should achieve it.
  • Hardware acceleration. Animations get smoother, faster and less CPU-draining. The GPU is optimized for this and can perform the calculations using only a fraction of the power and time a CPU would take to do the same number crunching.

When I am asking the CSS working group to reconsider CSS animations I am not in any way trying to take away these two strong points. I am firmly pro having GPU-accelerated, declarative animations in the browser. I just think the DOM is a better fit for them.

What will be animated?

CSS has no events, it has states. Basically it knows the focused and unfocused state on links and widgets and if a pointer is hovering, clicking ("active") or not hovering over an element. While exeperimenting with the CSS animation syntax, the working is producing examples using these states. Ironically mobile devices are one of the main reasons why CSS animations were originally thought up, and they rarely have a pointer, and CSS is not really equipped to handle touch events.

It can safely be assumed that 99 % of all real world use cases for animation will be the result of user or server interaction on some part of the document that is not being animated. The user clicks a button and a div will slide into view. The user presses a key on his keyboard and text will wiggle and bounce. Data is sent back from an AJAX request, and the received data will appear through an attention grabbing sliding effect.

Currently the only way to achieve these real world use cases is by adding or removing class attribute values. Thus we have scripts that will trigger animation in the DOM and the actual design of the animated effects in CSS. Conceptually this is nice. Separation of logic is a good thing™. However, in real world practice this will not be so neat.

Events confusion

Even though there are no events in CSS, there is discussion about having animations running upon entering a state, while being in a state and when leaving a state. These are not events in a technical sense, but outside of the W3C working group, most developers will be really confused about the difference. Such precise knowledge is not found in abundance! If a specific application need to differentiate between these, it is by far easier for developers to use the more familiar DOM events.

Having some animations run because they are affected directly, e.g. on hover, some animations run because they are triggered by a scripted change of className, and in both cases also have animations that may run entering, during and leaving states looks like a recipe for unmaintainable and confusing development. It is way much better to trigger all events from one place only, and that place can only be the DOM.

How to implement animations in a library

OK, you are building a little library to animate stuff. What do you do? I suppose the following:

  1. First you capability detect support for declarative animation. That in itself would be easier if it was in the DOM, but it is at least doable now. But not in a neat fashion. Score one against having animations in CSS.
  2. If CSS-animation is indeed supported, you will wrap your animate function around className switches. Doable, but not neat.
  3. If CSS-animation is unsupported, you fall back to old school timed manipulation of the style attribute.
    • However, using the animation parameters from the CSS-file is a huge impracticality. You must find a way to read all CSS-files, parse them and interpret the cascade, the specificitivity of all animation rules and convert that information into timed logic. This is impractical, slow and CPU-draining and fragile.
    • The CSS Object Model (CSSOM) will not alleviate this problem. Browsers that need to parse the animation rules are the ones that neither implement animations, nor the corresponding CSSOM.

Alternatively, the author is required to re-specify the animation once again, now using a syntax for the fallback. We thus get code duplication, with all the error proneness and maintenance problems that follows from that approach. But it is the only approach currently available with reasonable results.

It can safely be said, that CSS-animations are not backwards compatible in any reasonable way. And we are going to need backwards compatible solutions for almost another decade or so.

What about progressive enhancement?

Using progressive enhancement we can deliver CSS-based animation to browser that support it, and non-animated but still usable content to the rest. Problem solved, is it not?

I like progressive enhancement. I teach it and I practice it. However, there will be a great number of real world customers that will insist upon having animations in both the brand new cutting edge browsers and the legacy ones. As least as long as more than 10 % of their visitors use them. We can preach all we want. This scenario will face the real world developer way too often.

Animations will be used to convey information as well as for eye candy. Not having a scripted fall back will not be an option for such use cases either. In real world web development, progressive enhancement can not be called upon to be a panacea, how appealing that thought ever may be.

What else will be hard to do using a CSS approach?

In real world use cases developers are also going to want to manipulate animation and keyframe properties, as well as programmatically create animations from scratch. Using the CSSOM this can probably be done in browser that support animation but once again the fallback for legacy browsers will be very hard to achieve.

What is my counter proposal

I have barely begun thinking about this issue, so any propsal I have at the moment should not be regarded as a final suggestion. In order to keep the separation of concern between designers and developers – for those situations where one has the luxury to keep them separate – animations must be easy to define with a CSS-like syntax. JSON fits that requirement quite well. The method to start an animation could be called runAnimation. It might return a value that I can store in a variable in order to manipulate or cancel the running animation. Another way to manipulate it would be by altering the animation properties. For convenience, there should also be a method to stop all running animations on an element.

Here is an example, recreating the effect from Surfin' Safari's announcement:


// Keep the JSON objects in a separate file for designers to fiddle with

var bounce = {
    "from" : {
        "left" : "0px"
   }
   "to" : {
       "left" : "200px"
    }
}

var myAnimation = {
 "animation-name" : "bounce",
 "animation-duration" : "4s",
 "animation-iteration-count" : "10",
 "animation-direction": "alternate"
}

// Keep these lines in another file for the JavaScript guy/girl to fiddle with

document.getElementById("foo1").onclick = function() {
    document.getElementById("bar").runAnimation(myAnimation);
}

document.getElementById("foo2").onclick = function() {
    document.getElementById("bar").stopAllAnimations();
}

// Example 2
// Keep the JSON objects in a separate file for designers to fiddle with

var pulse = {
    "0%" : {
        "background-color" : "red",
        "opacity" : "1.0",
        "transform": "scale(1.0) rotate(0deg)"
    }
    "33%" : {
        "background-color": "blue",
        "opacity" : "0.75",
        "transform" : "scale(1.1) rotate(-5deg)"
    }
    "67%" : {
        "background-color": "green",
        "opacity": "0.5",
        "transform": "scale(1.1) rotate(5deg)"
    }
    "100%" : {
        "background-color": red,
        "opacity": "1.0",
        "transform" : "scale(1.0) rotate(0deg)"
    }
}

var pulsedbox {
    "animation-name": "pulse",
    "animation-duration": "4s",
    "animation-direction" : "alternate",
    "animation-timing-function" : "ease-in-out"
}

// And here comes the DOM-parts, this time using JQuery for easy iteration

$(".pulsedbox").each(function() {
    this.runAnimation(pulsedbox);
});

As stated above, my counter proposal is not a finished product in any way. It merely is intended to serve as an illustration to an alternative approach. The technical merits or defeciencies of that proposal is in itself not really something that should guide the general discussion about how to implement declarative animation. The principles on which I draw the conclusion that the DOM is a better fit is the true talking point here.

Now I am especially interested in hearing the opinions from the DOM-scripting community!