Working with SVG Transformations
Introduction
Having recently re-engaged in a passion project of mine -- building an SVG/javascript artworks framework -- I found myself needing to make use of SVG transforms. Transforms are a very powerful feature of SVG that can be used to create and position shapes, move and deform elements, and modify paths to create amazing and beautiful effects and animations. But, in order to exploit this feature to its utmost potential, programmers and artists must make the effort to fully understand SVG coordinate systems and how transforms work!
Coordinate Systems in SVG
SVG was created to define images in terms of mathematical abstractions as opposed to bitmap representations. In so doing it opens up a whole new universe of possibilities for creative types to produce artworks -- art that can be beautifully rendered with full fidelity without concern for information loss due to resolution. Conceptually, as a creator you're still working with a canvas. But to produce your art you're manipulating lines and shapes in ways that, to me, feels more like sculpting than like painting.
In any case, to best make use of the features offered by the SVG format (especially transformations), we need to understand the constructs used by the system. Let's start with the SVG canvas and associated concepts; the viewport and the viewBox
attribute.
-
The SVG canvas is an infinite, abstract coordinate space where all SVG content theoretically exists. Think of it as a conceptual drawing surface that extends indefinitely in all directions.
-
The SVG viewport is the visible region where the SVG is rendered. The viewport determines how much of the canvas is displayed on the screen. The viewport's coordinate system has its origin at (0,0), with $x$ increasing to the right and $y$ increasing downward (consistent with most computer graphics systems).
-
The
viewBox
attribute maps a specific region of the SVG viewport to a display area in an SVG client (a device rendering the artwork).
Visual thinker that I am, I've drawn an SVG illustration to try to illustrate these important conceptual elements...
The renderable region of the canvas is defined by width
and height
attributes on the svg
document element as shown in the following snippit.
<svg id='example_1'
width="300"
height="400"
viewBox="0 0 300 400">
...
</svg>
The viewBox
attribute defines a coordinate system as a basis for transforming SVG canvas coordinates to fit a specific display area. The syntax is minX minY width and height. The SVG elements in the defined region will be mapped to the client display area. If the width and height values are the same for the canvas and viewBox the mapping is 1:1. If the values differ between the two the client will translate and scale the canvas elements to fit the viewBox coordinate system. For example, if the viewBox
values above were changed to 0, 0, 600, 800 the SVG elements would be scaled down by half. If they were changed to 0, 0, 150, 200 they'd be scaled up by a factor of 2.
Client and SVG Viewports
Understanding these concepts becomes even more important when you embed your SVG in HTML Web Pages and other applications. In such cases you have to worry about not just your SVG but also the client viewport. Here I've created an illustration depicting an SVG viewport embedded in a client webpage.
If you happen to be reading this on computer and move your mouse around the illustration you can get a feel for the two coordinate systems. The dynamic display should update both the client and SVG coordinates for the model.
Key Takeaways
So the key takeaways from all this is that in working with SVG it's important to understand the conceptual relationships between the canvas, the viewbox and the SVG and client viewport coordinate systems.
Here's another way to think about these things:
-
The SVG canvas is like a World Coordinate System. Imagine an abstract, infinite coordinate plane on which you get to draw. This is where the SVG exists conceptually.
-
The User Coordinate System is defined by your SVG document. The
<svg>
element defines a view into your world. This is the user coordinate system. -
The Viewport Coordinate System creates a basis for a Transformed View. As we'll see shortly a big part of the magic of SVG is that transforms can be applied to elements to create new shapes effects and animations. But the
<svg>
element itself undergoes a transformation determined by it's viewBox. This transform modifies how the user coordinate system is mapped to the actual display area (the viewport).
Why doe all this matter? I've seen folks working with software like Illustrator and other vector arts creation tools get bogged down when trying to export their content for display in other clients (think web browsers, phones and even physical media). Without a deep understanding of the concepts covered here it's easy to get puzzled when you're working with with extensive tool chains.
Tip
For illustration I primarily work with Inkscape (a very powerful open source tool for creating vector graphics). In using Inkscape as part of a tool-chain (e.g., for Web development or working with the soon-to-be revealed SVG Artworks Framework) you'll generally want to insure a 1:1 mapping between your SVG canvas and viewbox settings. In other words, make sure your viewbox settings are 0, 0, width and height with width and height corresponding to your root SVG.
SVG Transformations
Armed with our thorough and comprehensive understanding of SVG coordinate systems we're ready to apply our knowledge to SVG transformations. Transformations can be applied to SVG elements and groups to compose objects, create effects and enable animations. Transformations are acheived using the transform
attribute in combination with built in SVG functions for translation, rotation, scale and skew. I'll illustrate each of these functions in turn using this compass needle I created to the right.
Translation
Translation moves an elment or group relative to it's origin in the SVG viewport coordinate space. You can translate elements or groups using the translate( x, y )
function as a value for the transorm property.
To demonstrate this I've translated the needle by 200 pixels in both the x and y directions using the following code:
<g id="needle"
transform="translate(200, 200)"
>
<!-- SVG CODE DEFINING THE NEEDLE -->
</g>
Important!
It's important to note that when a transform is applied to an element in SVG technically it's applied to the element's local coordinate system.
To illustrate the point, I've drawn the needle's local coordinate axes in green. So in addition to the viewport coordinate system all objects on the canvas have their own local coordinate systems affecting their transformations.
Rotation
Next let's look at rotating objects. In the same way we used the SVG built-in function translate
to move our needle we can use the rotate
function to rotate it around a point. Here I'll rotate the needle 45°. <g
id="needle"
transform="rotate(45)"
>...</g>
This works as expected because I defined the compass needle with its local coordinate system origin to be the same as the viewport origin. In other words, the center of the needle is at $P = (0,0)$ by design. But what if I have an object that is not centered at the canvas origin?
In the next example I've created a windfarm sprite with a propeller group centered at $(100, 100)$. Notice what happens when I try rotating the group by 15°. The group is rotated 15° about the viewport origin. And this is not exactly what I might want here. The reason is that the propeller-group local coordinate system starts out the same as the viewport coordinate system both centered at $(0, 0)$ on the canvas. The propeller components (the blades and center) are defined by points that are offset from the origin and when I apply rotate
the group is rotated as a whole by 15° about the origin (I've tried to show the rotation with a green arc).
Propeller at
(100, 100) |
Rotated 15° about viewport
origin: rotate(15) |
For this reason -- the rotate
function permits additional arguments to specify the $x$ and $y$ coordinates about which to rotate a target like so:
<g id="propeller"
transform="rotate(45 100 100)"
>
...
</g>
The arguments to rotate
are:
1. The angle of rotation, followed by
2. The $x$, and $y$ coordinates of the pivot point.
Tip
If you are an artist making SVG sprites design them to be centered at the SVG canvas origin (0, 0). Otherwise users of your artworks may have to calculate corrections depending on their transformation needs.
Bonus: Animation Preview
It's worth noting here that a similar effect can be achieved using the transform-origin
attribute. transform-origin
can be applied to the target of a transform or an animation involving a transform. In this example, I've applied the attribute to the propeller group on the windmill. The effect is the same as above -- it specifies a pivot point for the transformation at (100, 100).
<g id="propeller"
transform-origin="100 100"
...
>
...
</g>
This is particularly useful for animation (which I'll be covering extensively in forthcoming blog series revolving around my SVG Artworks Framework). But -- to forshadow -- a quick and easy way to achieve basic animation is using SVG tags (technically SMIL). To animate this example is as easy as adding an animateTransform
tag to the SVG.
<g id="propeller" transform-origin="100 100" ... > <!-- Animate Rotation --> <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="2s" repeatCount="indefinite"/> ... </g>
And voilà! Rotation at work for us. Since XML is human readible I'll leave it to you, the reader, to parse the attributes. In this case they're pretty obvious.
Scale
Next let's look at scale. Scale puts the 'S' in SVG. Again, a large part of the beauty of Scalable Vector Graphics is that lines and shapes can be scaled to any size for presentation without loss of information. To illustrate this point consider the SVG graphic that I created as an icon for my blog (shown on the right). I use it as a "favicon" -- an image which browsers display in tabs holding the blog pages. Below I've scaled up the graphic to 10 times it's original dimensions. To the naked eye the scaling operation reveals greater detail associated with the image than can be observed in it's usual scale. Down and to the right I've applied the same scaling operation to the image in rastor format (png). The result presents with a classic case of pixellation typical of changing the resolution of bitmap images.
SVG favicon scaled by 10x
|
![]() Same icon in PNG format when scaled.
|
Historically, this aspect of the SVG format was extremely significant for web-development (and, indeed, was part of the impetus behind the effort). Before browser support for the SVG standard became available web designers had to spend considerable effort creating graphics icons used across their websites. The problem was compounded as more Internet capable devices emerged. For any given graphic multiple versions had to be created and managed to support varying resolutions. But with SVG support now ubiquitous across graphics rendering systems designers no longer have that problem.
Using SVG, scaling objects is as simple as using the scale
function in a transform. In the next example I've scaled the compass needle we used earlier to three times it's size. For comparison, I'm showing it side by side against the original size in the same SVG file.
Here's the code (just a one liner) ...
<g id="needle_scaled"
transform="translate(300, 200) scale(2)"
>
...
</g>
There are a couple of important things to notice here. First and formost, if you look closely you'll see I've applied not just one but two functions in the transform attribute; scale
of course but also translate
. I did that in order to show the original and scaled needles side-by-side. But I also wanted to make the point that the transform
attribute actually takes a list of functions and will apply them in the order given.
And that brings us to the second major point here; order matters. Remember, that the transform functions are applied to the coordinate systems of the targets. So in this case we (1) translate the object and then (2) scale it.That's different than applying scale
and then translating the object.
<g id="needle_scaled"
transform="scale(2) translate(300, 200)"
>
...
</g>
In the latter case we first scale the cooridinate system of the needle, and then translate it by the scaled coorinates effectively moveing it outside the original SVG viewport (which I can show by playing with the viewBox) ...
Important
So try to keep in mind that all these transforms apply to the targets' coordinate systems. And the order of operations counts! Applying the functions in the wrong order can lead to unexpected results.
The Story so Far...
At this point it's probably worth summing up what we've covered so far.
-
We started with a discussion of SVG and client coordinate systems and saw how the canvas sets the basis for moving stuff around in SVG.
-
Next we explored some of the main SVG transform functions; translate, rotate and scale and saw how these functions can be applied to primitive shapes and groups using the
transfom
attribute. -
As we just saw, these functions can be applied in series. Remember, order counts!
-
And a more subtle point to keep in mind, all these functions are applied to the local coordinate systems of the transform targets (and we briefly went into the implications of that fact).
-
And as an added bonus we got a bit of a preview to another topic I'll cover in greater depth elsewhere; animating SVG.
All that being said and done, we still have a few more topics to cover to round out SVG transforms.
Skew
The final SVG transform attribute function we can look at is skew. A skew transform in SVG distorts an object by slanting it along the x-axis, the y-axis, or both. In geometry that would be referred to as a shear transformation. To apply shear use the skew
function in the transform
attribute as shown in the following card examples...
This first example skews the card 30° along X using:
<g id="card_sheared" transform="skewX( 30 )" > ... </g>
The next skews a card on the Y axis.
<g id="card_sheared" transform="skewY( 30 )" > ... </g>
And as we see next, you can apply skew on both the X and Y axes.
<g id="card_sheared" transform="skewX( 30 ) skewY( 15 )" > ... </g>
And finally, as I've shown below, you can apply multiple transforms including skew in a single transform attribute. I'll leave it as an exercise for the reader to identify the transformations I applied to create the effect.
As we've seen applying skew in SVG is straightforward and can be used to create compelling effects. Beyond playing with cards it can be applied to slant text, create shadows, and create reflections. In games and simulations skewed shapes can be used to build orthographic projections adding a whole new dimension of information to an SVG scene.
This just about rounds out our discussion of SVG transformations. We've looked at translation, rotation, scale and shear and seen how these transformations can be applied to create interesting and beautiful effects and objects suitable for a multitude of purposes. But a discussion of SVG transformations would not be complete without due consideration of the transformation matrix.
SVG Transformation Matrices
So it turns out that all the transformations we've discussed up to this point can be represented and handled mathematically using SVG transformation matrices.
LinAlg for Artists
Now at this point I can imagine many reactions like; "Waitaminnit Nick! Why on earth would I ever want to work with a matrix? Doesn't that involve math?!" And you're right. It does. So, yeah this section on the SVG transformation matrix is a bit more advanced. And to be honest some folks might want to skip it. And you probabably could and still take away a lot from this blog post. But I'd really like to encourage you not to. I know many folks have somewhat of an aversion to it, but, personally, I believe anyone with the desire can do the math -- and maybe even come to appreciate its beauty. In any case, for SVG creators it's important to at least know about the math. And who knows -- exploring a bit of math may open up whole new worlds of creative possibilities for you. To that end, I've written a primer which explains the math related to transformations covered in this blog post. So if you're interested in learning a bit about linear algebra for computer graphics I'd highly encourage you to give it a shot.
Forms
SVG transformation matrices have the following form:
$$ TM = \begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix} $$
Where a, b, c, d, e, and f are values that can be applied to transform coordinate systems in all the ways we discussed above with functions. The following list shows the transformation matrices for translation, scale and rotation.
-
Translation: $$ TM_{translation} = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} $$
-
Scale: $$ TM_{scale} = \begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix} $$
-
Rotation: $$ TM_{rotation} = \begin{bmatrix} cos(a) & -sin(a) & 0 \\ sin(a) & cos(a) & 0 \\ 0 & 0 & 1 \end{bmatrix} $$
Mappings
As we've been seeing all along, transformations provide a mapping from a prior (or parent) coordinate sytem to a new coordinate system. Transformation matrices provide a formal means of describing the mapping operations. The general form of the mapping looks like this:
$$ \begin{bmatrix} x_{prevCoordSystem} \\ y_{prevCoordSystem} \\ 1 \end{bmatrix} = \begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} x_{newCoordSystem} \\ y_{newCoordSystem} \\ 1 \end{bmatrix} $$
Given the general form, let's look at a concrete example.
Example
Let's revisit our windmill. Since wind direction can change we might need to rotate our blades in order to efficiently generate power. So let's see how we'd apply a transformation matrix to achieve that end.
Once again, consider the SVG transformation matrix.
$$ \begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix} $$
Notice that only the first two rows represent used values. The bottom row is an identity provided to enable matrix multiplication (think of it as multiplying any number by $1$ -- you get the same number; identity). Since this is the case we can reduce the matrix to a vector of six values: $[a, b, c, d, e, f]$ where:
- $a$ = scale factor on the X-axis,
- $b$ = skew on X,
- $c$ = skew on Y,
- $d$ = scale factor on the Y-axis,
- $e$ = X-axis translation, and
- $f$ = Y-axis translation.
So for this example, I wanted to achieve the effect of rotating the blades about the mast of the wind turbine. To do so I had to:
- Translate the blades along the X-axis, and
- Scale them down on X (to get the right perspective).
Here's the svg fragment with the relevant matrix
function:
<g id="windmill">
...
<g id="blades"
...
transform-origin="100 100"
transform="matrix(0.6, 0, 0, 1, 10, 0)"
>
...
</g>
...
Really Nick! Why on Earth Would I Ever want to Work with a transformation Matrix?!
If you've made it this far into this post you probably fall into one of two camps:
-
Like me, maybe you appreciate the beauty of mathematics and have enjoyed the discussion around the application of matrix operations to achieve SVG transforms.
Or...
-
Maybe you don't appreciate the mathematics underlying SVG's linear transformations and through eyes glazed over are wondering why on earth you should ever have to worry about it.
Fair enough. Working with SVG you can achieve quite a bit with the function syntax -- which may feel more "user friendly". Either way though, the view into the matrix operations applicable to SVG is good to have and keep in mind. And there are a number of reasons to consider the matrix approach in the creation of your artworks. Especially if you use animations and need to make your work interactive.
The SVG transformation matrix provides a powerful mechanism to for manipulating and animating SVG lines shapes and objects. The ability to apply matrices in SVG transforms offers numerous key advantages. Here's a short list.
-
Efficiency. As we've seen, a single matrix can represent a combination of multiple transformations (e.g., translation, rotation, scale, and skew). This allows you to apply multiple transformations in a single step, which is more efficient than applying them sequentially.
-
Control. Matrices provide precise control over the order in which transformations are applied. Beyond that, knowledge of matrix applications enables the creation of custom effects over and above the list we've covered here. Perspective is an important example.
-
Optimization. The matrix allows individual transformations to be mathematically combined into a single, equivalent transformation. Consolidation reduces the calculations to be performed by the rendering engine leading to significant performance benefits.
-
DOM Manipulation. Using matrix computations as the potential to reduce DOM manipulation which can have big impacts on performance.
The bottom line is that using the transformation matrix to combine multiple transformations like translate, skew, and rotate can optimize your SVG by reducing processing overhead and leveraging GPU optimization. This is particularly beneficial for complex SVGs and animations where performance is a key factor.
Conclusion
This post ended up a bit longer than I'd originally anticipated. Originally I'd intended to write focusing mainly on just the SVG transformation matrix. But soon into it I realized I'd have to provide some context -- namely the discussion around coordinate systems. And as I wrote, I went down the garden path of exploring the various SVG built-in transformation functions and thinking about the implications for creating SVG artworks. Nonetheless, I'm happy to've gone down that path. In doing so I hope to have shared some of the rich and vibrant features SVG offers to creative types of all kinds. I feel we've covered a lot in this post -- but keeping the main points we've covered in mind; the way viewport and client coordinate systems work, the SVG functions central to transformations, and the power of the transformation matrix itself will bring you a long way toward understanding what the world of Scalable Vector Grapics opens up!