Learn stroke-dasharray and stroke-dashoffset - SVG Asphalt 9 Preloader

January 29, 2022 - 7 min read

If you have ever used SVG you are most likely familiar with stroke-dashoffset and stroke-dasharray. If you have used SVG's before... well... this will be a nice crash course to not only get your feet wet with using SVG's but see how fun and easy it can be to create something cool. Let's cover some of the basics.

Drawing a square with SVG

<svg version="1.1" x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <path 
      id="square" 
      d="M 30 30, 170 30, 170 170, 30 170 Z" 
      fill="none" 
      stroke-width="6px" 
      stroke="#222"/>
</svg>

Result

Stroke Dasharray Example

What is stroke-dasharray?

The stroke-dasharray attribute is a presentation attribute defining the pattern of dashes and gaps used to paint the outline of the shape.

Note: As a presentation attribute, stroke-dasharray can be used as a CSS property.

Demonstrating stroke-dasharray

Setting stroke-dasharray to a single number will give it an equal dash and equal spacing.

<svg version="1.1" x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <path 
      id="square" 
      d="M 30 30, 170 30, 170 170, 30 170 Z" 
      fill="none" 
      stroke-width="6px"
      stroke-dasharray="10" 
      stroke="#222"/>
</svg>

Stroke Dasharray Example

If I add a second digital it will adjust the spacing. For example if I change it to stroke-dasharray="10 30"

<svg version="1.1" x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <path 
      id="square" 
      d="M 30 30, 170 30, 170 170, 30 170 Z" 
      fill="none" 
      stroke-width="6px"
      stroke-dasharray="10 30" 
      stroke="#222"/>
</svg>

Stroke Dasharray Example

What is stroke-dashoffset?

The stroke-dashoffset attribute is a presentation attribute defining an offset on the rendering of the associated dash array..

Note: As a presentation attribute stroke-dashoffset can be used as a CSS property.

Example using stroke-dashoffset

<svg version="1.1" x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <path 
      id="square" 
      d="M 30 30, 170 30, 170 170, 30 170 Z" 
      fill="none" 
      stroke-width="6px"
      stroke-dasharray="10 30"
      stroke-dashoffset="60" 
      stroke="#222"/>
</svg>

Without Stroke Dasharray ExampleWith dash-offset="60"Stroke Dasharray Example

Setting up our basic layout for the preloader

We start with two basic squares... we could create this another way with svg but with path it makes it easy to animate as we will see later.

<svg x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <path d="M 15 15, 185 15, 185 185, 15 185 Z" fill="#222"/>
  <path d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="6px" stroke="rgba(255,255,255,.2)"/>
</svg>

Result

Basic Setup

Next, we are going to add the 4 paths. Two for the light trail ( one to add a blur and the second to add some definition), one for the light and the other for the light radiant glow.

<svg x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <style>
    #bolt {
      stroke-dasharray: 45 515;
      stroke-dashoffset: 0;
    }
    #light {
      stroke-dasharray: 4 556;
      stroke-dashoffset: -42;
    }
    #radiant {
      stroke-dasharray: 10 550;
      stroke-dashoffset: -40;
    }
  </style>
  <filter id="blurLine">
      <feGaussianBlur in="SourceGraphic" stdDeviation="4" />
    </filter>
  <filter id="blurLight">
    <feGaussianBlur in="SourceGraphic" stdDeviation=".8" />
  </filter>
  <path d="M 15 15, 185 15, 185 185, 15 185 Z" fill="#222"/>
  <path d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="6px" stroke="rgba(255,255,255,.2)"/>
  <path id="bolt" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="4px" stroke="#FF0054" filter="url(#blurLine)"/>
  <path id="bolt" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="2px" stroke="#FF0054" />
  <path id="radiant" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="10px" stroke="#FF0054" filter="url(#blurLine)"/>
  <path id="light" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="4px" stroke="rgba(255,255,255,.8)" filter="url(#blurLight)"/>
</svg>

If you look at the code you will notice that I used style within the svg to target the ID's of the path. This works great if you save this as a .svg file later on. If you are using the SVG code inline I would recommend to ensure the ID's that you use are unique or use a class.

I also used the filter option within the svg. This is a great option for adding a simple gaussian blur. We won't get into it within this tutorial but there are many great things you can do with the svg filter element.

Breaking this done a little further you will notice the stroke-dasharray and stroke-dashoffset ... why did I use those numbers?

First you need to find the length of your stroke. To do this you can use same basic vanilla javascript

var myPath = document.getElementById("bolt");
var length = myPath.getTotalLength();
console.log("Result " + length);

//Result 560

Now that we have our length, the numbers might make a little more sense.

For the #bolt or light trail we use stroke-dasharray: 45 515;, this makes the stroke 45 in length and adds a space of 515

For the #light we use stroke-dasharray: 4 556; this makes the stroke 4 with a space of 556, but then we use offset so we can have it line up to the end of the light trail stroke-dashoffset: -42;. Similar with the radiant around the light.

Result

Basic Setup

Animating our preloader

To animate the preloader we are going to use @keyframes

<svg x="0px" y="0px" viewBox="0 0 200 200" xml:space="preserve">
  <style>
    #bolt {
      stroke-dasharray: 45 515;
      stroke-dashoffset: 0;
      animation: bolt 3s linear infinite reverse;
    }
    @keyframes bolt {
      to {
        stroke-dashoffset: 560;
      }
    }
    #light {
      stroke-dasharray: 4 556;
      stroke-dashoffset: -42;
      animation: light 3s linear infinite reverse;
    }
    @keyframes light {
      to {
        stroke-dashoffset: 518;
      }
    }
    
    #radiant {
      stroke-dasharray: 10 550;
      stroke-dashoffset: -40;
      animation: radiant 3s linear infinite reverse;
    }
    @keyframes radiant {
      to {
        stroke-dashoffset: 520;
      }
    }
  </style>
  <filter id="blurLine">
      <feGaussianBlur in="SourceGraphic" stdDeviation="4" />
    </filter>
  <filter id="blurLight">
    <feGaussianBlur in="SourceGraphic" stdDeviation=".8" />
  </filter>
  <path d="M 15 15, 185 15, 185 185, 15 185 Z" fill="#222"/>
  <path d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="6px" stroke="rgba(255,255,255,.2)"/>
  <path id="bolt" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="4px" stroke="#FF0054" filter="url(#blurLine)"/>
  <path id="bolt" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="2px" stroke="#FF0054" />
  <path id="radiant" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="10px" stroke="#FF0054" filter="url(#blurLine)"/>
  <path id="light" d="M 30 30, 170 30, 170 170, 30 170 Z" fill="none" stroke-width="4px" stroke="rgba(255,255,255,.8)" filter="url(#blurLight)"/>
</svg>

Using to inside the @keyframes is basically telling it to animate from 0% to 100%. The end result to make it go full circle is to make the stroke-dashoffset to equal our line length (560), minus the offset.

I also used linear so the animation is a consistent speed. infinite so that... well you guessed it... would be infinite, and lastly reverse so it would go in the direction I wanted it to go.

Result

Basic Setup

Extra: to rotate it use transform.

I added this inside the style tag

path {
  transform: rotate(45deg) scale(.8);
  transform-origin: center;
}

Basic Setup