Animating SVG

Recall the jQuery .animate() function. We used it to animate the CSS properties of a page element to new values.

With jQuery, we could set CSS properties immediately with .css() and animate them with .animate().

When working with Raphaël, the shape objects (rectangles, circles, etc) all have an .animate() function that can animate the attributes the shape has.

Notice the difference: in jQuery we're working with CSS properties and the functions are called .css() and .animate(). With Raphaël, we're working on the shapes' attributes and the functions are .attr() and .animate().

With that difference aside, the way we can work with them is very similar. We used this example of animating in jQuery:

newstyle = {
  'font-size': '1.5em',
  'margin-left': '2em'
}
$('#styling').animate(newstyle, 2000)

In Raphaël, we can set the shape's attributes initially, and them animate to new attributes in two seconds like this:

initial_attr = {
  'fill': '#fff',
  'stroke-width': '1'
}
final_attr = {
  'fill': '#f00',
  'stroke-width': '5'
}
rect = paper.rect(10, 10, 20, 20)
rect.attr(initial_attr)
rect.animate(final_attr, 2000)

If we have a variable referring to the shape (rect in that example), we can perform the animation any time, such as later when the user clicks something.

One of the most useful attributes to animate with Raphaël is the element's transformation. A transform string is used to specify the transformation. You can specify commands: t for translation (moving), r for rotation, s for scaling. For example, this will rotate a shape by 180° in five seconds:

rotated = {
  'transform': 'r180'
}
shape.animate(rotated, 5000)

This will move an element 100 units right and 200 down, while scaling to 3 times its original size:

slide_grow = {
  'transform': 't100,200s3'
}
shape.animate(slide_grow, 1000)

These transformations can be applied with .attr() or .animate().

Example 1: User-Initiated Animation

In our first example, we will perform two different animations on a square when the user clicks buttons. The first will be to change the square's size, and move it to the right:

slide = function() {
  final = {
    'width': '15',
    'height': '15',
    'transform': 't130,0'
  }
  sq.animate(final, 2000)
}

Second will be to rotate the square:

spin = function() {
  initial = {
    'transform': 'r0'
  }
  final = {
    'transform': 'r360'
  }
  sq.attr(initial)
  sq.animate(final, 2000)
}

The code here resets the rotation to 0° each time because the transformation 'r360' is relative to the shape's starting position. That is, if we didn't have sq.attr(initial) there, the animation would only appear to work once. The second time, the square would already be rotated 360°, so wouldn't move any further.

You can try these animations.

Example 2: Repeating Animation

In this example, we will create a repeating animation by using two more arguments to .animate() that are optional but let us do more.

There are actually four possible arguments we can use:

shape.animate(new_attrs, time, 'linear', callback)

The first two are what we have already seen: the new attributes and animation time. The third is the “easing formula” that we will leave as the default, 'linear'.

The last argument there is a function that will be called when the animation is over. We can use that to start another animation as the first one finishes.

The strategy here will be to move a shape to the right (in anim_right). When that's done, we'll move it back to the left (with anim_left). When that's over, we'll call (in anim_right) again, and so on.

anim_right = function() {
  new_attr = {
    'transform': 't100,0'
  }
  circ.animate(new_attr, 1000, 'linear', anim_left)
}
anim_left = function() {
  new_attr = {
    'transform': 't0,0'
  }
  circ.animate(new_attr, 1000, 'linear', anim_right)
}
setup = function() {
  paper = Raphael('container', 200, 100)
  circ = paper.circle(50, 50, 30)
  anim_right()
}
$(document).ready(setup)

We had to call the function anim_right in setup so the animation starts when the page loads.

Have a look at the example repeating animation to see this code working.