For the last few weeks we’ve been looking at SVG coordinate systems, the SVG canvas, the viewport through which we can see a part of the canvas, and the viewBox that lets us map a region of the canvas into the viewport.
Last week I left off with an example where the viewBox and the viewport were different aspect ratios. In the example I started with a circle and because of the viewBox I selected, the circle was distorted considerably in the x direction.
That might be the behavior you want at times, but odds are most of the time it isn’t. We need a way to tell browsers how we want elements to display when the aspect ratio of the viewBox is different than the aspect ratio of the viewport.
The preserveAspectRatio Attribute
The preserveAspectRatio attribute tells the browser how to display the canvas when the aspect ratio of the viewBox and viewport are different.
If no viewBox has been created then there can’t ba an aspect ratio conflict and so preserveAspectRatio is ignored. The attribute takes at least one and as many as three different values. The first value, defer, is optional and applies only to images.
preserveAspectRatio = defer? <align> <meetOrSlice>?
If preserveAspectRatio is set directly on an image and the first value is defer, then any preserveAspectRatio set on the referenced content should be used instead of the value set on the image. The image defers to the referenced content. If no image is present defer is ignored.
The align value directs browsers to force uniform scaling (no deformation) on the viewBox as well as what method of uniform scaling to use. One allowable value for align is none.
preserveAspectRatio = "none"
When set to none the aspect ratio will not be preserved and the four corners of the viewBox will align with the four corners of the viewport. The SVG canvas will be scaled to fit and the aspect ratio will be whatever it needs to be.
Note: In the last example of last week’s post, I set preserveAspectRatio to none so you could see the distortion in the circle. That’s the part I didn’t show in the code if you were wondering.
There are nine other possible values for preserveAspectRatio besides none. The values come from combining three options each in x and y directions.
- xMin — Aligns the left edge of the viewBox with the left edge of the viewport.
- xMid — Aligns the midpoint of the viewBox with the midpoint of the viewport along their x-axes
- xMax — Aligns the right edge of the viewBox with the right edge of the viewport.
- YMin — Aligns the top edge of the viewBox with the top edge of the viewport.
- YMid — Aligns the midpoint of the viewBox with the midpoint of the viewport along their y-axes
- YMax — Aligns the bottom edge of the viewBox with the bottom edge of the viewport.
The Min values align the viewBox to the left or top edge of the viewport, the Mid values align the midpoints of viewBox and viewport along the specified axis, and the Max values align the right or bottom edges of the viewBox and viewport.
To set the actual
<align> value you combine the x and y components into a single value like xMinYMid or xMidYMid. The later is the default value by the way.
All these different values can get confusing so let’s take a look at some examples and align a circle in different ways to see what happens.
1 2 3
<svg width="500" height="100" viewBox="0 0 50 50" preserveAspectRatio="xMinYMin" style="outline: 5px solid #630; margin: 1.6em 0"> <circle r="25" cx="25" cy="25" fill="#f00" /> </svg>
I scaled the viewBox to be a different aspect ratio than the viewport. It’s stretched 10 times along the x-axis and twice along the y-axis. preserveAspectRatio is set to xMinYmin, which aligns the canvas to the left and top edge.
If we hold the y component constant at Min and change the align value to xMidYmin here’s the result.
Here’s what xMaxYmin looks like.
All three results are probably what you expected. I held the y component of the value constant and changed the x component of the value to see it aligned to the left, in the center, and to the right.
What happens if we change the y component to YMax and use a value of xMinYMax instead of xMinYMin?
Nothing. There’s no change? We’ll get the same result if we use a y component of YMid. Why? You might think it’s because the viewBox is already aligned to both the vertical center and the bottom edge of the viewport, but that’s not the reason.
To answer the question why, I first need to cover the last possible value of preserveAspectRatio,
<meetOrSlice>, which takes a value or a value of slice
- meet — preserves aspect ratio and scales the viewBox so it fits in its entirely within the SVG viewport.
- slice — preserves aspect ratio, lets the viewBox scale past the boundaries of the SVG viewport, and slices off any part that doesn’t fit in viewport.
When meet is set, the viewBox will scale up until one edge meets an edge of the viewport. When scaling down the viewBox will shrink until both edges are inside the viewport. In either case the viewBox will be same size as the viewport in one direction, and be smaller in the other direction.
When slice is set, the viewBox will grow until the edges in both directions have at least reached the edge of the viewPort. When scaling down the viewBox will shrink until one edge of viewBox meets the edge of viewport.
In either case the viewBox will will be the same size as the viewport in one direction and be larger in the other direction. The portion of the viewBox that exceeds the boundaries of the viewport will be sliced cropping the viewBox.
Let’s rewrite the previous example to include a meetOrSlice value.
1 2 3
<svg width="500" height="100" viewBox="0 0 50 50" preserveAspectRatio="xMinYMin meet" style="outline: 5px solid $63"> <circle r="25" cx="25" cy="25" fill="#f00" /> </svg>
The result when the value is meet is the same as before for an alignment of xMinYmin because meet is the default. The previous example used meet even though we didn’t explicitly set it.
Here I changed the value to slice.
1 2 3
<svg width="500" height="100" viewBox="0 0 50 50" preserveAspectRatio="xMinYMin slice" 5px solid #630;"> <circle r="25" cx="25" cy="25" fill="#f00" /> </svg>
The result is very different.
With slice set, changing the x component of
<align> has no effect, but changing the y-component does.
Again what’s going on? In both cases the viewBox is scaled until one of its edges is at the edge of the viewport. In one case the other edge is inside the viewport and in the other case it exceeds the viewport. In either case the opposite direction is the component of
<align> that controls.
In the original examples the viewBox was scaled until its bottom edge reached the edge of the viewport. To align the viewBox the opposite direction (x in this case) controlled and the circle moved horizontally depending on whether xMin, xMid, or xMax is specified.
In this second set of examples with slice set, the viewBox was scaled until both of its edges reached (or exceeded) the edges of the viewport. In this example that happens when the right edge of the viewBox reaches the right edge of the viewport. Again the opposite direction controls the alignment and the circle moves vertically depending on whether YMin, YMid, and YMax is specified.
It will always be one or the other (x or y) component that controls alignment. Which one depends on the value set for meetOrSlice and which edge of the viewBox and viewport end up in contact.
I hope the past few posts have helped to show what’s going on with the SVG canvas, the viewport, and the viewBox. Like I said at the start, I originally found it confusing to conceptualize what was happening.
The metaphor of a canvas (SVG canvas or user space), a wall, and a window (viewport) helped me, but if you’re still unsure how this all works take a look at Sara Soueidan’s article on the same topic. Sara uses a different metaphor, which might help if mine didn’t.
Jakob Jenkov also has a nice and easily understandable tutorial. I leaned on both Jakob and Sara for this and the previous posts in this round of the series.
As I did last week, I’ll encourage you to play around with some examples. Seeing the results of specific values also helped me understand what was going on and what will happen when the same value is set in the future on another example.
That’s all for now about canvases and viewports and viewBoxes, but it’s not the end of my current look at SVG. Next week I want to continue and talk about how you can group SVG objects to structure more complex graphics.
In the weeks that follow I’ll show you how to define graphics in part or whole for reuse elsewhere in your document to help make yoru SVG more modular and easier to maintain.
Download a free sample from my book, Design Fundamentals.