Building SVG Loader
Tags: svg, strokedashoffset, strokedasharray, negativestrokedashoffset
Published: 2022-03-22 03:00:43

In this post, I want to share how I built the Meesho loader using SVG as shown below. I have explained in a way even If you are complete beginner to SVG, you can expect to understand this implementation.


![SVG loader](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y00ngz7dtazztqaqlygv.gif)

**Overview**

The intend of developing this loader is to leverage the inbuilt SVG APIs in HTML5 and avoiding gif usage for simple animations. 

SVG can be scaled up without quality loss and comparatively very small in size. SVG can also work with CSS very well in order to achieve desirable shapes with animations. Javascript is not mandatorily required for SVG unlike Canvas and WebGL. SVG can be rendered before external script download and parse delay.

**Let us look at the code** 

**Step 1 - Set SVG dimensions**

```
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">
</svg>
```

Above block of code creates and allocates 300 X 300 space for SVG. We can consider this as a drawing board setup. By default SVG consider the unit in pixels. So It is 300px width and 300px height square here. 

**Step 2 - Draw a line**

Here I have used `<path/>` over `<line/>` element since there is requirement of lines with circular arc. 

Have given two instructions to `<path>` element to draw a line. Each instruction will work with or without comma separated. Have used comma here for better readability.

{% codepen https://codepen.io/manickumar/pen/xxpOpJm %}

`M 30, 250` - Move to 30px horizontally from left and 250px vertically from top of SVG block. 
`L 30 150` - Draw a line from current position to 30px from left, 150px from top.  Since the starting and end position of x values are same, It will be a straight line of length 100px (250 - 150)

**Step 3 - Draw an arc**

`A 50 50 0 0 1 130 150` - Draw an arc from current position to 130px from left and 150px from top with the radius of 50px. 

{% codepen https://codepen.io/manickumar/pen/qBpNxXd %}

**Step 4 - Draw a line again to complete the half shape**

`L 130 250` - Draw a line from end of arc to the position 130 px from left and 250 px from top. This will now show half of the shape that we are trying to draw. All we have to do now is just repeat.

{% codepen https://codepen.io/manickumar/pen/vYpKdrm %}

**Step 5 - Repeat the steps to complete full shape**

We have to draw one more arc and line to complete. 

`M 130 150` - Move again to 130px, 150px position from where we are going to draw an arc.

`A 50 50 0 0 1 230 150` - Draw another arc of same radius to the position of 230px from left and 150px from top.

`L 230 250` - Draw a line from current position to the position of 230px from left and 250px from top.

{% codepen https://codepen.io/manickumar/pen/rNpLJrX %}

**Step 6 - Adding blunt edge**

To make the edge blunt, Found `stroke-linecap: round` style exactly fits my requirement.

{% codepen https://codepen.io/manickumar/pen/XWVKZyW %}

**Step 7 - Add another track for animation**

For animation, We need another shape in SVG block with dark pink, same size and positions. It would be easy to just copy paste the path and only change the `stroke` property (`stroke` property helps to fill the path with given color code).

But We have a problem with using `move to` instruction more than once in the `<path>` element. When we have multiple `move to`, it breaks the path and When try animating it, We would see multiple tracks being animated by considering each `move to` as origin with in single `path` element. 

Since We have used Move to twice in current shape, That will cause an issue in animation If We take the same code.

```
<path 
  class='track'
  d="M 30 250,L 30 150,A 50 50 0 0 1 130 150, L 130 250,M 130 150,A 50 50 0 0 1 230 150,L 230 250" 
/>
```

I thought of overcoming this `move to` issue by starting the second curve right from the end of first curve. Which is 130px from left and 250px from top. So instead of Move to , We can draw a line from `130, 250 to 130, 150` and then draw an arc. This will not break the path and animation also will work fine. But this solution has issue with blunt edge. Since we are starting from end of curve one, The middle edge becomes not an edge, So that `stroke-linecap: round` does not apply to middle edge as shown below.

{% codepen https://codepen.io/manickumar/pen/VwyKmWv %}

I ended up solving this issue by splitting the curves into two. `Move to` also split the curve but If I split then, I will have control to animate in a way I wanted.

{% codepen https://codepen.io/manickumar/pen/ExogZZK %}

**Step 8 - Animate the path**

Now it's time to animate, The requirement of animation here is to fill the colour of path gradually from left to right and undoing it in same direction.

![SVG loader](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y00ngz7dtazztqaqlygv.gif)

We can divide this animation requirement in to two for better understanding and I will be referring this term instead of mentioning whole animation in every places, 

My goal was to only do animation by CSS properties. Since I am building a loader and that needs to be rendered as soon as possible after the HTML and CSS being downloaded and parsed. If Javascript used then I would have make sure it is not externally loaded and parsed also it should not slow down the main thread Since the animation type we are going to implement is infinite, 

***First part*** - sliding in filled path from left to right direction and change state hidden to visible

***Second part*** - sliding filled path in same direction but from visible state to hidden. 

As much as I have explored, I found `stroke-dashoffset` CSS property which fits my requirement. The default value of `stroke-dashoffset` is set to 0, based on the origin where We started our `path` element.

We can set initial offset value equal to total length of path we have, And that will move the path entirely to the left side because we have increased the offset same as length of path element.And that will hide the element from the view port. 

For animation, We can set the final offset value as 0. Which means element which is hidden will slowly come to the view and fully visible at the end. 

Also please note that, To animate using `stroke-dashoffset`, We have to use `stroke-dasharray` which helps to divide `path` into dashes. So we need to declare our curve here as single dash and play with the offset alone. Value of `dasharray` will be length of the curve for single dash.

{% codepen https://codepen.io/manickumar/pen/bGawgPX %}

```
animation: slide 2s infinite ease-in;

@keyframes slide {
  100%{
    stroke-dashoffset: 0;
  }
}
```

We have implemented the first part of implementation here with the help of `animation` property and `keyframes` in CSS. `keyframes` helps to modify `stroke-dashoffset` value at any stage of animation duration. And `animation` property consists

```
animation-name: slide 
animation-duration: 2s
animation-iteration-count: infinite
animation-timing-function: ease-in
```

Further tasks are, Second part of animation and both the curves are animating in same time, We need to arrange in a way that it is flowing.

For second part, We can use same logic . To change from hidden state to slide in visible state, We used offset value 1000 to 0. Like the way If we use 0 to -1000 then path can slide out visible to hidden state.

To avoid animating both the curve same time, We can add `animation-delay` to right cure just to align the flow so that It will look like single curve which is being animated from left to right.

{% codepen https://codepen.io/manickumar/pen/RwxGprv %}

**Step 8 - Browser compatibility**

Above code works perfectly fine for all chromium based browsers but Safari will not accept `stroke-dashoffset` propery value in negative. Which is always annoying as a developer to change your logic or way of implementation just for one browser is not compatible with it. But It is also mandatory to fix otherwise your solution will not go production. So went looking for alternate way of animating the same.

Before that, Just to add few points on why chrome supports negative offset value and safari does not. As per the W3C specifications, Negative values are not recommended for `stroke-dashoffest` to avoid confusion in moving forward and backward direction. But Chrome team might have thought it is not a confusion, but restriction. So they have enabled it. But Safari stick to the specs which is also not questionable. 

Coming back to the solutioning, I straight away could not find the alternate solution for second part of animation to replace negative offset.But after playing with offset and dasharray properties a lot, Arrived to this hacky way I should call it because there is no other proper solution I found.

We can achieve second part of animation by animating `stroke-dasharray` property from `1000 0` to `0 1000`. Which means from dash width 1000px and gap value 0 to dash width 0 and gap value 1000px. Here since dash value gradually reduces to 0, We can achieve second part of animation. 

So the idea here is to combine both the ways. Animating with `dashoffset` value for first part and with `dasharray` for second part.

{% codepen https://codepen.io/manickumar/pen/GRyjmoZ %}

Here, As soon as the first part of animation is done, I am resetting the value for second part instantly at 51%. Instant from 50 to 51 to avoid animation between `dasharray` value `1000` to `1000 0`.

Also I have increased `dashoffset` value by 100px to avoid flickering since we are animating both offset and `dasharray` properties.

**Conclusion**

Now that you know How I have built this loader. Please comment If there is any other way the same is possible. Doesn't matter whether it is better approach or not but curious to know.