In the YUI 3 Gallery: Extensions for SVG, Created for SVG Wow!

Introduction

SVG (Scalable Vector Graphics) provides a declarative syntax for interactive, animated 2D graphics: shapes, images and text. SVG support is part of the HTML 5 specification and SVG is implemented by all major browsers, including Microsoft's Internet Explorer in version 9.

The svg-wow.org web site showcases what can be done with SVG today. The demos on this web site were created for the SVG Open conference, where the SVG Wow! sessions have been a tradition for several years. The SVG Wow! sessions were started by Dean Jackson, then in collaboration with myself and then continued by Erik Dahlstrom. Erik and I have collorated on the session for the 2009 and 2010 editions of the conference.

For the past two years, the demos have increasingly used AJAX frameworks, applying their features to SVG instead of (or in addition to) HTML. YUI is the most widely used framework on the web site, which uses both YUI 2 and YUI 3.

Because SVG images are defined in terms of geometry and rendering attributes, it is possible to render them at any resolution. As a result, SVG images can be scaled to any size while retaining a high rendering quality, for example when printing (no more jagged edges).

The following zoomed-in view shows the same SVG image shown earlier but rendered at a higher resolution while preserving the high quality.

Just like HTML, SVG supports interactivity: it is possible to add event listeners on graphic objects for mouse or keyboard interactions. Of course, SVG supports the Document Object Model, which makes it easy to manipulate the different properties of graphical objects, such as their geometry or rendering style.

There is a lot to the SVG specification: advanced rendering styles (stroking, filling, patterns, gradients), filter effects (blurs, shadows, color matrices), CSS styling, advanced text features (such as text on a path) and declarative animation. You can check out the references at the end of this post to learn more about the SVG format features.

SVG and HTML

With HTML5, SVG can be inlined in HTML documents without further ado. Browsers are starting to support that feature (e.g., Firefox 4). For the time being, all major browsers support SVG inlined in XHTML, which provides the same functionality. SVG in XHTML just requires that namespaces are properly declared.

The <animate> tag is borrowed from the SMIL specification and, along with the other animation elements, it provides a very powerful animation engine to SVG.

Unfortunately, until recently, browser support for SVG animation was sparse. It has improved over the past two years, but Microsoft has made it clear it will not support declarative SVG animation in IE 9.

As a result, most of the demos on the svg-wow.org web site use scripted animation instead of declarative animations. On one hand, this is unfortunate because declarative animations are more efficient than scripted animations. On the upside, scripted animations can be very flexible, and they work across implementations very well.

The need for a good scripted animation solution is what drove the usage of YUI on the svg-wow demos. However, the demos also use other YUI features, in particular the Loader and Node modules.

You'll note that the transform values are defined in terms of "components" (such as tx or ty) which are combined to form a value using the transformTemplate found on the animation configuration object.

The template is a flexible mechanism for building transform values while separate components make it easy to compute the animated values. This is an example where the YUI animation model allowed more flexibility (and more expressive power) than SVG's SMIL animation element (animateTransform). In order to create the animation described above, the equivalent SMIL declaration would have been:

Note how the above snippet requires multiple animateTransform elements which have to be synchronized: the begin attribute of the first animation is set to scaleAnim.begin to synchronize the start of the two animations. A nice feature of the YUI animation engine is that the timing of an animation (i.e., start, end and duration) can be shared to apply to multiple element properties.

The YUI extension for animating SVG transforms is used extensively, such as in the camera and animated lyrics examples. The former uses an extension of YUI 3 while the latter uses an extension of YUI 2.

Animating geometry

Basic Geometry

Animating SVG geometry with YUI is quite simple. The following example animates a <rect> element's width, height and corner radii:

As discussed later on, some changes under the YUI hood made this work. But from a developer's perspective, this animation works the exact same way as the animation of any other HTML attribute or CSS property.

The <path>'s d attribute

One geometry attribute is a little special: the d attribute on the <path> element. The <path> element is used for arbitrarily complex geometry. A <path> can describe any shape. Its d attribute describes its geometry in terms of path segments which can be lines, arcs, quadratic or cubic Bezier curves (there are a few more commands, but they all map to Bezier curves).

Animating the d attribute also required a bit of extension to YUI's animation engine, but with that extension, the d attribute can be animated like any other, as shown below.

See the filter animations tests. The following image shows how animating a Gaussian blur can be used to transition between button states.

This type of effect is used in the fast blur effect demo, even though that demo does not use YUI animation but declarative SMIL animation elements (at the expense of only running in browsers supporting that feature, as explained earlier).

Animating CSS properties

Like HTML, SVG elements have attributes and also CSS properties. SVG has some specific CSS properties. These properties can be animated, sometimes to create surprising effects. For example, the stroke-dashoffset property can be used to simulate drawing a shape.

The graffitis demo uses this technique (even though without YUI animation) and so does the camera demo (this time with YUI animation).

YUI and SVG: Under the hood

The svg-wow.org web site uses both YUI 2 and YUI 3 and has SVG-specific extensions for both. The following section of this article focuses on the YUI 3 extensions.

Extensions were needed to:

make YUI work with SVG's DOM specificities

account for implementation differences

add support for new attribute types such as SVG transforms

add additional animation timing and synchronization features

Accounting for SVG DOM Specificities

As described earlier, SVG attributes can be animated with declarative elements such as <animate>. To support SVG's animation model, SVG attribute values hold both an animated and a base value. For example, the r attribute on a <circle> element is an SVGAnimatedLength defined as follows:

Extensions were needed to allow the animation engine to account for the SVG attributes' unusual value model. Thankfully, YUI 3 has a concept of animation behaviors. Behaviors are essentially attribute-specific hooks, and it was fairly easy to add support to handle SVG attribute values. For example, SVGAnimatedLength attributes are handled like so:

There are more extensions for other SVG attributes values such as the transform attribute, color attribute values (like fill, stroke or stop-color) and attributes such as stdDeviation mentioned earlier.

A few similar tweaks were required, for example in the Y.Node.prototype.toString method, again to account for SVG's baseVal (this time on the className node property). Another example is the default node setter in the Node module.

Accounting for browser differences

While the previous extensions are required because of specification differences between HTML and SVG, the following are required because of implementation variations between browsers.

SVG has a special category of attributes called presentation attributes. In implementations also supporting CSS styling (which all browsers support), these presentation attributes are really just another way to specify a CSS property with a low specificity. From the SVG specification:

The presentation attributes thus will participate in the CSS2 cascade as if they were replaced by corresponding CSS style rules placed at the start of the author style sheet with a specificity of zero. In general, this means that the presentation attributes have lower priority than other CSS style rules specified in author style sheets or 'style' attributes.

Unfortunately, some browsers do not implement the specification correctly and window.getComputedStyle does not always account for all possible sources for setting the SVG CSS properties: CSS selectors, style attribute and presentation attributes.

YUI came to the rescue thanks to the Node module which could be extended to hide these browser differences. The Y.DOM.CUSTOM_STYLES and the Y.Node.prototype.getComputedStyle could be extended to offer a uniform way to read SVG style properties.

Extending Y.DOM

YUI wraps all DOM access through the Node interface. As a result, some SVG specific DOM methods, such as getBBox (used to compute the bounds of an SVG element), are not accessible on the wrapped object.

To make things easier to program for SVG, extensions to the default Y.DOM module (which Node uses) were added to either expose SVG DOM features or add convenience methods, commonly needed when manipulating content:

is a shorthand for making various DOM calls (such as createElementNS, setAttributeNS and appendChild) to create a g element and its children and inserting it before prevSibling under myNode. The utility deals with namespaces for attributes and elements.

Additions to the Animation engine for timing and synchronization

Many, if not most effects involving animation require multiple choreographed animation instances. Typically, several animations are required to create a desired effect, and the start or end of animations depend on each other, sometimes with a time offset: animations need to be synchronized.

For example, if you have a set of shapes which need to fade in one after the other, you will need to create a fade animation for each element and then chain their start time with a slight offset.

The method onBegin makes it easy to synchronize the start of two animations, with an optional time offset. Actually, onBegin can also invoke a function with a time offset. Likewise, the onEnd extension makes it easy to synchronize with the end of an animation.

By default, YUI animations have events which provide a way to synchronize. The onBegin and onEnd methods express the synchronization more concisely (a similar example of conciseness is shown below).

In addition, it is sometime necessary to synchronize an animation with an event, again with a time offset. The beginOn and endOn extensions allow us to express that. For example:

anim.beginOn(Y.one('#button'), 'click', 0.5);

will start the animation 0.5s after the element with id 'button' was clicked. This is a short-hand for:

A final extension made to the animation class was the ability to make an animation object apply its first frame's state before it was actually started. This is often needed when creating animation effects involving multiple animations which start at different time offsets.

The picture shuffle demo uses animation offsets for the effect that spreads the stack of pictures or puts the pictures back in a stack.

Conclusion

Working with SVG and YUI, and in particular YUI 3, has been a very enjoyable experience: a lot of the YUI functionality applies to SVG right out of the box and YUI's extensible architecture made it possible to work around occasional issues and to add desired functionality.

Of course, increased standard support for SVG in YUI would be helpful, in particular making YUI work with stand alone SVG documents and making the Node class wrap SVG elements without workarounds.

There are also a few enhancements that would be helpful. For example, it would help if animations could target multiple elements. Likewise, supporting multiple values (as in the <animate> SVG element for example) would be helpful to create more sophisticated effects.

The demos on svg-wow.org were written for YUI 3.1 and ported to 3.2 for the purpose of this blog. In 3.2, transitions were introduced which make use of native CSS transitions if available in the browser. It might be possible for the YUI animation engine to similarly leverage SMIL animation where available (Opera, Firefox and WebKit at the time of this writing) which should also yield performance improvements.