SVG Groups and Transforms

Tutorial

It is often convenient to treat a collection of graphical elements together as a cohesive whole. Instead of moving each individual chart element, we can move logical groups, such as the x axis labels, all at once. On the same note, we need a way to rotate, change the size of elements, and change the position of elements or groups of elements.

SVG Transforms

SVG transforms allow you to change the size, orientation, and position of graphical elements. All of the SVG elements we have discussed can be modified with the transform attribute. The transform attribute can take several values depending on what change you desire. Below is a table of possible values for transform that we will be using.

TransformWhat it does
translate(x,y)Moves element to the specified x,y position.
rotate(degrees, cx, cy)Rotates the element about a point (cx, cy). Specifying the point is optional. Rotation angle is specified in degrees which go from 0 to 360 degrees (as opposed to radians).
scale(sx, sy)Scales the height and width of the element by the sx and sy scaling factors.

Sample Translate

<rect x="10" y="20" width="20" height="10" style="fill:black"/>
<rect x="10" y="20" width="20" height="10" style="fill:red" transform="translate(40,10)"/>

Think of transformations as creating a new coordinate system. What I mean by this is that when I use translate(40, 10) in the example above, (40, 10) becomes the new (0, 0). A rectangle whose upper left corner is at (10, 20) will be located at (50, 30) after the transformation.

Sample Rotate

<rect x="10" y="20" width="20" height="10" style="fill:black"/>
<rect x="10" y="20" width="20" height="10" style="fill:red" transform="rotate(45,10,20)"/>
<rect x="10" y="20" width="20" height="10" style="stroke:blue; fill:none" 
         transform="rotate(45,10,20) translate(30,0)"/>

A rotate operation changes the coordinate system so that it's pricipal directions change. In the example above a 45 degree rotation makes positive x point down and to the right instead of just to the right. Because many transformations can be specified and are applied in order, the translate on the blue outlited rectangle moves it diagonally. Be conscious of the point of rotation, it is easy to have your object seemingly disappear because the point of rotation isn't where the object is located.

Sample Scale

<rect x="10" y="20" width="20" height="10" style="fill:black"/>
<rect x="10" y="20" width="20" height="10" style="fill:red" transform="scale(2)"/>

In the example above, the scale operation somewhat unexpectedly moved the red rectangle, even though it is only scaled up from the black one. What happened is scale(2) changes the coordinate system so that (1,1) really means (2,2). Likewise (3, 5) becomes (6, 10). With this is mind we now understand why the red rectangle ends up at (20, 40) instead of (10, 20) as we might have initially expected.

As previously mentioned, multiple transforms can be put in a single transform attribute. Each successive transformation creates a new coordinate system relative to the previous coordinate system. Let's walk through an example of creating a circle of circles. We want our circle of circles centered at (100,100) so first we translate each circle to (100,100).

<circle r="3" fill="lightgray" transform="translate(100,100)"/>
<circle r="3" fill="darkgray" transform="translate(100,100)"/>
<circle r="3" transform="translate(100,100)"/>
<circle r="3" transform="translate(100,100)"/>
<circle r="3" transform="translate(100,100)"/>
<circle r="3" transform="translate(100,100)"/>

From there we want to place them in a circle, so we rotate each circle by a fixed amount of degrees. We have 6 circles to make our circle so we start at 0 and increase by 360/6 or 60 degrees for each circle.

<circle r="3" fill="lightgray" transform="translate(100,100) rotate(0)"/>
<circle r="3" fill="darkgray" transform="translate(100,100) rotate(60)"/>
<circle r="3" transform="translate(100,100) rotate(120)"/>
<circle r="3" transform="translate(100,100) rotate(180)"/>
<circle r="3" transform="translate(100,100) rotate(240)"/>
<circle r="3" transform="translate(100,100) rotate(300)"/>

Despite being rotated at different angeles, these circles are all still located at (100,100)! To move them out radially we need another translate, I choose to use a radius of 20 and translate by (20,0). Because each circle has been rotated, each circle is moved by 20 in a different direction and the result is a circle of circles.

<circle r="3" fill="lightgray" transform="translate(100,100) rotate(0) translate(20, 0)"/>
<circle r="3" fill="darkgray" transform="translate(100,100) rotate(60) translate(20, 0)"/>
<circle r="3" transform="translate(100,100) rotate(120) translate(20, 0)"/>
<circle r="3" transform="translate(100,100) rotate(180) translate(20, 0)"/>
<circle r="3" transform="translate(100,100) rotate(240) translate(20, 0)"/>
<circle r="3" transform="translate(100,100) rotate(300) translate(20, 0)"/>

The lightest colored circle is rotated 0 degrees and the second-lightest colored circle is rotated 60 degrees. As you can see rotation is clockwise. It is also clear that transformations are evaluated from left to right because the first translate to (100,100) occured before the rotation, which also occured before the final translate to (20, 0). Notice how the "required" circle attributes of cx and cy are no longer required because we were able to use transformations to move the circles from their default location of (0, 0).

SVG Groups

SVG groups give you the ability to treat a collection of SVG elements as a cohesive whole. Often times we'll want to work with higher-level objects than circles or lines. For example, we'll want to position a chart axis or a legend. Instead of having to move each individual item, we can put all of them elements into a group and move it together using a transform.

SVG groups are created with the g element. All members of the group are contained heirarchically within the g element. For example to put two circle's in a group you would write:

<g>
  <circle cx="20" cy="20" r="5"/>
  <circle cx="10" cy="10" r="5"/>
</g>

The advantage is we can now move this group of circles using a transform without having to manipulate the indiviaul circles. Our previous circle of circles example could have been written as below. The translate(100,100) has been moved to the g element.

<g transform="translate(100,100)">
  <circle r="3" fill="lightgray" transform="rotate(0) translate(20, 0)"/>
  <circle r="3" fill="darkgray" transform="rotate(60) translate(20, 0)"/>
  <circle r="3" transform="rotate(120) translate(20, 0)"/>
  <circle r="3" transform="rotate(180) translate(20, 0)"/>
  <circle r="3" transform="rotate(240) translate(20, 0)"/>
  <circle r="3" transform="rotate(300) translate(20, 0)"/>
</g>

Groups can also be nested, making it easy to create groups, and then group them into higher level groups. For example we could create a chart axis as a group but then we might group the chart area itself to make it easy to put the appropriate padding around it.

Quiz

Things to do

Often you'll want to move parts of a visualization around to make it look good. Let's practice this using groups and transforms to make this easier.

  1. Put the whole chart except the title into a group and move it 20 pixels to the right and 30 pixels down
  2. Put the horizontal and vertical axis labels each into their own group.
  3. Move the vertical axis 5 pixels to the left
  4. Move the horizontal axis 3 pixels up
  5. Put the whole chart, including the title in a group and make the whole group 5% larger.

Extra Credit

Change the code on the left. Once you've made a change, the page will render on the right.