SVG Drawing Animation notes

Tiger AbrodiTiger Abrodi
4 min read

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

  1. Make one dash = full shape length

  2. Hide it by offsetting it away

  3. 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>
);
0
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.