home / skills / dylantarre / animation-principles / popmotion

This skill helps you implement Disney's 12 animation principles using Popmotion to craft fluid, expressive UI animations.

npx playbooks add skill dylantarre/animation-principles --skill popmotion

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.1 KB
---
name: popmotion
description: Use when implementing Disney's 12 animation principles with Popmotion's functional animation library
---

# Popmotion Animation Principles

Implement all 12 Disney animation principles using Popmotion's composable animation functions.

## 1. Squash and Stretch

```javascript
import { animate } from "popmotion";

animate({
  from: { scaleX: 1, scaleY: 1 },
  to: { scaleX: 1.2, scaleY: 0.8 },
  duration: 150,
  onUpdate: ({ scaleX, scaleY }) => {
    element.style.transform = `scaleX(${scaleX}) scaleY(${scaleY})`;
  }
});
```

## 2. Anticipation

```javascript
// Wind up then action
animate({
  from: 0,
  to: 10,
  duration: 200,
  onUpdate: v => element.style.transform = `translateY(${v}px) scaleY(0.9)`,
  onComplete: () => {
    animate({
      from: 10,
      to: -200,
      duration: 400,
      ease: easeOut,
      onUpdate: v => element.style.transform = `translateY(${v}px)`
    });
  }
});
```

## 3. Staging

```javascript
animate({
  from: 1,
  to: 0.6,
  onUpdate: v => bg.style.opacity = v
});
animate({
  from: 1,
  to: 1.1,
  onUpdate: v => hero.style.transform = `scale(${v})`
});
```

## 4. Straight Ahead / Pose to Pose

```javascript
import { keyframes } from "popmotion";

keyframes({
  values: [
    { x: 0, y: 0 },
    { x: 100, y: -50 },
    { x: 200, y: 0 },
    { x: 300, y: -30 }
  ],
  duration: 1000,
  onUpdate: ({ x, y }) => {
    element.style.transform = `translate(${x}px, ${y}px)`;
  }
});
```

## 5. Follow Through and Overlapping Action

```javascript
animate({ from: 0, to: 200, duration: 500,
  onUpdate: v => body.style.transform = `translateX(${v}px)` });

animate({ from: 0, to: 200, duration: 500, elapsed: -50, // delay
  onUpdate: v => hair.style.transform = `translateX(${v}px)` });

animate({ from: 0, to: 200, duration: 600, elapsed: -100,
  onUpdate: v => cape.style.transform = `translateX(${v}px)` });
```

## 6. Slow In and Slow Out

```javascript
import { animate, easeInOut, easeIn, easeOut } from "popmotion";

animate({
  from: 0,
  to: 300,
  duration: 600,
  ease: easeInOut,
  onUpdate: v => element.style.transform = `translateX(${v}px)`
});
```

## 7. Arc

```javascript
keyframes({
  values: [
    { x: 0, y: 0 },
    { x: 100, y: -100 },
    { x: 200, y: 0 }
  ],
  duration: 1000,
  ease: easeInOut,
  onUpdate: ({ x, y }) => {
    element.style.transform = `translate(${x}px, ${y}px)`;
  }
});
```

## 8. Secondary Action

```javascript
// Primary action triggers secondary
animate({
  from: 1, to: 1.1, duration: 200,
  onUpdate: v => button.style.transform = `scale(${v})`,
  onComplete: () => {
    animate({
      from: 0, to: 15, duration: 150,
      onUpdate: v => icon.style.transform = `rotate(${v}deg)`
    });
  }
});
```

## 9. Timing

```javascript
import { spring } from "popmotion";

// Fast
animate({ from: 0, to: 100, duration: 150 });

// Spring physics
spring({
  from: 0,
  to: 100,
  stiffness: 300,
  damping: 20,
  onUpdate: v => element.style.transform = `translateX(${v}px)`
});

// Slow
animate({ from: 0, to: 100, duration: 800, ease: easeOut });
```

## 10. Exaggeration

```javascript
spring({
  from: { scale: 1, rotate: 0 },
  to: { scale: 1.5, rotate: 720 },
  stiffness: 200,
  damping: 10, // low = overshoot
  onUpdate: ({ scale, rotate }) => {
    element.style.transform = `scale(${scale}) rotate(${rotate}deg)`;
  }
});
```

## 11. Solid Drawing

```javascript
animate({
  from: { rotateX: 0, rotateY: 0 },
  to: { rotateX: 45, rotateY: 30 },
  duration: 500,
  onUpdate: ({ rotateX, rotateY }) => {
    box.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
  }
});
```

## 12. Appeal

```javascript
animate({
  from: 1,
  to: 1.02,
  duration: 300,
  ease: easeOut,
  onUpdate: v => {
    card.style.transform = `scale(${v})`;
    card.style.boxShadow = `0 ${20*v}px 40px rgba(0,0,0,${0.2*v})`;
  }
});
```

## Key Popmotion Features

- `animate()` - Tween animations
- `spring()` - Physics-based spring
- `keyframes()` - Multi-step animations
- `decay()` - Momentum/inertia
- `easeIn`, `easeOut`, `easeInOut` - Easing functions
- Composable functions - mix and pipe
- Framework agnostic
- Powers Framer Motion under the hood

Overview

This skill demonstrates how to implement Disney’s 12 principles of animation using Popmotion’s functional animation primitives. It maps each principle to concrete Popmotion patterns (animate, spring, keyframes, etc.) so you can craft expressive, physics-aware UI and character motion. Examples are framework-agnostic and focused on practical, composable animation code.

How this skill works

Each principle is implemented with appropriate Popmotion APIs: tweens with animate(), physics with spring() and decay(), multi-step moves with keyframes(), and easing with built-in ease functions. The examples show how to wire onUpdate callbacks to DOM transforms, chain animations with onComplete, and offset timings for overlapping action. The skill emphasizes composability so you can mix springs, eases, and keyframes to achieve staging, arcs, follow-through, and appeal.

When to use it

  • Designing UI motion that follows classic animation principles (buttons, cards, microinteractions).
  • Animating characters or illustrations in web projects where squash, stretch, arcs, and follow-through matter.
  • Building physics-driven interactions that need natural timing and momentum.
  • Prototyping complex chained animations with clear pose-to-pose or straight-ahead approaches.
  • Implementing subtle secondary actions to reinforce primary user gestures.

Best practices

  • Use spring() for bouncy, physical motion and animate() with easing for precise timing control.
  • Chain small animations with onComplete or stagger start times (elapsed offsets) for overlapping action.
  • Keep transforms on the GPU (transform, scale, translate, rotate) and avoid expensive layout changes.
  • Favor subtle exaggeration and slow-in/slow-out to increase appeal without breaking usability.
  • Compose simple primitives (tweens, springs, keyframes) rather than large monolithic animations.

Example use cases

  • A button that squashes on press, then triggers a small rotating icon as a secondary action.
  • A hero element staged with background fade and a subtle scale-in to focus attention.
  • A character hop implemented with anticipation, arc motion, squash/stretch on landing, and follow-through for hair or cape.
  • A card lift that uses slow-in/slow-out plus shadow adjustments for increased appeal.
  • A list reordering animation using keyframes and staggered elapsed offsets to convey overlapping action.

FAQ

Can I use these patterns in React or Vue?

Yes. Popmotion is framework-agnostic; bind onUpdate to element refs in React or template refs in Vue and manage lifecycle with effects.

When should I prefer spring() over animate()?

Use spring() for natural, physics-like motion with overshoot and momentum. Use animate() for deterministic timing and precise control with eases.