# React, SMIL + viewBox animation notes

# What is viewBox Animation?

**viewBox = your camera lens**

```xml
<svg viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="20" />
</svg>
```

The circle is always at coordinate (50,50) with radius 20. **The circle never moves.** But when you animate viewBox, you're changing what part of the coordinate space you can see.

# The Camera Analogy

Think of it like a camera filming a stage:

```go
Stage (infinite SVG canvas):
┌─────────────────────────────────┐
│     🎭        🎪        🎨      │
│                                 │
│  🎵     ⭐ (50,50)     🎯      │
│                                 │
│     🎸        🎺        🎹      │
└─────────────────────────────────┘
```

**viewBox="0 0 100 100"** = Camera shows top-left corner, 100×100 area:

```go
Camera view:
┌─────────────┐
│ 🎭   🎪     │
│             │
│ 🎵   ⭐     │  ← Star is visible at center
│             │
└─────────────┘
```

**viewBox="50 50 100 100"** = Camera moves to show different area:

```go
Camera view:
┌─────────────┐
│      ⭐  🎯 │  ← Same star, now at top-left!
│             │
│   🎺   🎹   │
│             │
└─────────────┘
```

**The star never moved** -&gt; we just changed our view of it!

# The React Problem

Here's the tricky part with React:

**What you want to happen:**

1. viewBox starts at "0 0 100 100"
    
2. User changes prop to "50 50 100 100"
    
3. viewBox smoothly animates from old → new
    

**What actually happens:**

1. React re-renders with new prop
    
2. SVG immediately gets `viewBox="50 50 100 100"`
    
3. animate element also gets `to="50 50 100 100"`
    
4. You're trying to animate from "50 50 100 100" to "50 50 100 100"
    
5. **No animation!**
    

# The Solution Breakdown

```javascript
function Viewbox({ viewbox, children }) {
  const previousViewbox = usePrevious(viewbox); // Remember old value
  const animateRef = useRef();

  useEffect(() => {
    if (previousViewbox) {
      // Skip first render
      animateRef.current?.beginElement(); // Start animation
    }
  }, [viewbox]);

  return (
    <svg viewBox={viewbox}>
      {" "}
      {/* React sets this immediately */}
      {children}
      <animate
        ref={animateRef}
        attributeName="viewBox"
        from={previousViewbox} // Old value
        to={viewbox} // New value
        dur="0.3s"
        begin="indefinite" // Don't auto-start
      />
    </svg>
  );
}
```

# Step-by-Step Animation Flow

**Initial state:**

* `viewbox` prop = "0 0 100 100"
    
* `previousViewbox` = undefined
    
* SVG shows: `viewBox="0 0 100 100"`
    

**User changes to "50 50 100 100":**

1. **React re-render:**
    
    * `viewbox` prop = "50 50 100 100"
        
    * `previousViewbox` = "0 0 100 100" (from usePrevious)
        
    * SVG immediately shows: `viewBox="50 50 100 100"` (React controlled)
        
2. **animate element gets:**
    
    ```xml
    <animate from="0 0 100 100" to="50 50 100 100" />
    ```
    
3. **useEffect runs:**
    
    ```tsx
    animateRef.current.beginElement(); // Start the animation
    ```
    
4. **SMIL animation takes over:**
    
    * Overrides the SVG's viewBox attribute
        
    * Smoothly interpolates: "0 0 100 100" → "50 50 100 100"
        
    * Takes 0.3 seconds
        
5. **Animation ends:**
    
    * SMIL stops controlling viewBox
        
    * SVG returns to React's value: "50 50 100 100"
        
    * Seamless handoff!
        

# The Magic of usePrevious

```javascript
function usePrevious(value) {
  const ref = useRef(value);

  useEffect(() => {
    ref.current = value; // Update AFTER render
  }, [value]);

  return ref.current; // Return OLD value during render
}
```

**Timeline:**

```go
Render 1: value="A" → usePrevious returns "A"
Render 2: value="B" → usePrevious returns "A" (useEffect hasn't run yet)
          useEffect runs → ref.current becomes "B"
Render 3: value="C" → usePrevious returns "B"
          useEffect runs → ref.current becomes "C"
```

# Visual Effects You Can Create

**Zoom in:**

```tsx
// From wide view to focused view
viewBox="0 0 200 200" → viewBox="75 75 50 50"
```

**Pan across:**

```tsx
// Slide the camera right
viewBox="0 0 100 100" → viewBox="50 0 100 100"
```

**Zoom out:**

```tsx
// From detail to overview
viewBox="40 40 20 20" → viewBox="0 0 100 100"
```

**Follow an object:**

```tsx
// Keep character centered as they move
character at (30,40) → viewBox="5 15 50 50"
character at (70,20) → viewBox="45 -5 50 50"
```

# Why This is Powerful

1. **Performance:** No DOM elements move -&gt; just the viewport
    
2. **Simplicity:** One attribute controls complex camera movements
    
3. **Flexibility:** Works with any SVG content
    
4. **Smoothness:** Native browser interpolation
