npm package: @petit-kit/animate
Description: A lightweight, reactive web component framework with built-in plugins for modern web development.
Latest version: 0.0.23

# @petit-kit/animate

A lightweight animation library with time-based **lerp** and **spring** controllers for scalar, array, and object values. Includes **raf** (requestAnimationFrame loop) and simplified **runLerp** / **runSpring** fire-and-forget helpers.

- [Installation](#installation)
- [lerp](#lerp)
- [runLerp](#runlerp)
- [spring](#spring)
- [runSpring](#runspring)
- [raf](#raf)
- [curves](#curves)

## Installation

```bash
npm install @petit-kit/animate
# or
pnpm add @petit-kit/animate
```

## lerp

Time-based exponential smoothing. Interpolates toward a target using a per-frame factor (0–1) that is converted to time-based interpolation.

### Usage

```javascript
import { lerp } from '@petit-kit/animate';

// Scalar
const opacity = lerp({ from: 0, to: 1, lerp: 0.2 });
opacity.setTarget(1);
opacity.next(); // advance by real time (call each frame)

// Array
const position = lerp({ from: [0, 0], to: [1, 1], lerp: 0.15 });
position.setTarget([2, 3]);

// Object
const point = lerp({ from: { x: 0, y: 0 }, to: { x: 1, y: 1 } });
point.setTarget({ x: 2, y: 3 });
```

### Options

| Option           | Type      | Default | Description                                   |
| ---------------- | --------- | ------- | --------------------------------------------- |
| `from`           | `T`       | `0`     | Initial value                                 |
| `to`             | `T`       | `1`     | Initial target                                |
| `lerp`           | `number`  | `0.1`   | Interpolation intensity (0–1) per 60fps frame |
| `tolerance`      | `number`  | `0.001` | Distance threshold to consider "at rest"      |
| `resumeOnTarget` | `boolean` | `true`  | Resume when a new target is set after done    |

### API

| Method                          | Description                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `setTarget(nextTarget)`         | Set the target value. Resumes if done and `resumeOnTarget` is true                |
| `setValue(nextValue, options?)` | Set current value. Options: `resetTime`, `setTarget`, `markDone`                  |
| `getValue()`                    | Get current value (cloned)                                                        |
| `isDone()`                      | Whether the animation is at rest                                                  |
| `onResume(handler)`             | Subscribe to resume events. Returns unsubscribe                                   |
| `step(dt)`                      | Advance by `dt` seconds. Returns current value                                    |
| `next(now?)`                    | Advance by real elapsed time since last call. Uses `performance.now()` by default |

### Game loop example

```javascript
const pos = lerp({ from: [0, 0], to: [100, 100], lerp: 0.1 });

function tick() {
  const [x, y] = pos.next();
  element.style.transform = `translate(${x}px, ${y}px)`;
  requestAnimationFrame(tick);
}
tick();
```

### runLerp

Fire-and-forget lerp driven by RAF. Creates the controller internally and calls `onUpdate` each frame. Returns a stop function.

```javascript
import { runLerp } from '@petit-kit/animate';

const stop = runLerp({
  from: 0,
  to: 1,
  options: { lerp: 0.1, fps: 60 },
  onStart: () => console.log('start'),
  onUpdate: (v) => (el.style.opacity = v),
  onComplete: () => console.log('done'),
});
// later: stop();
```

| Option                   | Type                 | Default | Description                                         |
| ------------------------ | -------------------- | ------- | --------------------------------------------------- |
| `from`                   | `T`                  | `0`     | Initial value                                       |
| `to`                     | `T`                  | `1`     | Target value                                        |
| `options.lerp`           | `number`             | -       | Interpolation intensity (0–1) per 60fps frame       |
| `options.fps`            | `number`             | -       | Throttle RAF to target FPS                          |
| `options.tolerance`      | `number`             | -       | Distance threshold to consider "at rest"            |
| `options.delay`          | `number`             | -       | Seconds before animation starts                     |
| `options.resumeOnTarget` | `boolean`            | -       | Resume when new target is set after done            |
| `onStart`                | `() => void`         | -       | Called when animation starts                        |
| `onUpdate`               | `(value: T) => void` | -       | Called each frame (required)                        |
| `onComplete`             | `() => void`         | -       | Called when animation is done                       |
| `immediate`              | `boolean`            | `true`  | Call onUpdate with initial value before first frame |
| `stopWhenDone`           | `boolean`            | `true`  | Stop RAF loop when animation is done                |

---

## spring

Time-based spring physics. Uses mass, stiffness, and damping to animate toward a target with natural overshoot and settle.

### Usage

```javascript
import { spring } from '@petit-kit/animate';

// Scalar
const scale = spring({ from: 0.8, to: 1, stiffness: 200, damping: 18 });
scale.setTarget(1.2);

// Array
const position = spring({ from: [0, 0], to: [1, 1], stiffness: 160 });
position.setTarget([2, 3]);

// Object
const point = spring({ from: { x: 0, y: 0 }, to: { x: 1, y: 1 } });
point.setTarget({ x: 2, y: 3 });
```

### Options

| Option           | Type      | Default | Description                                               |
| ---------------- | --------- | ------- | --------------------------------------------------------- |
| `from`           | `T`       | `0`     | Initial value                                             |
| `to`             | `T`       | `1`     | Initial target                                            |
| `mass`           | `number`  | `1`     | Mass of the spring                                        |
| `stiffness`      | `number`  | `120`   | Spring stiffness (k)                                      |
| `damping`        | `number`  | `14`    | Damping coefficient (c)                                   |
| `velocity`       | `T`       | `0`     | Initial velocity                                          |
| `tolerance`      | `number`  | `0.001` | Velocity and displacement threshold to consider "at rest" |
| `resumeOnTarget` | `boolean` | `true`  | Resume when a new target is set after done                |

### API

| Method                          | Description                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `setTarget(nextTarget)`         | Set the target value. Resumes if done and `resumeOnTarget` is true                |
| `setValue(nextValue, options?)` | Set current value. Options: `resetVelocity`, `resetTime`, `setTarget`, `markDone` |
| `getValue()`                    | Get current value (cloned)                                                        |
| `isDone()`                      | Whether the spring is at rest                                                     |
| `onResume(handler)`             | Subscribe to resume events. Returns unsubscribe                                   |
| `step(dt)`                      | Advance physics by `dt` seconds. Returns current value                            |
| `next(now?)`                    | Advance by real elapsed time since last call. Uses `performance.now()` by default |

### Game loop example

```javascript
const scale = spring({ from: 0.8, to: 1, stiffness: 200, damping: 18 });

function tick() {
  const s = scale.next();
  element.style.transform = `scale(${s})`;
  requestAnimationFrame(tick);
}
tick();
```

### runSpring

Fire-and-forget spring driven by RAF. Creates the controller internally and calls `onUpdate` each frame. Returns a stop function.

```javascript
import { runSpring } from '@petit-kit/animate';

const stop = runSpring({
  from: 0.8,
  to: 1,
  options: { stiffness: 200, damping: 18, fps: 60 },
  onStart: () => console.log('start'),
  onUpdate: (v) => (el.style.transform = `scale(${v})`),
  onComplete: () => console.log('done'),
});
// later: stop();
```

| Option                   | Type                 | Default | Description                                           |
| ------------------------ | -------------------- | ------- | ----------------------------------------------------- |
| `from`                   | `T`                  | `0`     | Initial value                                         |
| `to`                     | `T`                  | `1`     | Target value                                          |
| `options.mass`           | `number`             | -       | Mass of the spring                                    |
| `options.stiffness`      | `number`             | -       | Spring stiffness (k)                                  |
| `options.damping`        | `number`             | -       | Damping coefficient (c)                               |
| `options.velocity`       | `T`                  | -       | Initial velocity                                      |
| `options.fps`            | `number`             | -       | Throttle RAF to target FPS                            |
| `options.delay`          | `number`             | -       | Seconds before animation starts                       |
| `options.tolerance`      | `number`             | -       | Velocity/displacement threshold to consider "at rest" |
| `options.resumeOnTarget` | `boolean`            | -       | Resume when new target is set after done              |
| `onStart`                | `() => void`         | -       | Called when animation starts                          |
| `onUpdate`               | `(value: T) => void` | -       | Called each frame (required)                          |
| `onComplete`             | `() => void`         | -       | Called when animation is done                         |
| `immediate`              | `boolean`            | `true`  | Call onUpdate with initial value before first frame   |
| `stopWhenDone`           | `boolean`            | `true`  | Stop RAF loop when animation is done                  |

---

## raf

RequestAnimationFrame loop with optional FPS throttling. Returns an unsubscribe function.

```javascript
import { raf } from '@petit-kit/animate';

const stop = raf((time, deltaTime) => {
  position += velocity * (deltaTime / 1000);
});
// later: stop();
```

```javascript
// Throttle to 30 FPS
raf((time, dt) => update(dt), 30);
```

---

## curves

Easing functions for `t` in `[0, 1]`. Use with keyframe or timeline-based animations.

```javascript
import { curves } from '@petit-kit/animate';

curves.linear(t);
curves.easeInQuad(t);
curves.easeOutQuad(t);
curves.easeInOutQuad(t);
curves.easeInCubic(t);
curves.easeOutCubic(t);
curves.easeInOutCubic(t);
// ... easeIn/Out/InOut for Quart, Quint, Expo, Circ, Back, Elastic, Bounce
curves.easeInSine(t);
curves.easeOutSine(t);
curves.easeInOutSine(t);
```