Mastering the viewBox for fun and profit
Tags: svg, tutorial, webdev
Published: 2020-07-17 20:05:59

This is the first of a series of articles I'm planning to write about a couple of powerful yet fairly simple concepts of the SVG image format: the **viewport** and the **viewBox**.
As I'm going to show you, these are very flexible features: indeed they can be (ab)used to obtain many different effects!

In this article I will first introduce the SVG image format. Then I will move on to describe the SVG coordinate system and positioning.

<!-- I will try to keep the level of the description as basic as possible. If you want to delve further into the argument there are many great resources on the web, such as the great Sara Soueidan blog. -->

# The SVG format

SVG is a rather peculiar format. Unlike raster formats that describe an image through a grid of pixels, SVG describes an image using **algorithms** that define the shapes and paths that create the complete image.

SVG isn't suited to represent photographic images, but is particularly suited to draw **illustrations and icons** in a **compact way**. An SVG image is basically composed of one or more "shapes", each described by a specific mathematical function. Thanks to this, an SVG image can be enlarged/zoomed as much as you want, unlike the raster image formats.

Another difference with the bitmap formats is that SVG is a **human-readable** text format. In fact an SVG image is nothing more than an XML document. Each shape can be defined using simple textual instructions in the form of XML nodes.

For example, this markup creates an image with a blue circle:

```xml
<svg xmlns="http://www.w3.org/2000/svg">
	<circle cx="50" cy="50" r="50" fill="#0971f1"/>
</svg>
```

The SVG specification defines many other tags that can be used to create both basic and complex shapes, such as rectangles, arcs, ellipses, or "paths", that can be used to define custom shapes using so called "Bezier curves".

<!-- The SVG specification defines many other tags that can be used to create both basic and complex shapes. The "path" tag is the most powerful of these, since it can be used to create custom shapes using so called "Bezier curves". -->

Another great feature of the SVG file format, especially when used inside a web page, is that it can be **styled** in many ways through dedicated **css** properties or through so called "presentation attributes", i.e. CSS properties that can be used as attributes on SVG elements.

For example, you can change the color of a shape by simply setting the "fill" property.

In the previous example, we indeed defined the color of the circle using the "fill" attribute. Another way would be to assign the circle node a class, and then defining the style rules for that class in a css file:

```xml
<svg xmlns="http://www.w3.org/2000/svg">
	<circle cx="50" cy="50" r="50" class="mycircle"/>
</svg>
```

```css
.mycircle {
	fill: #0971f1;
}
```

## The viewport and the viewBox

The _viewport_ and the _viewBox_ are two very important concepts that affect the size, the position and the visible area of an SVG image.
<!-- To better understand them, it is useful to "visualize" them through an example. -->

You can effectively think of the **viewport** as your mobile phone **display** with the camera app running.
The **viewBox** is the **part** of the **scene** you see inside the display. You can zoom in and out or move the phone and frame different parts of the scene, so to change the viewBox, but it will always be constrained to the physical size of the display, i.e. the viewport.
Lastly, the whole scene represent your SVG image, the so called **SVG canvas**.

![SVG viewport and viewBox example](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_example.png?v=1)


<!-- You can effectively think of the _viewport_ as a window through which you can see part of the outside landscape. The _viewBox_ is the part of the landscape you see. While you're standing still in front of the window, you see a part of the landscape, but as soon as you start moving you see other parts of it:

![Example of the viewBox](https://github.com/okrad/viewbox-article/raw/master/img/landscape_viewbox_example.png?v=6)

I find another example to fit even better: the viewport can in fact be thought as your mobile phone camera. The viewBox is therefore the image you see inside the display, as it "captures" only part of the world in front of you. You can however zoom in and out or move the phone "framing" different parts of the landscape. These are actually some of the things you can do by acting on the viewBox and the viewport. -->

Inside an SVG document, all measures are unitless: you don't say "this rectangle is 10mm large", you just say "this rectangle is large 10".
In our first example, we are just saying that the circle has its center at (50, 50) (not 50 _px_ or _mm_) and that its radius is just "50" (and not 50 _px_). These dimensions are called _user units_.

The **viewport** can be set using two attributes on the SVG element: **width** and **height**. These represent the width and the height of the "display".
The viewport is specified using one of the units of measure that are known to the user agent: the browser in fact needs to know how much space to reserve in the DOM for the image.
When the viewport is specified in user units, the values are assumed to be in "px".

The **viewBox** controls which **part** of the image will be **shown** and at which **size**. It is defined using the _viewBox_ attribute on the root SVG element, and its value is a space (or comma) separated list of 4 numbers expressed in user units that describe a rectangle that will be mapped to the size of the viewport. The four numbers represent respectively:
* **min-x**: the horizontal coordinate of the upper left corner of the rectangle.
* **min-y**: the vertical coordinate of the upper left corner of the rectangle
* **width**: the width of the rectangle...
* **height**: you guessed it!

When not specified, the viewBox is assumed to correspond to the SVG canvas.

## Some examples ##

In the following example we are mapping the full SVG canvas to the viewport.
As you can see, the canvas size is 200 x 200, while the viewport size is 50px x 50px. Since the viewBox area has its top-left corner at (0,0), width and height both equal to 200 (which are the same sizes of the canvas), all the image is rendered inside the viewport:

![Mapping the full canvas to the viewport](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping1.png?v=4)

___

In the next example, the viewBox size is 100 x 100, which corresponds to half of the canvas size. Since the top-left corner of the viewBox is set to (0,0), the viewport will show only the top-left quarter of the image:

![Mapping part of the svg canvas to the viewport](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping2.png?v=4)

___

This time the viewBox rectangle has its top-left corner right in the middle of the canvas. The width and height of the viewBox are both 100... What we get to see in the viewport is therefore the bottom-right part of the svg:

![Mapping part of the svg canvas to the viewport](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping3.png?v=4)


## The preserveAspectRatio attribute ##

The examples seen till now all have one thing in common: the viewBox has the same aspect ratio of the viewport, i.e. the ratio between the viewBox width and height is the same ratio between the viewport width and height.

But what happens when the viewBox aspect ratio differs from that of the viewport? The **preserveAspectRatio** attribute controls just this behaviour. Its value is made up of two keywords: the _alignment value_ and the _meet or slice reference_.

The **alignment value** determines how the image should be scaled. It can take up to 10 values, the most straightforward of them being:

* **none**: in this case the image will be scaled **non uniformely** to completeley fit into the viewport without taking into account its aspect ratio.

![Scaling the viewBox non uniformely to fit into a viewport with different aspect ratio](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping4.png?v=5)

In all the following cases the image is instead **uniformely scaled**. These values determine where to position the scaled viewBox with respect to the viewport:

* **xMinYMin**: the min-x and min-y values of the viewBox (the first two numbers) are aligned to the upper left corner of the viewport.

* **xMidYMin**: the horizontal center of the viewBox is aligned with the horizontal center of the viewport while the min-y of the viewBox is aligned with the top of the viewport.

* **xMaxYMin**: the min-x + width of the viewBox (i.e. the "right side") is aligned with the right of the viewport while the min-y of the viewBox is aligned with the top of the viewport.

* **xMinYMid**: the min-x of the viewBox is aligned with the left of the viewport while the vertical center of the vieBox is aligned with the vertical center of the viewport.

* **xMidYMid**: the horizontal and vertical centers of the viewBox are aligned with the center point of viewport.

* **xMaxYMid**: the min-x + width of the viewBox (the "right side") is aligned with the right of the viewport while the vertical center of the viewBox is aligned with the vertical center of the viewport.

* **xMinYMax**: the min-x of the viewBox is aligned with the left of the viewport while the min-y + height of the viewBox (the "bottom side") is aligned with the bottom of the viewport.

* **xMidYMax**: the horizontal center of the viewBox is aligned with the horizontal center of the viewport while the min-y + height of the viewBox (the "bottom side") is aligned with the bottom of the viewport.

* **xMaxYMax**: the min-x + width of the viewBox (the "right side") is aligned with the right of the viewport while the min-y + height of the viewBox (the "bottom side") is aligned with the bottom of the viewport.

The second keyword of the _preserveAspectRatio_ atttibute (the **meet or slice reference**) is optional and determines how the image should be scaled. It can be one of the two following values:

* **meet**: the default value. The image is scaled such that the **viewBox entirely fits** into the viewport while preserving its aspect ratio. This means that the viewBox could result smaller than the viewport, depending on their aspect ratios.

![Mapping viewBox to a viewport with different aspect ratio, keeping aspect ratio (meet)](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping_xymeet.png?v=4)

* **slice**: The image is scaled such that the **viewBox covers the viewport** while preserving its aspect ratio. This means that the viewBox could extend beyond the viewport, depending on their aspect ratios.

![Mapping viewBox to a viewport with different aspect ratio, keeping aspect ratio (slice)](https://github.com/okrad/viewbox-article/raw/master/img/viewbox_mapping_xyslice.png?v=4)

<!-- In the following example you can play along with those attributes and see them in action: -->

<!-- {% codesandbox ksj95 %} -->

## That's all for now! ##

Now that we introduced the base concepts we can go on and see how just by changing the numbers of the viewBox we can achieve different "tricks". In the next articles of this serie we are just going to see some of them!


### References ###
* [Mozilla article](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) on the viewBox
* [Understanding SVG Coordinate Systems and Transformations (Part 1) — The viewport, viewBox, and preserveAspectRatio](https://www.sarasoueidan.com/blog/svg-coordinate-systems/) by [Sara Soueidan](https://www.sarasoueidan.com) - Btw, you should definitely follow her!
* Cover photo by [Pine Watt](https://unsplash.com/photos/3_Xwxya43hE) on [Unsplash](https://unsplash.com)