How to Create a Pencil Loader Animation with CSS
Step-by-Step Guide to Making a Pencil Loader Animation Using CSS
Introduction
In today's technological era, captivating animations are essential to engage users and enhance the user experience on websites. As part of Day 32 of the #100DaysOfCode Challenge, we're diving into the world of CSS animations by creating an animated pencil loader. This loader not only serves a practical purpose but also provides a visually appealing loading experience for users. Join me on this journey as we break down the HTML, CSS, and JavaScript code step by step to understand how this animated pencil loader works.
Step 1: Setting Up the HTML Structure
Let's start by defining the HTML structure for our animated pencil loader. We'll use an SVG element to draw the pencil and its components. Here's the basic HTML structure:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta tags for character set, compatibility, and viewport -->
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Link to external CSS file -->
<link rel="stylesheet" href="style.css">
<!-- Title of the webpage -->
<title>Animated Pencil Loader</title>
</head>
<body>
<!-- SVG markup for animated pencil loader -->
<svg xmlns="http://www.w3.org/2000/svg" height="200px" width="200px" viewBox="0 0 200 200" class="pencil">
<!-- Definitions for clipping path -->
<defs>
<clipPath id="pencil-eraser">
<rect height="30" width="30" ry="5" rx="5"></rect>
</clipPath>
</defs>
<!-- Main pencil stroke -->
<circle transform="rotate(-113,100,100)" stroke-linecap="round" stroke-dashoffset="439.82"
stroke-dasharray="439.82 439.82" stroke-width="2" stroke="currentColor" fill="none" r="70"
class="pencil__stroke"></circle>
<!-- Pencil body parts -->
<g transform="translate(100,100)" class="pencil__rotate">
<g fill="none">
<!-- First body part -->
<circle transform="rotate(-90)" stroke-dashoffset="402" stroke-dasharray="402.12 402.12"
stroke-width="30" stroke="hsl(223,90%,50%)" r="64" class="pencil__body1"></circle>
<!-- Second body part -->
<circle transform="rotate(-90)" stroke-dashoffset="465" stroke-dasharray="464.96 464.96"
stroke-width="10" stroke="hsl(223,90%,60%)" r="74" class="pencil__body2"></circle>
<!-- Third body part -->
<circle transform="rotate(-90)" stroke-dashoffset="339" stroke-dasharray="339.29 339.29"
stroke-width="10" stroke="hsl(223,90%,40%)" r="54" class="pencil__body3"></circle>
</g>
<!-- Pencil eraser -->
<g transform="rotate(-90) translate(49,0)" class="pencil__eraser">
<!-- Eraser design -->
<g class="pencil__eraser-skew">
<rect height="30" width="30" ry="5" rx="5" fill="hsl(223,90%,70%)"></rect>
<rect clip-path="url(#pencil-eraser)" height="30" width="5" fill="hsl(223,90%,60%)"></rect>
<rect height="20" width="30" fill="hsl(223,10%,90%)"></rect>
<rect height="20" width="15" fill="hsl(223,10%,70%)"></rect>
<rect height="20" width="5" fill="hsl(223,10%,80%)"></rect>
<rect height="2" width="30" y="6" fill="hsla(223,10%,10%,0.2)"></rect>
<rect height="2" width="30" y="13" fill="hsla(223,10%,10%,0.2)"></rect>
</g>
</g>
<!-- Pencil tip -->
<g transform="rotate(-90) translate(49,-30)" class="pencil__point">
<!-- Triangle shape for pencil tip -->
<polygon points="15 0,30 30,0 30" fill="hsl(33,90%,70%)"></polygon>
<!-- Smaller triangle shape for pencil tip -->
<polygon points="15 0,6 30,0 30" fill="hsl(33,90%,50%)"></polygon>
<!-- Details on pencil tip -->
<polygon points="15 0,20 10,10 10" fill="hsl(223,10%,10%)"></polygon>
</g>
</g>
</svg>
</body>
</html>
Step 2: Styling with CSS
Now, let's add styles to our loader using CSS. We'll create animations for different parts of the pencil to achieve a dynamic loading effect. Here's the CSS code:
/* Resetting default margin, padding, and box-sizing */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Body styles */
body {
/* Flexbox setup for centering content */
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background-image: linear-gradient(120deg, #b1cdfa 0%, #fbefc2 100%);
}
/* Styles for the pencil SVG */
.pencil {
display: block;
width: 10em;
height: 10em;
}
/* Animation properties for various pencil elements */
.pencil__body1,
.pencil__body2,
.pencil__body3,
.pencil__eraser,
.pencil__eraser-skew,
.pencil__point,
.pencil__rotate,
.pencil__stroke {
animation-duration: 3s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
/* Specific styles for each body part of the pencil */
.pencil__body1,
.pencil__body2,
.pencil__body3 {
transform: rotate(-90deg);
}
/* Animation names for each body part */
.pencil__body1 {
animation-name: pencilBody1;
}
.pencil__body2 {
animation-name: pencilBody2;
}
.pencil__body3 {
animation-name: pencilBody3;
}
/* Animation for the eraser part */
.pencil__eraser {
animation-name: pencilEraser;
transform: rotate(-90deg) translate(49px, 0);
}
/* Animation for skewing the eraser */
.pencil__eraser-skew {
animation-name: pencilEraserSkew;
animation-timing-function: ease-in-out;
}
/* Animation for the pencil point */
.pencil__point {
animation-name: pencilPoint;
transform: rotate(-90deg) translate(49px, -30px);
}
/* Animation for rotating the pencil */
.pencil__rotate {
animation-name: pencilRotate;
}
/* Animation for drawing the pencil stroke */
.pencil__stroke {
animation-name: pencilStroke;
transform: translate(100px, 100px) rotate(-113deg);
}
Step 3: Adding JavaScript Functionality
To make our loader interactive, we'll use JavaScript to toggle a menu and animate the counter. Here's the JavaScript code:
// Get elements from the DOM
const menu = document.querySelector(".menu");
const menuBtn = document.querySelector(".menu-btn");
const counters = document.querySelectorAll(".counter");
// Toggle open/close menu
menuBtn.addEventListener("click", () => {
menu.classList.toggle("menu-open");
});
// Loop through all counters
counters.forEach((counter) => {
// Set initial counter value to zero
counter.innerText = 0;
// Initialize count variable to track the count
let count = 0;
// Function to update count
function updateCount() {
// Get the target number to count to
const target = parseInt(counter.dataset.count);
// Increment count by 10 if it's below the target number
if (count < target) {
count += 10;
// Display the count
counter.innerText = count + "+";
// Repeat every 10 milliseconds
setTimeout(updateCount, 10); /* Count Speed */
} else {
// Set the counter text to the target number when target is reached
counter.innerText = target + "+";
}
}
// Initialize count update
updateCount();
});
Step 4: Animating the Pencil
In this step, we'll define keyframe animations to bring our pencil loader to life. We'll create animations for the pencil stroke, eraser, and other components.
/* Keyframes for animations */
@keyframes pencilBody1 {
from,
to {
stroke-dashoffset: 351.86;
transform: rotate(-90deg);
}
50% {
stroke-dashoffset: 150.8;
/* 3/8 of diameter */
transform: rotate(-225deg);
}
}
@keyframes pencilBody2 {
from,
to {
stroke-dashoffset: 406.84;
transform: rotate(-90deg);
}
50% {
stroke-dashoffset: 174.36;
transform: rotate(-225deg);
}
}
@keyframes pencilBody3 {
from,
to {
stroke-dashoffset: 296.88;
transform: rotate(-90deg);
}
50% {
stroke-dashoffset: 127.23;
transform: rotate(-225deg);
}
}
@keyframes pencilEraser {
from,
to {
transform: rotate(-45deg) translate(49px, 0);
}
50% {
transform: rotate(0deg) translate(49px, 0);
}
}
@keyframes pencilEraserSkew {
from,
32.5%,
67.5%,
to {
transform: skewX(0);
}
35%,
65% {
transform: skewX(-4deg);
}
37.5%,
62.5% {
transform: skewX(8deg);
}
40%,
45%,
50%,
55%,
60% {
transform: skewX(-15deg);
}
42.5%,
47.5%,
52.5%,
57.5% {
transform: skewX(15deg);
}
}
@keyframes pencilPoint {
from,
to {
transform: rotate(-90deg) translate(49px, -30px);
}
50% {
transform: rotate(-225deg) translate(49px, -30px);
}
}
@keyframes pencilRotate {
from {
transform: translate(100px, 100px) rotate(0);
}
to {
transform: translate(100px, 100px) rotate(720deg);
}
}
@keyframes pencilStroke {
from {
stroke-dashoffset: 439.82;
transform: translate(100px, 100px) rotate(-113deg);
}
50% {
stroke-dashoffset: 164.93;
transform: translate(100px, 100px) rotate(-113deg);
}
75%,
to {
stroke-dashoffset: 439.82;
transform: translate(100px, 100px) rotate(112deg);
}
}
Step 5: Final Touches and Testing
With our HTML, CSS, and JavaScript in place, it's time for the final touches. We'll test our animated pencil loader to ensure everything works smoothly across different browsers and devices.
Download the full source code
Download the full source code from here and feel free to reach out to me on Bento for any questions or feedback. Happy coding!
Conclusion
Congratulations! You've successfully mastered CSS animation by creating an animated pencil loader from scratch. With your newfound skills, you can now create captivating animations to enhance the user experience on your websites. Keep experimenting and exploring new techniques to level up your coding journey.