Rotating elements


18 Jun 2011 Code on Github

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.

Comments (16)

Andrea on 17 Oct 2013, 2:55 p.m.

Hello ! How can I rotate an object (ex. a rectangle) only when a variable is set at TRUE ? (When the variable is FALSE the rectangle has to stop). Thanks very much

Chris Cuevas on 16 Nov 2013, 12:32 a.m.

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

Jim Higson on 26 Dec 2013, 12:39 p.m.

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

Peedy on 1 Mar 2015, 7:45 p.m.

THANK YOU FOR PROVIDING THIS PIECE OF CAKE !!!

With your code-snippet I was able to rotate my fan-blades ... :-))

Chapeau ... Peedy

Anonymous on 19 Apr 2015, 4:53 p.m.

It took me hours to get this to work, until I found this.. Thanks for the script!

Alex Latenko on 6 Aug 2015, 3:38 p.m.

Thank you a lot, Peter for this perfect script!

Thats exactly the thing I have being looking for!

Alex

Anonymous on 10 Mar 2016, 11:28 p.m.

In chrome I got this warning.

"SVG's SMIL animations (<animate>, <set>, etc.) are deprecated and will be removed. Please use CSS animations or Web animations instead."

Stefan Fröhlich on 3 Jun 2016, 11:30 a.m.

Brilliant solution! The rotating_gears.svg unfortunately isn't working on IE9-11 and Edge. Even though it should be possible, right?

Any ideas to get it running on MS's offspring?

Cheers,

Stefan.

Warre Buysse on 5 Jun 2016, 1:44 p.m.

This post might be five years old but it helped me understand svg animateTransform rotation, thank you.

kione on 27 Jun 2016, 2:13 p.m.

Still very helpful! Thanks for the share

Andrea on 15 Jul 2017, 10:30 p.m.

Hello ! How can I rotate an object (ex. a rectangle) only when a variable is set at TRUE ? (When the variable is FALSE the rectangle has to stop). Thanks very much

Robert on 8 Aug 2017, 11:37 p.m.

I am using VueJS and trying to do this. I didn't want to use inline JS. For my particular purpose, I was trying to get a circular geo icon to rotate when as user clicks "Find nearby businesses." ... I was able to work around this by rotating the entire SVG box with CSS and not just the path. However, I can see how this wouldn't work unless your viewbox is perfectly square, all sides same length. Since my image was circular, it works for me.

James Briano on 12 Mar 2018, 4:29 p.m.

Thank you.

jumanja on 12 Sep 2018, 10:03 p.m.

Thanks a lot for your site, really helpgul. Regarding SMIL deprecation, it seems Google attempted to deprecate it, but they suspended their attempt, as you can read in this thread: https://stackoverflow.com/questions/30965580/deprecated-smil-svg-animation-replaced-with-css-or-web-animations-effects-hover

Thanks again

Regina on 19 May 2020, 5:28 p.m.

It seems, Google Chrome and Edge both have problems with indefinite rotation - I am rotating a group of elements (a wind-mill) with continuous rotation. Works fine in FF and Safari, but Chrome and Edge counter-rotate to the beginning, destroying the whole effect. Any ideas why?

Challenge. on 20 May 2020, 6:03 a.m.

Here is a challenge I can’t find a solution for: hot do you rotate from 350 degrees to 10 degrees smoothly, passing over 0 degree? Say you have a function that only returned what the next degree should and not know where it’s starting from. Is there a better way than checking if it’s passed half the circumference, and do math to manually go to 360, than stop animating. Then restart from 0 and go to 10 ?

If you just say animate from 350 to 10, it will go the other way around... annoying, but logical. Think of a compass to get the concept.