Chapter 1. The Anatomy of an SVG

Scalable Vector Graphics are becoming increasingly popular as a means of serving images on the web. The format’s advantages can be deduced from its name:

  • SVG images are scalable, which in an age of increasingly varied viewport sizes is a huge boon to development. With SVG we have one graphic to rule them all that scales to all devices, and therefore can save us from subsequent HTTP requests. Even the newer CSS properties such as srcset and picture require different images to be cut for different viewports, but SVG avoids all of that extra work.
  • Vector (rather than raster) means that, because they are drawn with math, SVGs tend to have greater performance and smaller file sizes.

SVG is an XML file format, and we can use it to succinctly describe shapes, lines, and text while still offering a navigable DOM; this means it can be performant and accessible.

In this first chapter, we’ll lay the foundation for an understanding of what this DOM comprises, because we’ll be reaching within it in order to create complex animations. We’ll be going over some of the syntax within the SVG DOM so that you know exactly what you’re manipulating and can debug as needed. We won’t be doing a deep dive into everything that the SVG DOM has to offer, because it’s out of the scope of this book. If you’d like more backstory, SVG Essentials by J. David Eisenberg and SVG Colors, Patterns, and Gradients by Amelia Bellamy-Royds and Kurt Cagle, both from O’Reilly, are great resources.

SVG DOM Syntax

Consider Figure 1-1, and the code that produces it:

<svg x="0px" y="0px" width="450px" height="100px" viewBox="0 0 450 100">
 <rect x="10" y="5" fill="white" stroke="black" width="90" height="90"/>
 <circle fill="white" stroke="black" cx="170" cy="50" r="45"/>
 <polygon fill="white" stroke="black" points="279,5 294,35 328,40 303,62
  309,94 279,79 248,94 254,62 230,39 263,35"/>
 <line fill="none" stroke="black" x1="410" y1="95" x2="440" y2="6"/>
 <line fill="none" stroke="black" x1="360" y1="6" x2="360" y2="95"/>
</svg>
Figure 1-1. The result of the SVG code

Looking at the SVG structure, most of the markup may appear familiar to you. The syntax is easy to read because of the commonalities with HTML. In the root <svg>  element, we see a declaration of x and y values—both set to 0 here, for the points in the coordinate matrix that we’re starting at. The width and height are both designated, and you’ll see that they correspond to the last two values in the viewBox.

viewBox and preserveAspectRatio

The SVG viewBox is a very powerful attribute, as it allows the SVG canvas to truly be infinite, while controlling and refining the viewable space. The four parameters it takes as a value are as follows: x, y, width, and height. This space is not defined in pixels, but rather is a more malleable space that can be adjusted to many different scales. Think of this as mapping out shapes and drawings on a piece of graph paper (see Figure 1-2).

Figure 1-2. The SVG viewBox

We can define coordinates based on this system, and the system itself can be self-contained. We can then alter the size of this sheet of paper, and everything within it. If we were to designate half the width and height for the SVG, but retain the same viewBox, the result would be what is shown in Figure 1-3.

Figure 1-3. The result of the viewBox alteration

This is part of the reason why SVG is such a powerful tool for responsive development—it can adjust to multiple viewports very easily.

SVG also stores information outside the viewBox area. If we move a shape outside this space, we’ll see what’s shown in Figure 1-4.

Figure 1-4. The result of moving a shape outside of the viewBox space

The white area is what the viewer sees, while the white and gray area together hold the information that the SVG actually contains. This feature allows the SVG to be both scalable and easy to crop on the fly. This comes in very handy in responsive applications, particularly sprites.

There is one more aspect of viewBox you should be aware of, invisible in this example. Most SVGs you will see on the web won’t even specify it because the default, preserveAspectRatio="xMidYMid meet, is what most people will want more than 9 times out of 10. This forces the drawing area to adjust itself with uniform scaling.

There are several other options as well. The first parameter, xMidYMid, determines whether or not to uniformly scale the element, and which part of the viewport to scale from, in camel case (styled like this: camelCase). The default is to scale from the center, or Mid, but there are several other alignment options, such as xMinYMax. You may also designate none, in which case the aspect ratio at its default percentages will be ignored, and the element will be squashed or stretched to fill the available space.

The second parameter can be either meet or slice. meet will attempt to scale the graphic as much as possible to fit inside the containing viewBox, while keeping the aspect ratio consistent. This functionality is similar to background-size: contain in that the image will stay contained in the boundaries of the containing unit.

slice will allow the graphic within the viewBox to expand beyond what the user sees in the direction specified, while filling up the available area. You can think about it like background-size: cover in that the image will push beyond the boundaries of the containing unit to fill up the available user space.

Drawing Shapes

Within our SVG, we’ve defined five shapes. rect refers to a rectangle or square. The x and y values, just as with the SVG itself, are where the shape begins: in this case, its upper-left corner. The shape’s width and height use the same coordinate system:

<rect x="10" y="5" fill="white" stroke="black" width="90" height="90"/>

The fill and the stroke are designated here as white and black; if nothing was specified here, the fill would default to black and the stroke would be none (i.e., invisible).

circle refers to—you guessed it—a circle:

<circle fill="white" stroke="black" cx="170" cy="50" r="45"/>

cx is the point where the center of the circle lies on the x-axis, cy is the point where the center of the circle lies on the y-axis, and r is the radius. You can also use ellipse for oval shapes, the only difference being there are two radius values: rx and ry.

polygon passes an array of values in a space-separated list, defined by points:

<polygon fill="white" stroke="black" points="279,5 294,35 328,40 303,62 309,94
  279,79 248,94 254,62 230,39 263,35"/>

As you might assume, the first value refers to the x coordinate position, comma-separated from its matching y value to plot the points of this shape.

Lines are fairly straightforward:

<line fill="none" stroke="black" x1="410" y1="95" x2="440" y2="6"/>
<line fill="none" stroke="black" x1="360" y1="6" x2="360" y2="95"/>

The first point of a line is plotted at the x1 and y1 values, and the end of the line at the x2 and y2 values. I’ve shown two lines here so you can see that the syntax stays consistent whether the line is straight or diagonal. In terms of code, I didn’t want you looking at lines sideways.

Responsive SVG, Grouping, and Drawing Paths

Now let’s consider Figure 1-5 and the code that generates it:

<svg viewBox="0 0 218.8 87.1">
 <g fill="none" stroke="#000">
   <path d="M7.3 75L25.9 6.8s58.4-6.4 33.5 13-41.1 32.8-11.2 30.8h15.9v5.5s42.6
     18.8 0 20.6" />
   <path d="M133.1 58.2s12.7-69.2 24.4-47.5c0 0 4.1 8.6 9.5.9 0 0 5-10 10.4.9 0 
     0 12.2 32.6 13.6 43 0 0 39.8 5.4 15.8 15.4-13.2 5.5-53.8 
     13.1-77.4 5.9.1 0-51.9-15.4 3.7-18.6z" />
 </g>
</svg> 
Figure 1-5. The result of removing width and height definitions

The first thing to notice about this SVG is that we’ve removed the width and height definitions. You can declare these elsewhere (usually in the CSS, or on the <img> or <object> element you use to embed the SVG), which makes it very malleable, especially for responsive development.

Width and Height Overrides

It’s nice and easy to have CSS control all of the sizing and keep it in one place, but I sometimes leave the width and height in if I’m worried about the CSS not loading properly. If there’s no fallback for the width and height inline, the SVG will scale to the available space, which can look pretty ostentatious. For that reason, you may consider writing these values inline as well. The CSS will override the presentational attributes (but not inline styles).

The SVG can now scale in percentage or viewport units, and can even be affected by media queries. The one catch is that you must declare a viewBox in this instance: it is no longer optional. The default behavior of an SVG with width and height removed and a viewBox declared is to scale to the maximum parameters of the containing element, which may be the body, a div, or just about anything else.

The second thing I’d like to point out is the <g> element. g stands for group, and it’s a way to nest and assemble multiple elements together in the SVG DOM. You may also notice that rather than defining the fill and stroke on elements themselves, we’ve done so on the group, and you can see it applied across the descendants.

The last and very pertinent thing to note is the path syntax. The path begins with d, for data, and is always designated with the M or m (for moveTo) command as the first value. This establishes a new point. Unlike when creating a polygon/polyline, however the coordinates specified here are not always points on the final line.

Table 1-1 shows what each letter in a path means. Letters may be capital or lowercase. Capital letters specify an absolute coordinate, while lowercase establishes a relative coordinate.

Table 1-1. Path syntax
Letter Meaning Image, where applicable
M, m moveTo Start of the path
L, l lineTo
H, h Horizontal line drawn from current position
V, v Vertical line drawn from current position
Z, z Joins the end of a path to the most recent moveTo command End of the path
Curve commands    
C, c Cubic Bézier
S, s Reflecting cubic Bézier
Q, q Quadratic Bézier—where both sides share the same control point
T, t Command control point that’s been reflected
A,a Elliptical arc

Revisiting Figure 1-5 and its code, you can see the difference between the paths by noting which one has a z at the end of its path data.

Delving further into path data is out of the scope of this book, but there is a great interactive demo on how path syntax works on CodePen, courtesy of Sten Hougaard.

SVG on Export, Recommendations, and Optimization

You can absolutely create an SVG by hand, or create an SVG drawing with JavaScript with tools like D3. However, there are times when you may want to design and build an SVG in a graphics editor such as Adobe Illustrator (see Figure 1-6), Sketch, or Inkscape. Layers in the graphic will be exported as groups, complete with id values derived from the layer names. You may find, though, that upon export, your SVG has a lot of information that the code in the preceding examples does not:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 
   6.00 Build 0)  -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" 
   xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     width="218.8px" height="87.1px" viewBox="0 0 218.8 87.1" 
  enable-background=
    "new 0 0 218.8 87.1" xml:space="preserve">
  <g>
    <path fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" 
      d="M133.1,58.2c0,0,12.7-69.2,24.4-47.5c0,0,4.1,8.6,9.5,0.9
              c0,0,5-10,10.4,0.9c0,0,12.2,32.6,13.6,43c0,0,39.8,5.4,15.8,
      15.4c-13.2,5.5-53.8,13.1-77.4,5.9C129.5,76.8,77.5,61.4,133.1
      ,58.2z"/>
    <path fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" 
    d="M6.7,61.4c0,0-3.3-55.2,20.8-54.8s-7.2,18.1,4.1,29.9
            s8.6-31.2,32.1-15.8S86.7,41,77.2,61.8C70.4,76.8,76.8,79,37.9,
    79c-0.4,0-0.9,0.1-1.3,0.1C9,81,40.1,58.7,40.1,58.7" />
  </g>
</svg>

Here’s the earlier code again for comparison:

<svg viewBox="0 0 218.8 87.1">
 <g fill="none" stroke="#000">
   <path d="M7.3 75L25.9 6.8s58.4-6.4 33.5 13-41.1 32.8-11.2 30.8h15.9v5.5s42.6 
     18.8 0 20.6" />
   <path d="M133.1 58.2s12.7-69.2 24.4-47.5c0 0 4.1 8.6 9.5.9 0 0 5-10 10.4.9 0 
     0 12.2 32.6 13.6 43 0 0 39.8 5.4 15.8 15.4-13.2 5.5-53.8 13.1-77
       .4 5.9.1 0-51.9-15.4 3.7-18.6z" />
 </g>
</svg> 

You can see it’s much smaller: without proper optimization, you can easily bloat SVG code.

Some of this information is useful, and some we can do away with. The comment about Illustrator generating the code can certainly be removed. We also do not need the version or layer information, as the web will not use it and we’re trying to transmit as few bytes as possible.

If x and y are defined as 0 (usually the case), we can strip those out, too. The only case where we’d want to leave them in is if we’re working with a child SVG nested inside another SVG.

We can also strip away the XML definitions if we are using an SVG inline. I will recommend using inline SVGs for animations throughout this book because the support for animation is stronger and there are fewer gotchas. However, there are times when using an SVG as a background image works well for animation (you’ll see this in Chapters 3 and 4, when we talk about sprites). If you decide to use the SVG in an object or image, you should keep this XML markup because leaving it out can cause issues in older browsers:

xmlns="http://www.w3.org/2000/svg"

If you’re not sure whether to use it or not, it’s better to leave it in.

You can also optimize paths. Illustrator will export path data with unnecessary decimal places that can be removed, and may also export group markup that will clutter your code. These are only a few examples of the possibilities for compression.

Reduce Path Points

If you’re going to create a hand drawing, you can trace it, but past that point you should use Object → Path → Simplify. See Figure 1-7 for a shot of the Simplify dialog box. You will need to check the Preview box, as changes made at this stage can potentially ruin the image. The image quality will tend to degrade quickly as the curve precision is lowered, so 91% is usually the lowest you can get away with. The number of points removed at this level still reduces the file size dramatically.

Figure 1-7. With the Simplify dialog box in Illustrator, you can reduce the size of your files dramatically

This is also probably the quickest way to accomplish this type of reduction. A more labor-intensive way that I use for smaller, unnecessarily complex pieces is to redraw them with the Pen tool. Sometimes this is very little effort for a large payoff, but it really depends on the shape.

It may seem intimidating at first, but you can use the Pen tool to quickly make more complex areas, using the Pathfinder tool to merge them all together (see Figure 1-8). If it doesn’t look quite right, don’t fear! You can reduce the opacity a little (so that you can see what you’re trying to emulate in the shape underneath), then use the Direct Selection tool, (A in quick keys, or the white arrow on the toolbar) to drag the points of the shape around until you get a more refined result. It also never hurts to zoom in a bit to see fine details.

Figure 1-8. Draw shapes quickly and merge them together to create complex paths without a lot of path points

Optimization Tools

You don’t need to strip this information out by hand, though. There are many great tools for optimizing SVGs and they offer more ways to help trim your code, such as rounding and rewriting numbers, merging path data, removing unneeded groups, and more.

The following list includes some of the available open source tools. The ones that visually show the output tend to be the most useful, as you can see how optimization may change the result:

SVGOMG

Jake Archibald has created a really nice web-based GUI for the terminal-based SVGO (see below). This tool is the most robust and easy to work with, and includes many toggle optimization options. SVGOMG shows the relative visual output and the byte-saving comparison after optimization.

SVG Editor

Peter Collingridge’s SVG Editor is very similar to SVGOMG, with slightly fewer options. A nice feature is that you can edit the SVG right in another panel in case you need to adjust the output just slightly. It’s web-based, with a nice visual interface.

SVGO

SVGO is terminal-based, with no visual GUI; however, you can add one with SVGO-GUI (https://github.com/svg/svgo-gui). This requires a bit more setup but is a workflow boon if you’re more comfortable working in your terminal than popping in and out of the browser. The functionality powers SVGOMG as well.

Please be aware that you will need to change and adjust optimization settings depending on what you’re trying to achieve in your animation. Get comfortable with adjusting these options rather than settling for the defaults, as doing so will save you considerable time later. You may find that a very busy animation requires repeated optimizations while you’re developing; for this reason, I recommend leaving your graphics editor and optimization tool open while working with your code editor to make your workflow as seamless as possible.

Default Export Settings to Be Aware Of

Be mindful of some of the defaults when you’re exporting. The ones that I find myself checking and unchecking the most are:

  • Clean IDs—This will remove any carefully named layers you may have.
  • Collapse useless groups—You might have grouped them to animate them all together, or just to keep things organized.
  • Merge paths—Nine times out of 10 this one is OK, but sometimes merging a lot of paths keeps you from being able to move elements in the DOM around independently.
  • Prettify—This is only necessary when you need to continue working with the SVG code in an editor.

Get SVG Animations now with the O’Reilly learning platform.

O’Reilly members experience live online training, plus books, videos, and digital content from nearly 200 publishers.