Today let's work with CSS animations & clip-path to create a nice little download animation with an indefinite and a definite mode.
Read the full article or watch me code this on Youtube:
Result
Markup
The markup of this loader is quite simple as it consists of one div.downloader
for each loading animation and either the additional .indefinite
class or the .definite
class with a variable set to the percentage the loader should display.
.downloader.indefinite
.arrow
.bar
.downloader.definite(style="--percentage: 60%;")
.arrow
.bar
Variable Setup
The size of the loader is controller through the --size
CSS custom property - give it any value you want. The height of the bar and the space between the arrow and the bar is specified in percent whereas the size of the arrow is then derived through the remaining space.
The background color of the loader is given through --inactive-color
and the actual animation color is defined through --active-color
.
.downloader {
--size: 5rem;
--bar-height: 6%;
--bar-arrow-spacing: 10%;
--arrow-height: calc(100% - var(--bar-height) - var(--bar-arrow-spacing));
--inactive-color: rgba(255, 255, 255, 0.2);
--active-color: white;
width: var(--size);
}
The basic layout for the downloader is defined as a flex box operating in column mode:
.downloader {
...
display: flex;
flex-direction: column;
}
Creating the Arrow Shape
The arrow shape is created by using a clip-path
that crops the .arrow
element to have it's arrowy shape. So it's also easy to customize and you can give it almost every shape you want.
.downloader {
...
.arrow {
background-color: var(--inactive-color);
padding-top: var(--arrow-height);
position: relative;
clip-path: polygon(
47% 0%,
53% 0%,
53% 85%,
80% 56%,
80% 67%,
50% 100%,
20% 67%,
20% 56%,
47% 85%
);
}
}
In order to create the animation an :after
pseudo element is created inside the arrow, so it's also affected by the clip-path
and it will be running from top to bottom, which is why it's fixed to the top (see bottom: 100%;
) at the beginning.
.downloader {
...
.arrow {
...
&:after {
content: "";
position: absolute;
background-color: var(--active-color);
left: 0;
right: 0;
bottom: 100%;
top: 0;
}
}
}
Configuring the Indefinite Mode
So, for the indefinite mode the arrow's :after
element is given an animation, that makes it run from top to bottom:
.downloader {
...
&.indefinite {
.arrow:after {
animation: 2s loading-vertical ease-in-out infinite;
}
}
}
@keyframes loading-vertical {
0% {
top: 0%;
bottom: 100%;
}
30% {
top: 0%;
bottom: 100%;
}
50% {
top: 0%;
bottom: 0%;
}
70% {
top: 0%;
bottom: 0%;
}
100% {
top: 100%;
bottom: 0%;
}
}
And same goes for the bar
Now, for the bar we're going to do the same, except that its size and animation direction differs:
.downloader {
...
.bar {
position: relative;
width: 100%;
padding-top: var(--bar-height);
margin-top: var(--bar-arrow-spacing);
background-color: var(--inactive-color);
&:after {
content: "";
position: absolute;
background: var(--active-color);
left: 0;
right: 100%;
bottom: 0;
top: 0;
}
}
&.indefinite {
...
.bar:after {
animation: 2s loading-horizontal ease-in-out infinite;
}
}
}
...
@keyframes loading-horizontal {
0% {
left: 0%;
right: 100%;
}
30% {
left: 0%;
right: 100%;
}
50% {
left: 0%;
right: 0%;
}
70% {
left: 0%;
right: 0%;
}
100% {
left: 100%;
right: 0%;
}
}
Making the Definite Mode Work
Now, all that's required for the definite mode to work is the configuration of the .definite
class. The arrow will keep its animation and the .bar:after
's right property is calculated through the --percentage
custom property.
.downloader {
...
&.definite {
.arrow:after {
animation: 2s loading-vertical ease-in-out infinite;
}
.bar:after {
transition: 250ms right ease-in-out;
right: calc(100% - var(--percentage));
}
}
}