Move And Scale SVG Graphics With The viewBox Attribute

The SVG canvas and the SVG viewport, through which you see the canvas operate on independent coordinate systems. The systems are aligned by default, but you can change the default to move and scale the canvas.

Last week I began a look at the SVG coordinate system, the infinite SVG canvas,and the SVG viewport. Today I want to continue and talk about how we can map the coordinate system of one to the other.

Two SVG attributes allow us to move the canvas around and stretch or shrink part of it so it fits into the viewport. I’ll cover the viewBox attribute today and the preserveAspectRatio attribute next week.

First a quick review.

A Quick Review

Last week I presented a metaphor that helped me understand how SVG works and I want to offer it again as a reminder.

Picture an infinite rectangular canvas on which you can draw anything you want, wherever you want in infinite space. This is the SVG canvas. It’s also called user space. Now imagine someone builds a wall in front of the canvas. The wall is equally infinite to the canvas and covers every bit of it exactly.

Fortunately someone built a rectangular window into the wall. It’s flexible in that you get to set any width and height you want, but once set the window becomes fixed in size and space. This window is the viewport and you set its dimensions when you set the width and height of the element.

The viewBox Attribute

When we stopped last week, I was moving a rectangle around on the canvas. Today we’ll move the canvas itself.

The viewBox is a tool that lets you map coordinates on the canvas to the coordinates of the viewport. You create a viewBox through the aptly named viewBox attribute.

[code type=”html”]
viewBox = <min-x> <min-y> <width> <height>
[/code]

Commas and whitespace are optional so the above could also be written as the following.

[code type=”html”]
viewBox = <min-x>,<min-y>,<width>,<height>
[/code]

The values specify a rectangular area on the canvas, which will be mapped to the viewport. In other words the area specified on the SVG canvas will be moved and scaled so it’s visible within the viewport.

The values for min-x and min-y set the coordinate of the top left corner of the specified region on the canvas. The width and height values set the width and height of this rectangle, which effectively tells us where the bottom right corner of the rectangle is located.

Let’s see how this works with an example.

[code type=”html”]

<circle r="25" cx="25" cy="25" fill="#f00" />
<rect x="500" y="100" width="50" height="50" fill="#933" />

[/code]

Here I’ve drawn a red circle on the SVG canvas. The circle has a radius of 25px and its center is located 25px from the origin along both axes. I’ve also drawn a 50px by 50px green square and positioned it so it’s just outside the viewport.

The viewport is 500px wide and 100px high and like last week I added an outline so we can better see its boundaries.

Here’s the resulting graphic.

You can see the small red circle inside the viewport. The square, positioned outside the viewport, isn’t currently visible.

Let’s add a viewBox and see if we can change that.

[code type=”html”]

<circle r="25" cx="25" cy="25" fill="#f00" />
<rect x="500" y="100" width="50" height="50" fill="#393" />

[/code]

I chose this specific viewBox to start because it changes nothing. The resulting graphic is still the same as the previous example.

The first two values 0 and 0 set the top left corner of the viewBox to the top left corner of the viewport. That’s where it was already located so no change there. The second two values set the width and height to the same dimensions as the viewport, so again where things already were.

Let’s change the first two values and see what happens.

[code type=”html”]

<circle r="25" cx="25" cy="25" fill="#f00" />
<rect x="500" y="100" width="50" height="50" fill="#393" />

[/code]

Here I set both min-x and min-y to 25 (px). I left the width and height the same so we’re not scaling anything just yet.

You can see in the resulting graphic that the circle has moved up and to the left so only its lower right quadrant is visible. The move also brings the top left quadrant of the square into view and it’s now visible in the viewport.

Neither circle nor square has moved on the canvas. Rather the whole canvas has shifted. What we did was grab a point (25px, 25px) on the canvas and moved it to align with the origin. That shifted the enitre canvas up and to the left moving part of the circle outside the viewport and part of the square into it.

The size of the viewBox is still the same 500px by 100px. We grabbed the whole 500 by 100 rectangle and shifted it to show a slightly different area of the canvas.

Let’s simplify the example to look at the last two values and remove the square so we can focus on a single shape.

[code type=”html”]

<circle r="25" cx="25" cy="25" fill="#f00" />

[/code]

Here I set min-x and min-y back to 0 and changed the width and height of the viewBox to be half the values of the viewport. What does this do?

There’s no shifting of the canvas’ top left corner. The origin of the canvas is still aligned with the origin of the viewport. However the bottom right corner of the viewBox now only extends for half the width and half the height of the viewport.

The bottom right corner of the viewBox is then aligned with the bottom right corner of the viewport and the viewBox itself is scaled so all four of its corners align with all four corners of the viewport.

In the case of this example that requires stretching the specified area of the canvas and doubling its size in both the x and y directions. The circle is scaled with the viewBox and it should end up twice its original size in both directions as well.

Here’s the resulting graphic and you can see the circle has scaled up and it’s radius is now twice the 25px set on it.

Here’s another way to think about what’s happening. The viewBox goes from 0 to 250 units along the x-axis. That means 250 units of viewBox needs to equal 500px (the viewport width).

[code type=”html”]
500px (viewport) / 250 (viewBox) = 2px
[/code]

To fit 250 units of viewBox inside a 500px viewport means every unit of viewBox must be 2px. Any dimension set on the elements inside the viewport will be twice the dimension set. The calculation above is for the x-axis only, but the same is true along the y-axis as well.

[code type=”html”]
100px (viewport) / 50 (viewBox) = 2px
[/code]

By setting the viewBox to half the dimensions of the viewport, we’ve effectively scaled the circle to be twice its previous size. It now has a radius of 50px and it’s center is located 50px from the origin in both the x and y axes, which is exactly what we see in the graphic.

Similarly if we double the size of the viewBox to 1000 by 200 we effectively shrink the elements inside the canvas to half their size. Here’s how it looks.

You aren’t limited to doubling or halving things, of course.

[code type=”html”]

<circle r="25" cx="25" cy="25" fill="#f00" />

[/code]

In this example, not only did the values change, but the aspect ratio of the viewBox (1:2) is no longer the same aspect ratio as the viewport (5:1).

Let’s see what that does to our circle.

It’s stretched considerably (10 times) in the x direction, while maintaining its size in the y direction. It doesn’t look very good because the aspect ratio of the circle wasn’t preserved.

I have to admit I made one addition to this SVG that I didn’t show in the code preceding it. By default the aspect ratio would be preserved, but I turned that off so we could see what happens when it isn’t.

I wanted to show you what could happen when the viewBox and viewport have different aspect ratios as a way into talking about the different ways in which the aspect ratio can be preserved. That’s where I’ll continue next week.

Closing Thoughts

The SVG canvas lives in its own coordinate space. The viewport through which we see a part of the canvas lives in its own coordinate space. Both systems are aligned by default allowing us to see one specific area of the canvas.

The viewBox attribute allows you to change that default. You can use it to show different regions of the canvas by mapping selected points on the canvas to the four corners of the viewport.

Through the viewBox attribute you can shift the canvas left, right, up, and down. You can also scale the canvas and show an area of it larger or smaller than its original size.

I’d encourage you to play around with a simple example like I’ve done here and in the previous post. Isolate one value at a time in the SVG element, the viewBox or the shapes inside, change it, and see what happens. When you have a handle on the specific property or attribute, isolate and change something else.

I found where the viewBox scaling is concerned it helps if you change the width and height values so they’re double or half the viewport dimension. It also helps to work in one direction (x or y) at a time. However, feel free to experiment with whatever values you want.

Next week I’ll continue and talk about how to deal with a viewBox with a different aspect ratio than the viewport into which it’s aligned. Well talk about the preserveAspectRatio attribute and the different methods to preserve or not preserve the aspect ratio of the original graphics.