# 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)
    

```html
<!-- 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**
    

```html
<!-- 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

```css
circle {
  stroke-dasharray: 37.7;
  stroke-dashoffset: 37.7;
  transition: stroke-dashoffset 2s ease;
}

circle:hover {
  stroke-dashoffset: 0; /* Draws the circle */
}
```

## JavaScript Implementation

```javascript
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

```html
<!-- 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)
    

```html
<!-- 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

```html
<circle
  cx="12"
  cy="12"
  r="6"
  stroke-dasharray="37.7"
  stroke-dashoffset="37.7"
/>
```

## Progress Circle (starts from top)

```html
<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

```html
<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)

```javascript
// 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

```javascript
const percentageComplete = (startTime - timeLeft) / startTime;
const offset = circumference * (1 - percentageComplete);
circle.style.strokeDashoffset = offset;
```

## Rotating Line/Hand

```javascript
const rotationDegrees = percentageComplete * 360;
line.style.transform = `rotate(${rotationDegrees}deg)`;
```

# CSS Attribute Selectors

```css
/* 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

```javascript
element.style.setProperty("--rotation", "45deg");
// or
element.style.cssText = "--rotation: 45deg;";
// or in React
<div style={{ "--rotation": "45deg" }} />;
```

## CSS

```css
.element {
  transform: rotate(var(--rotation));
  transition: transform 1s ease;
}
```

# SMIL Animation

## Basic Syntax

```html
<circle>
  <animate
    attributeName="stroke-dashoffset"
    from="37.7"
    to="0"
    dur="2s"
    fill="freeze"
  />
</circle>
```

## With Mouse Events

```html
<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

```javascript
// 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

```css
/* Add transitions to make animations smooth */
.animated-path {
  transition: stroke-dashoffset 1s linear;
}
```

# Quick Reference Formulas

```javascript
// 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

```jsx
// 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>
);
```
