SVG animation: Rotating elements

A few days ago, I wrote about creating a rotating 3D SVG cube, which involves using EMCAScript to redraw every line. However, simple two-dimensional transformations can be animated in SVGs with much less hassle using <animateTransform>. For example:

The code for this animated square is

<rect x="150" y="20" width="60" height="60" fill="blue">
  <animateTransform attributeType="xml"
                    attributeName="transform"
                    type="rotate"
                    from="0 180 50"
                    to="360 180 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 from 0 degrees to 360 degrees (the n1 values).
  • The centre of rotation is at (n2, n3).
  • The dur is "4s", meaning a full rotation takes 4 second.
  • The repeatCount is "indefinite", but can be a number, even a decimal.
  • There are also begin and end attributes to determine when to start and stop the animation.

Rotating arbitrary elements

In order to rotate an element about it's 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. 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.

The only way I could find to determine the centre of a path, short of writing my own program to parse the coordinates (which would be pretty complicated), was to use EMCAScript 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 = svgDoc.getElementById(target_id);
  var a = svgDoc.createElementNS(svgNS, "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();
}

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

<script type="text/ecmascript"><![CDATA[
  var svgNS = "http://www.w3.org/2000/svg";

  function init(evt)
  {
    if ( window.svgDocument == null )
    {
      svgDoc = evt.target.ownerDocument;
    }

    addRotateTransform('gear-1', 12, 1);
    addRotateTransform('gear-2', 16, -1);
  }

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

This adds the rotation animation to two elements, with ids 'gear-1' and 'gear-2'. Finally, you need to add onload="init(evt)" as an attribute of the SVG element itself. For the complete code see the file below.

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.

AttachmentSize
rotating_square.svg687 bytes
4gears.svg13.91 KB

Comments

Could you give us to try using our gear design,please?

 

This is brilliant! This is exactly what I have been looking for!

A little advice: if you draw all the cogs in inkscape at the origin (so their centres are at (0,0) it is much easier to position and rotate them.

Jim

Post new comment

The content of this field is kept private and will not be shown publicly.