SVG transform: translate, scale, rotate and matrix
The SVG transform attribute moves, resizes, turns and skews a shape or group. How translate, scale, rotate and matrix work, and how to turn about the centre.
Published
The transform attribute moves, resizes, rotates or skews an SVG element without changing its coordinates. It takes one or more functions, translate(), scale(), rotate(), skewX(), skewY() and matrix(), and applies to a single shape or to a group and everything in it.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect x="40" y="40" width="20" height="20" fill="teal" transform="rotate(45 50 50)"/>
</svg>
That square is turned 45 degrees about the point 50,50, its own centre, and so appears as a diamond.
The functions
| Function | Effect |
|---|---|
translate(tx ty) | Moves by tx across and ty down. With one number, ty is 0 |
scale(sx sy) | Multiplies x by sx and y by sy. With one number, both use it |
rotate(angle) | Turns clockwise by an angle in degrees, about the origin |
rotate(angle cx cy) | Turns about the point cx,cy |
skewX(angle), skewY(angle) | Slants along one axis |
matrix(a b c d e f) | Any combination of the above as six numbers |
In the attribute, numbers have no units, angles are in degrees, and values are separated by spaces or commas.
Everything happens about the origin
A transform does not act on the shape. It acts on the coordinate system the shape is drawn in, and that system’s fixed point is the origin, 0,0, which is normally the top-left corner of the drawing. This explains the two surprises everyone meets.
Scaling moves the shape. scale(2) doubles every coordinate, so a square whose corner is at 40,40 ends up with its corner at 80,80. It is twice the size and twice as far from the origin.
Rotating swings the shape away. rotate(45) turns the shape about the top-left corner of the drawing, like the hand of a clock, and it often swings out of view.
Rotate or scale about the centre
For rotation, give the centre as the second and third numbers:
<rect x="40" y="40" width="20" height="20" transform="rotate(45 50 50)"/>
The centre of a rectangle is x + width / 2, y + height / 2. For a circle it is cx, cy.
scale() has no such option. Move the centre to the origin, scale, and move it back:
<rect x="40" y="40" width="20" height="20"
transform="translate(50 50) scale(2) translate(-50 -50)"/>
The same three steps work for any transform, and rotate(45 50 50) is itself shorthand for translate(50 50) rotate(45) translate(-50 -50).
Order matters
Several functions in one attribute are not interchangeable. Each one changes the coordinate system for those to its right, so in effect the rightmost is applied to the shape first.
| Transform | Where the point 10,0 ends up |
|---|---|
translate(50 0) rotate(90) | Turned to 0,10, then moved to 50,10 |
rotate(90) translate(50 0) | Moved to 60,0, then turned to 0,60 |
Transforms on nested groups combine in the same way: a shape is affected by its own transform and then by that of each group around it, working outwards.
Flipping
A negative scale mirrors the drawing. scale(-1 1) flips left to right and scale(1 -1) flips top to bottom. Because the mirror line runs through the origin, the shape lands on the other side of it, outside the picture. Bring it back with a translate equal to the width of the drawing:
<g transform="translate(100 0) scale(-1 1)"> ... </g>
In a drawing 100 units wide, that sends each x to 100 minus x: a flip in place.
The matrix form
Every transform can be written as matrix(a b c d e f), which maps each point like this:
new x = a*x + c*y + e
new y = b*x + d*y + f
| Function | As a matrix |
|---|---|
translate(tx ty) | matrix(1 0 0 1 tx ty) |
scale(sx sy) | matrix(sx 0 0 sy 0 0) |
rotate(θ) | matrix(cos θ, sin θ, -sin θ, cos θ, 0, 0) |
Vector editors often save a single matrix() in place of a list of functions, since any list multiplies out to one matrix. The last two numbers are the movement, which is the quickest thing to read from one.
What gets transformed
Everything about the element: its position, its size, the thickness of its stroke, and any pattern or gradient fill. A line with stroke-width="1" inside scale(3) is drawn 3 units thick. To keep outlines at a constant thickness, add vector-effect="non-scaling-stroke" to the shape. A scale that differs between x and y makes strokes uneven, thicker in one direction than the other.
Transforms in CSS
Browsers also accept the CSS transform property on SVG elements, with CSS syntax: units are required, as in rotate(45deg) and translate(10px, 5px). If an element has both the attribute and a CSS rule, the CSS replaces the attribute and the two are not combined.
The origin is the catch. For HTML elements transform-origin defaults to the centre of the element. For shapes inside an SVG it defaults to 0,0 of the drawing, as with the attribute. To turn a shape about its own centre in CSS, set both of these:
.spinner {
transform-box: fill-box;
transform-origin: center;
transform: rotate(45deg);
}
transform-box: fill-box makes the origin relative to the shape’s own bounding box.
Transform or viewBox?
To move, zoom or crop the whole drawing, change the viewBox. To move one shape or group inside the drawing, use a transform.
Files for cutting machines and plotters
Transforms are a standard part of SVG and every browser and vector editor applies them. Some simpler programs that read SVG for a machine do not apply every transform, and parts of a design then arrive in the wrong place or at the wrong size. If that happens, the cure is a file whose coordinates are already final, with no transform attributes left in it. Open the file in the SVG viewer and search the code for transform to see whether it has any.
To turn or flip a whole file without editing its code, rotate or flip SVG wraps the drawing in one group and refits the frame.
Common problems
| What you see | Cause | Fix |
|---|---|---|
The shape vanishes after rotate() | It turned about the origin and left the viewBox | Give the centre: rotate(angle cx cy) |
| The shape moves when scaled | Scaling is about the origin | translate(cx cy) scale(s) translate(-cx -cy) |
| The shape vanishes after a flip | It was mirrored across the origin | Add a translate by the width or height |
| Outlines get thicker when scaled | Strokes scale too | vector-effect="non-scaling-stroke" |
| A CSS rotation turns about the wrong point | The origin defaults to 0,0 of the SVG | transform-box: fill-box; transform-origin: center |
| The attribute is ignored | A CSS transform on the same element replaces it | Use one or the other |
rotate(45deg) in the attribute fails in some software | The attribute takes plain numbers | Write rotate(45) |
Questions
Which way does a positive angle turn? Clockwise on screen, because the y axis points down.
Can a transform be animated? Yes: in CSS with a transition or animation on transform, or with JavaScript changing the attribute.
Does a transform change the path data? No. The d attribute of a path stays as it was, and the transform is applied each time it is drawn.