DEV Community

Cover image for Running flow
Phuoc Nguyen
Phuoc Nguyen

Posted on • Originally published at phuoc.ng

Running flow

Adding animations to your website or app can be a fun and engaging way to spruce up the visuals. One type of animation that can really catch the eye is the running flow animation. It gives the impression that an arrow or other object is smoothly flowing or moving across the screen. And the best part? You can create this effect in CSS!

In this tutorial, I'll show you different ways to create a cool running flow animation using CSS. Our layout will feature two steps on the left and right sides, connected by an arrow in the middle. But don't worry, you can always add more steps and connectors to fit your needs. Let's get started!

Using SVG animation

For our first approach, we'll use an SVG element as the connector. The SVG only contains a single line element, as you can see below:

<svg class="connector" width="120" height="2" viewBox="0 0 120 2" fill="none">
    <line
        class="connector__line"
        y1="1"
        x2="120"
        y2="1"
        stroke-width="2"
    ></line>
</svg>
Enter fullscreen mode Exit fullscreen mode

To create a line using the smaller dashes, we can use the stroke-dasharray attribute directly for the line, or use the same CSS property. In this example, I'll be using the latter approach.

.connector__line {
    stroke-dasharray: 10 5;
}
Enter fullscreen mode Exit fullscreen mode

Don't worry if you're not familiar with the stroke-dasharray property. It's actually pretty easy to understand. In this example, we've set stroke-dasharray to an array of values that determine the length of each dash (10 pixels) and the space between them (5 pixels).

Now it's time to add some animation to our connector. This will make it appear to be flowing or moving across the steps. To achieve this effect, we can take advantage of the stroke-dashoffset property. This property determines the distance between the start of the dash array and the beginning of the stroke. By animating this property over time, we can create the illusion of movement, as if our arrow is flowing across the steps.

To begin, we can set stroke-dashoffset to be equal to the total length of our SVG line (in this case, 120 pixels), so that it's invisible at first. Then, in our animation, we can gradually decrease stroke-dashoffset until it reaches 0. This will reveal our entire line and give us a smooth flowing effect.

Here's an example CSS code block that implements this animation:

.connector__line {
    stroke-dashoffset: 120;
}
@keyframes flow {
    from {
        stroke-dashoffset: 120;
    }
    to {
        stroke-dashoffset: 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, we define an animation called flow that runs for five second (5s) with a linear timing function. To make sure the connector flows indefinitely, we've set the animation to run infinitely.

.connector__line {
    animation: flow 5s linear infinite;
}
Enter fullscreen mode Exit fullscreen mode

Good to know

You can't use the word running to name an animation. If you declare animation: running 5s linear infinite; in the animation shorthand, running will be interpreted as the playing state, not the animation name.

If you want to make the connector flow in the opposite direction (from right to left), simply begin the animation with a negative value for the stroke-dashoffset property.

@keyframes flow {
    from {
        stroke-dashoffset: -120;
    }
    to {
        stroke-dashoffset: 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's check out this stunning animation together!

Using a mask

The idea behind this approach is to use an SVG arrow as a background and repeat it to create a connector.

.connector {
    background: url("/path/to/arrow.svg") repeat;
}
Enter fullscreen mode Exit fullscreen mode

While trying to adjust the background position to flow between the steps, I encountered some trouble. Luckily, CSS has a powerful technique called masking that can help.

CSS masking allows you to hide or reveal parts of an element using another element as a mask. This technique can create interesting effects such as cut-out shapes and gradient fades. The mask's transparent areas will show the content of the masked element, while opaque areas will hide it.

To try it out, let's replace the background property with the mask. Keep in mind that we need to add a browser prefix to ensure it works on Chrome.

.connector {
    -webkit-mask: url("/path/to/arrow.svg");
    mask: url("/path/to/arrow.svg");
}
Enter fullscreen mode Exit fullscreen mode

The arrow SVG used in this approach is simply a polygon tag that creates the arrow shape.

<svg xmlns="http://www.w3.org/2000/svg" width="18" height="16">
    <polygon points="0 0 0 16 16 8"></polygon>
</svg>
Enter fullscreen mode Exit fullscreen mode

Since the SVG is missing a background, we must assign a background color to the connector. This will fill our mask.

.connector {
    background: rgb(226 232 240);
}
Enter fullscreen mode Exit fullscreen mode

Now, we're going to use the mask-position property to add some animation to our mask position. This technique is similar to the previous one where we used an SVG, but instead of animating the stroke-dashoffset property, we'll be animating the position of the mask.

To achieve this, we'll define an animation that gradually moves our arrow from left to right, across our steps.

:root {
    --arrow-width: 1.125rem;
}
.connector {
    animation: flow 5s linear infinite;
}

@keyframes flow {
    from {
        -webkit-mask-position-x: calc(0% - var(--arrow-width));
    }

    to {
        -webkit-mask-position-x: calc(100% + var(--arrow-width));
    }
}
Enter fullscreen mode Exit fullscreen mode

The variable --arrow-width tells us the width of an arrow. Specifically, it's set at 18 pixels, which is equivalent to 1.125rem.

The code above creates an animation named flow that lasts for five seconds, repeats indefinitely, and uses a linear timing function. We've used the calc() function to calculate the mask-position-x values. This ensures that the animation starts just outside of our first step and ends just outside of our second step.

It's time to see the results of all the steps we've taken so far.

If you want to prevent the browser from making a separate request to load your SVG, you can encode it and pass it directly to the url function. We've used this method for background properties in many cases.

Check out the sample code below to see how it's done.

.connector {
    mask: url("data:image/svg+xml;charset=utf8,%3Csvg width='18' height='16'%3E%3Cpolygon points='0 0 0 16 16 8'/%3E%3C/svg%3E%0A");
}
Enter fullscreen mode Exit fullscreen mode

It's highly recommended that you visit the original post to play with the interactive demos.

If you found this series helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

Top comments (0)