Creating an Animated Compass Loader with HTML and CSS

Aarzoo IslamAarzoo Islam
4 min read

The world of web development constantly evolves with captivating animations and designs. One such fascinating project is creating an animated compass loader using HTML and CSS. This project is a part of the Day 52 challenge of the #100DaysOfCode. Below, you'll find a step-by-step guide to help you understand and implement this captivating loader.

Step 1: Setting Up the HTML Structure

Start by creating the basic HTML structure. This includes the doctype declaration, head section for meta information, and the body containing the SVG for the loader.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <!-- Ensures proper scaling and rendering on mobile devices -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Link to external stylesheet -->
    <link rel="stylesheet" href="style.css">
    <title>Animated Compass Loader</title>
</head>

<body>
    <!-- SVG element to create the animated compass loader -->
    <svg class="pl" viewBox="0 0 160 160" width="160px" height="160px" xmlns="http://www.w3.org/2000/svg">
        <defs>
            <!-- Linear gradient definition for masking -->
            <linearGradient id="grad" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0%" stop-color="#000"></stop>
                <stop offset="100%" stop-color="#fff"></stop>
            </linearGradient>
            <!-- Mask using the gradient -->
            <mask id="mask1">
                <rect x="0" y="0" width="160" height="160" fill="url(#grad)"></rect>
            </mask>
            <!-- Smaller mask for inner elements -->
            <mask id="mask2">
                <rect x="28" y="28" width="104" height="104" fill="url(#grad)"></rect>
            </mask>
        </defs>

        <!-- Outer rotating ring -->
        <g>
            <g class="pl__ring-rotate">
                <circle class="pl__ring-stroke" cx="80" cy="80" r="72" fill="none" stroke="hsl(223,90%,55%)"
                    stroke-width="16" stroke-dasharray="452.39 452.39" stroke-dashoffset="452" stroke-linecap="round"
                    transform="rotate(-45,80,80)"></circle>
            </g>
        </g>

        <!-- Inner rotating ring with mask -->
        <g mask="url(#mask1)">
            <g class="pl__ring-rotate">
                <circle class="pl__ring-stroke" cx="80" cy="80" r="72" fill="none" stroke="hsl(193,90%,55%)"
                    stroke-width="16" stroke-dasharray="452.39 452.39" stroke-dashoffset="452" stroke-linecap="round"
                    transform="rotate(-45,80,80)"></circle>
            </g>
        </g>

        <!-- Static ticks around the compass -->
        <g>
            <g stroke-width="4" stroke-dasharray="12 12" stroke-dashoffset="12" stroke-linecap="round"
                transform="translate(80,80)">
                <!-- Eight ticks at 45 degree intervals -->
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(-135,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(-90,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(-45,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(0,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(45,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(90,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(135,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,10%,90%)" points="0,2 0,14"
                    transform="rotate(180,0,0) translate(0,40)"></polyline>
            </g>
        </g>

        <!-- Inner rotating ticks with mask -->
        <g mask="url(#mask1)">
            <g stroke-width="4" stroke-dasharray="12 12" stroke-dashoffset="12" stroke-linecap="round"
                transform="translate(80,80)">
                <!-- Eight inner ticks at 45 degree intervals -->
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(-135,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(-90,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(-45,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(0,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(45,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(90,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(135,0,0) translate(0,40)"></polyline>
                <polyline class="pl__tick" stroke="hsl(223,90%,80%)" points="0,2 0,14"
                    transform="rotate(180,0,0) translate(0,40)"></polyline>
            </g>
        </g>

        <!-- Compass arrows -->
        <g>
            <g transform="translate(64,28)">
                <g class="pl__arrows" transform="rotate(45,16,52)">
                    <!-- Red arrow -->
                    <path fill="hsl(3,90%,55%)"
                        d="M17.998,1.506l13.892,43.594c.455,1.426-.56,2.899-1.998,2.899H2.108c-1.437,0-2.452-1.473-1.998-2.899L14.002,1.506c.64-2.008,3.356-2.008,3.996,0Z">
                    </path>
                    <!-- White arrow -->
                    <path fill="hsl(223,10%,90%)"
                        d="M14.009,102.499L.109,58.889c-.453-1.421,.559-2.889,1.991-2.889H29.899c1.433,0,2.444,1.468,1.991,2.889l-13.899,43.61c-.638,2.001-3.345,2.001-3.983,0Z">
                    </path>
                </g>
            </g>
        </g>

        <!-- Inner arrows with mask -->
        <g mask="url(#mask2)">
            <g transform="translate(64,28)">
                <g class="pl__arrows" transform="rotate(45,16,52)">
                    <!-- Pink arrow -->
                    <path fill="hsl(333,90%,55%)"
                        d="M17.998,1.506l13.892,43.594c.455,1.426-.56,2.899-1.998,2.899H2.108c-1.437,0-2.452-1.473-1.998-2.899L14.002,1.506c.64-2.008,3.356-2.008,3.996,0Z">
                    </path>
                    <!-- Light blue arrow -->
                    <path fill="hsl(223,90%,80%)"
                        d="M14.009,102.499L.109,58.889c-.453-1.421,.559-2.889,1.991-2.889H29.899c1.433,0,2.444,1.468,1.991,2.889l-13.899,43.61c-.638,2.001-3.345,2.001-3.983,0Z">
                    </path>
                </g>
            </g>
        </g>
    </svg>
</body>

</html>

Step 2: Styling the Loader with CSS

Next, style the loader using CSS. This includes setting up the animations and ensuring the loader appears centered on the screen.

/* Set body to use flexbox for centering content */
body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background-image: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
}

/* Style for the loader container */
.pl {
    display: block;
    width: 9.375em;
    /* 150px */
    height: 9.375em;
    /* 150px */
}

/* Animation settings for all animated elements */
.pl__arrows,
.pl__ring-rotate,
.pl__ring-stroke,
.pl__tick {
    animation-duration: 2s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

/* Arrow animation */
.pl__arrows {
    animation-name: arrows42;
    transform: rotate(45deg);
    transform-origin: 16px 52px;
}

/* Ring rotation and stroke transformations */
.pl__ring-rotate,
.pl__ring-stroke {
    transform-origin: 80px 80px;
}

.pl__ring-rotate {
    animation-name: ringRotate42;
}

.pl__ring-stroke {
    animation-name: ringStroke42;
    transform: rotate(-45deg);
}

/* Tick animation */
.pl__tick {
    animation-name: tick42;
}

/* Stagger the tick animation delays */
.pl__tick:nth-child(2) {
    animation-delay: -1.75s;
}

.pl__tick:nth-child(3) {
    animation-delay: -1.5s;
}

.pl__tick:nth-child(4) {
    animation-delay: -1.25s;
}

.pl__tick:nth-child(5) {
    animation-delay: -1s;
}

.pl__tick:nth-child(6) {
    animation-delay: -0.75s;
}

.pl__tick:nth-child(7) {
    animation-delay: -0.5s;
}

.pl__tick:nth-child(8) {
    animation-delay: -0.25s;
}

/* Arrow rotation animation */
@keyframes arrows42 {
    from {
        transform: rotate(45deg);
    }

    to {
        transform: rotate(405deg);
    }
}

/* Ring rotation animation */
@keyframes ringRotate42 {
    from {
        transform: rotate(0);
    }

    to {
        transform: rotate(720deg);
    }
}

/* Ring stroke animation */
@keyframes ringStroke42 {

    from,
    to {
        stroke-dashoffset: 452;
        transform: rotate(-45deg);
    }

    50% {
        stroke-dashoffset: 169.5;
        transform: rotate(-180deg);
    }
}

/* Tick animation */
@keyframes tick42 {

    from,
    3%,
    47%,
    to {
        stroke-dashoffset: -12;
    }

    14%,
    36% {
        stroke-dashoffset: 0;
    }
}

Step 3: Understanding the Key Components

The SVG element contains several groups (<g>) and shapes (<circle>, <polyline>, <path>) that form the compass loader. Each element is styled and animated using CSS.

  • Linear Gradients and Masks: Defined in the <defs> section to create masking effects.

  • Rotating Rings: Animated to rotate and change stroke properties.

  • Ticks: Staggered animations for dynamic effects.

  • Compass Arrows: Styled and animated for a complete compass look.

Conclusion

By following these steps, you can create a visually appealing animated compass loader. This project not only enhances your CSS and SVG skills but also adds a unique loader to your web projects. You can download the full source code and contact me for more such engaging projects.

Download the Full Source Code Here: Download Source Code
Contact Me: With Aarzoo

10
Subscribe to my newsletter

Read articles from Aarzoo Islam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aarzoo Islam
Aarzoo Islam

Founder of AroNus ๐Ÿš€ | Full-stack web developer ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป | Sharing web development tips and showcasing projects ๐Ÿ“‚ | Transforming ideas into digital realities ๐ŸŒŸ