Introduction

The page explains how to animate simple SVG transformations using the <animateTransform> tag. The code for the example can be found by following the Github link above.

Square rotating about its centre

[NOTE: SVG's SMIL animations are deprecated and you should probably use CSS transformations instead.]

Animating a square

This is the code for the animated square at the top of this page:

<rect x="160" y="20" width="60" height="60" fill="#007bff">
  <animateTransform
    attributeName="transform"
    type="rotate"
    from="0 190 50"
    to="360 190 50"
    dur="4s"
    repeatCount="indefinite" />
  </rect>

The important points to note are:

  • The <animateTransform> element is a child of the <rect> element.
  • The type is "rotate" (could also be "translate", "scale", "skewX" or "skewY").
  • The from and to are in the form "n1 n2 n3".
  • The rotation starts at 0 degrees and changes to 360 degrees (the n1 values).
  • The centre of rotation is at (n2, n3) and doesn't change (it could if you wanted).
  • The dur is "4s", meaning a full rotation takes 4 second.
  • The repeatCount is "indefinite", but can be a number, even a decimal, if you want the animation to stop after it has repeated some number of times.
  • You can also use begin and end attributes to determine when to start and stop the animation.

Rotating arbitrary elements

I was first inspired to tackle this problem after seeing this question on Stack Overflow. So I found a tutorial for how to draw gears using Inkscape (here) and tried to animate them.

In order to rotate an element about its centre, you first need to know its centre. For <circle> or <rect> elements that's easy, but for a path, it's not so straightforward.

(EDIT: As Jim says in the comments, you could draw the elements so their center is at the origin, and then translate them to where you want. This simplifies things at lot, but sometimes you don't get to draw the image yourself.)

The only way I could find to determine the centre of a path, short of writing my own program to parse the coordinates, was to use Javascript and the getBBox() function to find the bounding box of the element.

Below is a function that takes an element id, a duration (seconds to complete one rotation), and a direction (1 for clockwise, -1 for anti-clockwise). It then finds the element with that id, determines its centre with getBBox(). It then builds an <animateTransform> element from scratch and adds it to the chosen element. Finally, it calls beginElement() to start the animation.

function addRotateTransform(target_id, dur, dir) {
  var my_element = svg.getElementById(target_id);
  var a = svg.createElementNS("http://www.w3.org/2000/svg", "animateTransform");

  var bb = my_element.getBBox();
  var cx = bb.x + bb.width/2;
  var cy = bb.y + bb.height/2;

  a.setAttributeNS(null, "attributeName", "transform");
  a.setAttributeNS(null, "attributeType", "XML");
  a.setAttributeNS(null, "type", "rotate");
  a.setAttributeNS(null, "dur", dur + "s");
  a.setAttributeNS(null, "repeatCount", "indefinite");
  a.setAttributeNS(null, "from", "0 "+cx+" "+cy);
  a.setAttributeNS(null, "to", 360*dir+" "+cx+" "+cy);

  my_element.appendChild(a);
  a.beginElement();
}

addRotateTransform requires an svg element to be defined in order to create the animateTransform element. We can get the svg element by adding onload="init(evt)" to the SVG element and defining an init function to extract the svg from the evt (which is an object representing the onload event).

This function can be added within the SVG itself in the following <script> element:

<script type="text/javascript">
  var svg;

  function init(evt) {
    if (window.svgDocument == null) {
      svg = evt.target.ownerDocument;
    }
    addRotateTransform('gear-1', 12, 1);
    addRotateTransform('gear-2', 16, -1);
  }

  function addRotateTransform(target_id, dur, dir) {
    ...
  }
</script>

This is also where you can call the addRotateTransform function. Here I've called it twice to target elements with ids "gear-1" and "gear-2". It might seem like a lot of code just to rotate some elements, but you just need to add it to the top of your SVG and then it's easy to rotate any of the elements you chose by selecting the relevant ids.