# `Plushie.Animation.Spring`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.6.0/lib/plushie/animation/spring.ex#L1)

Renderer-side physics-based spring descriptor.

Springs animate using a damped harmonic oscillator simulation.
Unlike timed transitions, springs have no fixed duration -- they
settle naturally based on stiffness, damping, and mass. This
makes them ideal for interactive animations where the target
changes frequently (drag, scroll, hover) because interruption
preserves velocity for smooth redirection.

## Usage

    # Custom parameters
    scale: spring(to: 1.05, stiffness: 200, damping: 20)

    # Named presets
    scale: spring(to: 1.05, preset: :bouncy)

    # Pipeline
    alias Plushie.Animation.Spring
    scale: Spring.new(to: 1.05) |> Spring.stiffness(200) |> Spring.damping(20)

    # Do-block
    scale: spring do
      to 1.05
      stiffness 200
      damping 20
    end

## Presets

- `:gentle` -- slow, smooth, no overshoot
- `:snappy` -- quick, minimal overshoot
- `:bouncy` -- quick with visible overshoot
- `:stiff` -- very quick, crisp stop
- `:molasses` -- slow, heavy, deliberate

## When to use springs vs transitions

Use **springs** when:
- The target changes frequently (interactive elements)
- You want natural-feeling motion with momentum
- Overshoot/bounce is desirable

Use **transitions** when:
- You need precise timing (fade exactly 300ms)
- You need specific easing curves
- The animation is fire-and-forget (not interactive)

# `preset`

```elixir
@type preset() :: :gentle | :bouncy | :stiff | :snappy | :molasses
```

# `t`

```elixir
@type t() :: %Plushie.Animation.Spring{
  damping: number(),
  from: term() | nil,
  mass: number(),
  on_complete: atom() | nil,
  stiffness: number(),
  to: term(),
  velocity: number()
}
```

# `damping`

```elixir
@spec damping(spring :: t(), damping :: number()) :: t()
```

Sets the spring damping (higher = less oscillation).

# `from`

```elixir
@spec from(spring :: t(), from :: term()) :: t()
```

Sets the explicit start value.

# `mass`

```elixir
@spec mass(spring :: t(), mass :: number()) :: t()
```

Sets the spring mass (higher = slower, heavier).

# `new`

```elixir
@spec new(opts :: keyword()) :: t()
```

Creates a new spring descriptor.

`to:` is required. Use `preset:` for named configurations or
set `stiffness:` and `damping:` directly.

    Spring.new(to: 1.05, preset: :bouncy)
    Spring.new(to: 1.05, stiffness: 200, damping: 20)

# `on_complete`

```elixir
@spec on_complete(spring :: t(), tag :: atom()) :: t()
```

Sets the completion event tag.

# `presets`

```elixir
@spec presets() :: %{required(preset()) =&gt; keyword()}
```

Returns the map of available spring presets.

# `stiffness`

```elixir
@spec stiffness(spring :: t(), stiffness :: number()) :: t()
```

Sets the spring stiffness (higher = faster, snappier).

# `to`

```elixir
@spec to(spring :: t(), to :: term()) :: t()
```

Sets the target value.

# `velocity`

```elixir
@spec velocity(spring :: t(), velocity :: number()) :: t()
```

Sets the initial velocity.

# `with_options`

```elixir
@spec with_options(spring :: t(), opts :: keyword()) :: t()
```

Applies keyword options to an existing spring.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
