AGS Logo AGS Logo

Animating Drawing SVG Shapes and Paths
Using stroke-dasharray & stroke-dashoffset

A Scalable Vector Graphic (SVG) is defined by the W3 as

"a language for describing two-dimensional graphics. As a standalone format or when mixed with other XML, it uses the XML syntax. SVG code used inside HTML documents uses the HTML syntax. SVG allows for three types of graphic objects: vector graphic shapes (e.g., paths consisting of straight lines and curves), images and text" (“Introduction — SVG 2”).

The cool thing about SVGs is that since they are comprised of vectors and allow for HTML syntax, we can animate parts (or all) of the pieces using CSS when they are being used on the web. What we are going to look at is isolating specific elements and animating them in a fashion that simulates the path being drawn. I specifically say simulate here because our animation will not actually build the path, but gradually render it visible which gives the illusion that it is being drawn. Figure 1 shows the output that we are going to generate.

Final Animation

Final Animation

The first thing we need to do is build our SVG. We can manually code one by hand or we can use a graphics program to generate one for us. There are many both free and paying options available on the market, a list comparing some of them is available on wikipedia. Which technique we choose will most often depend on the complexity of the image being created and our comfort with SVG code. Figure 2 shows our starter SVG.

Starter SVG

Starter SVG

If we open the svg file in a text editor, our code will look as follows:

SVG File
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
  <circle r="92" cx="204" cy="182" fill="none" stroke="black" />
  <path d="M102,25L204,182L4,182z" fill="none" stroke="black"/>
</svg>

We have 2 elements in our SVG: the circle, and the path which is the triangle. Similar to an HTML file, there are three ways we can add the CSS to animate the SVG:

  • Inline, much like the styles already set on circle
  • Adding a style tag inside of the SVG itself
  • If the SVG added to the HTML inline, any stylesheet attached to that HTML

We are going to put the SVG inline in our HTML and to keep all of our CSS together in a stylesheet. We are also going to move the styles currently applied inline on our elements and place them with our CSS file to allow for easier cascading and inheritance.

HTML with inline SVG
<div class="container">
  <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
    <circle r="92" cx="204" cy="182" />
    <path d="M102,25L204,182L4,182z" />
  </svg>
</div>
CSS File
circle, path { 
  fill: none; 
  stroke: black; 
}

Animating the circle

Let's start by animating the circle. The properties we are going to use to pretend to make it draw itself are stroke-dasharray and stroke-offset. Essentially we are going to create a dashed line, and offset where the dashes start. The line has 1 dash consisting of the length of our line, and we offset that by the length of our line. We then adjust the offset length over time to incrementally show the stroke.

The length of our line is the circumference of our circle. Conveniently, the information we need to compute this is available right there on the circle itself. The property r="92" that is set on our element tells us that the radius of our circle is equal to 92. We can therefore calculate the perimeter.

circumference = 2πr = 2 * 3.14 * 92 = 577.76

With our line length calculated we can now start building out our animation.

First we give our circle a dashed line with dashes and gaps both equal to the circumference of our circle using stroke-dasharray.

stroke-dasharray

The stroke-dasharray property takes any number of values and defines the pattern of dashes and gaps in our line (“stroke-dasharray - SVG: Scalable Vector Graphics | MDN”). For example stroke-dasharray: 20 5 60 40; would produce the following repeating pattern: dash of 20, gap of 5, dash of 60, gap of 40.

A red dashed line with the following the pattern described above

Dashed Line: stroke-dasharray: 20 5 60 40

If the property is only given one number such as stroke-dasharray: 20; all dashes and gaps will have a length of 20.

 difference between stroke-dasharray: 20 5 60 40 and stroke-dasharray: 20. The dashed line with stroke-dasharray: 20, has equal lized gaps and dashes, both with a length of 20

Dashed Line: stroke-dasharray: 20

If the property given for the dash is equal to the length of the line, the line will appear to be a solid line.

Making a Dashed Line Look Like a Solid Line

We will set our pattern for our dashes and empties to equal our circumference: stroke-dasharray: 577.76; So far our circle will look the same as before.

No visible change to the initial image

Added stroke-dasharray: 577.76

There is no visible change. We can now handle our offset.

stroke-dashoffset

The stroke-dashoffset property offsets the positon of the dashes by the value provided (“stroke-dasharray - SVG: Scalable Vector Graphics | MDN”). This properties allows us to move the position of the pattern. When thinking about stroke-dashoffset linearly, we see that when we have a line with 2 points on a coordiante plane, the first point x1 at (0, 265), the second (x2) at (500, 265), the offset is being applied at the end of the line. The dash is being pushed backwards and out of view.

Dashed Line: stroke-dashoffset: 50

Note: The above line examples can be found on codepen at: https://codepen.io/martine-dowden/pen/VwrEppd

We are going to use this property to move the dash out of view. Our dash is already the full perimenter of our circle, appearing to be a solid line. We then add an offset also equalling the full circumference of the circle, which will make the line completely disappear. Then we animate the dash back into view by incrementally decreasing the length of the gap down to 0. The smaller the value of the stroke-dasharray, the more complete the line will look.

Putting it all together in an animation

We add stroke-dasharray and stroke-dashoffset to the circle, both with the value of our circumference. Our dash is the entirety of the length of the line that forms our circle, then we offset it with a gap that is also the entirety of the length of our circle, which hides the dash.

Adding the Stroke Properties
circle {
  stroke-dasharray: 577.76;
  stroke-dashoffset: 577.76;
}

We now animate moving the dash back into view to simulate drawing the circle. To accomplish this task we use @keyframes to define our animation.

Draw Keyframe
@keyframes draw { 
  to { stroke-dashoffset: 0 }
}
circle {
  stroke-dasharray: 577.76 ;
	stroke-dashoffset: 577.76;
  animation: draw 3s linear infinite;
}

We do not need to define an initial state, since it is the same as our current state and explicitly defined on our element. Our final state is the absence of an offset.

Animated Circle

Animated Circle

Handling the Triangle

For the triangle we are going to use a very similar method, but we don't have a handy radius to tell us the perimeter of our triangle. We are going to have to break down the path code to calculate the length of each segment.

<path d="M102,25L204,182L4,182z" />

The code is not actually as convoluted as it looks once it is broken down into its individual parts because SVG paths behave not all too dissimilarly to an Etch A Sketch. They are a series of points on a coordinate plane to which a command is given to determine the action or type of line.

The origin (0, 0) of the coordinate plane is the top left of the SVG.Let's break down the path to identify our points.

Brokend Down Path
<path d="
  M102,25
  L204,182
  L4,182
  z" />
Table 1: Path Breakdown
SegmentCommandXY
M 102, 25M = Move To10225
L 204, 182L = Line To204182
L 4, 182L = Line To4180
ZZ = Close Path

(“d - SVG: Scalable Vector Graphics | MDN”)

We are telling the SVG to drop its pencil at (102, 25), draw a line from current position position to (204, 182), and another to (4, 180), and then close the shape by returning to (102, 25).

The three points of our triangle are at (102, 25), (204, 182), (4, 180).

Now that we have our points we can calculate the perimeter of our triangle. For this we will use the Euclidean distance formula (“Distance Formula Between Two Points | Wolfram Alpha”)

distance = √((x2 - x1)2 + (y2 - y1)2)

Table 2: Perimeter Calculation
Point 1Point 2Distance
(102, 25)(204, 182)~ 187
(204, 182)(4, 182)200
(4, 182)(102, 25)~ 185
TOTAL572

Our perimeter is therefore equal to 187 + 200 + 185 = 572. We can now give our triangle the same treatment we gave our circle. We can even reuse our animation and keyframe.

Final CSS
circle, path {
  fill: none;
  stroke: black;
  animation: draw 3s linear infinite;
}
@keyframes draw { 
  to { stroke-dashoffset: 0 }
}
circle {
  stroke-dasharray: 577.76 ;
  stroke-dashoffset: 577.76;
}
path {
  stroke-dasharray: 572 ;
  stroke-dashoffset: 572;
}

Final Animation

Final Animation

Closing Thoughts

Although not intuitive on the surface, we can effectively draw all sorts of shapes and paths. Demonstrated here on regular shapes, this technique does become challenging when we have irregular shapes—especially if they contain curves—as the computations to calculate the perimeter of the shape can become quite complex. That being said, I encourage everyone to give it a try, play with it, and have some fun. The code presented in this article can be found Code Pen at https://codepen.io/martine-dowden/pen/BamORZR.

Happy Coding!

References

License: CC BY-NC-ND 4.0 (Creative Commons)