SVG Drawing Animation notes


Core Concepts
Circumference
What it is: The distance around the outside of a circle Formula: circumference = 2 × π × radius
Example: Circle with r="6"
→ 2 × 3.14159 × 6 = 37.7px
stroke-dasharray vs stroke-dashoffset
dasharray = WHAT the pattern looks like (creates the dashes)
dashoffset = WHERE the pattern starts (shifts the pattern)
<!-- Creates pattern: [10px dash][10px gap][10px dash][10px gap]... -->
<line stroke-dasharray="10" />
<!-- Same pattern, shifted 5px along -->
<line stroke-dasharray="10" stroke-dashoffset="5" />
Drawing Animation Technique
The Basic Setup
Make one dash = full shape length
Hide it by offsetting it away
Animate offset back to 0 to reveal
<!-- Step 1: Calculate length -->
<circle r="6" />
<!-- circumference = 37.7 -->
<!-- Step 2: Setup -->
<circle r="6" stroke-dasharray="37.7" <!-- One dash="full" circle -->
stroke-dashoffset="37.7"
<!-- Hidden initially -->
/>
<!-- Step 3: Animate -->
<!-- Change stroke-dashoffset to 0 via CSS/JS --></circle
>
CSS Implementation
circle {
stroke-dasharray: 37.7;
stroke-dashoffset: 37.7;
transition: stroke-dashoffset 2s ease;
}
circle:hover {
stroke-dashoffset: 0; /* Draws the circle */
}
JavaScript Implementation
const circle = document.querySelector("circle");
const circumference = 2 * Math.PI * 6; // radius = 6
// Setup
circle.style.strokeDasharray = circumference;
circle.style.strokeDashoffset = circumference;
// Animate
circle.style.transition = "stroke-dashoffset 2s ease";
circle.style.strokeDashoffset = "0";
pathLength Attribute
What it does
Normalizes path length to any number you want instead of using actual SVG units
<!-- Without pathLength: need to calculate exact length -->
<path d="..." stroke-dasharray="12.847" />
<!-- With pathLength: use round numbers -->
<path d="..." pathLength="10" stroke-dasharray="10" />
Why it's useful
Before: Calculate actual path length (messy decimals)
After: Choose your own units (clean round numbers)
<!-- Real checkmark path might be 12.847 units long -->
<path d="M7.75 11.75L10 14.25L16.25 7.75" pathLength="10" />
<!-- Now you can use stroke-dasharray="10" instead of "12.847" -->
Common Patterns
Basic Circle Drawing
<circle
cx="12"
cy="12"
r="6"
stroke-dasharray="37.7"
stroke-dashoffset="37.7"
/>
Progress Circle (starts from top)
<circle
cx="12"
cy="12"
r="6"
stroke-dasharray="37.7"
stroke-dashoffset="37.7"
style="transform: rotate(-90deg); transform-origin: center;"
/>
Checkmark with pathLength
<path
d="M7.75 11.75L10 14.25L16.25 7.75"
pathLength="10"
stroke-dasharray="10"
stroke-dashoffset="10"
/>
Advanced Techniques
Partial Animation (Arc Effect)
// Don't animate all the way to 0
const circumference = 37.7;
const partialOffset = circumference * 0.25; // Show 75% of circle
circle.style.strokeDashoffset = partialOffset;
Timer Progress Animation
const percentageComplete = (startTime - timeLeft) / startTime;
const offset = circumference * (1 - percentageComplete);
circle.style.strokeDashoffset = offset;
Rotating Line/Hand
const rotationDegrees = percentageComplete * 360;
line.style.transform = `rotate(${rotationDegrees}deg)`;
CSS Attribute Selectors
/* Exact match */
button[aria-checked="true"] {
}
/* Attribute exists */
button[aria-checked] {
}
/* Starts with */
button[class^="btn-"] {
}
/* Ends with */
button[class$="-large"] {
}
/* Contains */
button[class*="nav"] {
}
CSS Custom Properties with Dynamic Values
JavaScript
element.style.setProperty("--rotation", "45deg");
// or
element.style.cssText = "--rotation: 45deg;";
// or in React
<div style={{ "--rotation": "45deg" }} />;
CSS
.element {
transform: rotate(var(--rotation));
transition: transform 1s ease;
}
SMIL Animation
Basic Syntax
<circle>
<animate
attributeName="stroke-dashoffset"
from="37.7"
to="0"
dur="2s"
fill="freeze"
/>
</circle>
With Mouse Events
<svg id="svg">
<circle>
<animate
attributeName="stroke-dashoffset"
to="0"
dur="1s"
fill="freeze"
begin="svg.mouseover"
/>
</circle>
</svg>
Common Gotchas
The "Dashed Dash" Problem
Issue: Can't animate dashed shapes using stroke-dasharray technique Why: stroke-dasharray is already used for the dash pattern Solution: Use SVG masks instead
Getting Path Length
// For paths
const length = path.getTotalLength();
// For circles
const length = 2 * Math.PI * radius;
// For any shape with pathLength attribute
// Just use the pathLength value
Smooth Transitions
/* Add transitions to make animations smooth */
.animated-path {
transition: stroke-dashoffset 1s linear;
}
Quick Reference Formulas
// Circle circumference
const circumference = 2 * Math.PI * radius;
// Percentage to degrees (for rotation)
const degrees = percentage * 360;
// Progress animation offset
const offset = circumference * (1 - percentageComplete);
// Partial reveal offset
const offset = circumference * percentageRemaining;
Example: Complete Timer Implementation
// React component
const [timeLeft, setTimeLeft] = useState(60);
const [startTime] = useState(60);
const circumference = 2 * Math.PI * 6; // radius = 6
const percentageComplete = (startTime - timeLeft) / startTime;
const circleOffset = circumference * (1 - percentageComplete);
const lineRotation = percentageComplete * 360;
return (
<svg viewBox="0 0 24 24">
{/* Background circle */}
<circle cx="12" cy="12" r="6" opacity="0.2" />
{/* Progress circle */}
<circle
cx="12"
cy="12"
r="6"
stroke-dasharray={circumference}
stroke-dashoffset={circleOffset}
style={{
transform: "rotate(-90deg)",
transformOrigin: "center",
transition: "stroke-dashoffset 1s linear",
}}
/>
{/* Rotating line */}
<line
x1="12"
y1="8"
x2="12"
y2="10"
style={{
transform: `rotate(${lineRotation}deg)`,
transformOrigin: "center",
transition: "transform 1s linear",
}}
/>
</svg>
);
Subscribe to my newsletter
Read articles from Tiger Abrodi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Tiger Abrodi
Tiger Abrodi
Just a guy who loves to write code and watch anime.