Search documentation of Plushie

# Plushie.Animation.Tween (Plushie v0.7.2)

Copy Markdown View Source

SDK-side stateful interpolator for model-level animation.

Pure functions operating on structs. No processes, no state management beyond what lives in your app model. Use this for complex animations that need frame-by-frame control: canvas animations, physics simulations, custom interpolation logic.

For simple property animations (fades, slides, scales), prefer renderer-side transitions via `Plushie.Animation.Transition` which require zero model state and zero wire traffic.

## Example

    alias Plushie.Animation.Tween

    def init(_opts) do
      %{anim: Tween.new(from: 0.0, to: 1.0, duration: 300, easing: :ease_out)}
    end

    def subscribe(_model) do
      [Plushie.Subscription.on_animation_frame()]
    end

    def update(model, %SystemEvent{type: :animation_frame, value: ts}) do
      anim = model.anim |> Tween.start_once(ts) |> Tween.advance(ts)
      %{model | anim: anim}
    end

    def view(model) do
      opacity = Tween.value(model.anim)
      # use opacity in widget props
    end

## Easing

Uses `Plushie.Animation.Easing` for the full catalogue of 31 named curves plus cubic bezier. Pass easing as an atom:

    Tween.new(from: 0.0, to: 1.0, duration: 300, easing: :ease_out_bounce)

## Interruption

Change the target mid-animation with `redirect/2`. The animation smoothly continues from its current interpolated value:

    anim = Tween.redirect(model.anim, to: 0.0, at: timestamp)

## Spring mode

For physics-based animation on the SDK side:

    anim = Tween.spring(from: 0.0, to: 1.0, stiffness: 200, damping: 20)

## Summary

### Types

t()

### Functions

advance(anim, timestamp)

Advances the animation to the given timestamp.

finished?(tween)

Returns true if the animation has completed.

looping(from, to, duration_ms, opts \\ [])

Creates a looping tween that repeats forever with auto-reverse.

new(opts)

Creates a new timed animation.

redirect(anim, opts)

Redirects the animation to a new target, starting from the current interpolated value. Resets the timer.

running?(tween)

Returns true if the animation is actively running (started and not finished).

spring(opts)

Creates a spring-mode animation (SDK-side spring solver).

start(anim, timestamp)

Starts the animation at the given timestamp. Resets value to `from`.

start_once(anim, timestamp)

Starts the animation only if it hasn't been started yet.

value(tween)

Returns the current interpolated value.

## Types

# t()

    @type t() :: %Plushie.Animation.Tween{
      auto_reverse: boolean() | nil,
      delay: non_neg_integer() | nil,
      duration: pos_integer() | nil,
      easing: Plushie.Animation.Easing.t(),
      easing_fn: (float() -> float()) | nil,
      finished: boolean(),
      from: number(),
      last_timestamp: integer() | nil,
      repeat: pos_integer() | :forever | nil,
      spring_config: map() | nil,
      started_at: integer() | nil,
      to: number(),
      value: number() | nil
    }

## Functions

# advance(anim, timestamp)

    @spec advance(animation :: t(), timestamp :: integer()) :: t()

Advances the animation to the given timestamp.

Always returns an updated `%Tween{}` struct. Check `finished?/1` to detect completion.

If the animation hasn't been started, returns the struct unchanged.

# finished?(tween)

    @spec finished?(animation :: t()) :: boolean()

Returns true if the animation has completed.

# looping(from, to, duration_ms, opts \\ [])

    @spec looping(
      from :: number(),
      to :: number(),
      duration_ms :: pos_integer(),
      opts :: keyword()
    ) :: t()

Creates a looping tween that repeats forever with auto-reverse.

Convenience for common back-and-forth animations (pulsing, breathing, oscillating). Equivalent to calling `new/1` with `repeat: :forever` and `auto_reverse: true`.

## Example

    Tween.looping(0.0, 1.0, 500, easing: :ease_in_out)

# new(opts)

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

Creates a new timed animation.

## Required options

- `from:` - start value
- `to:` - end value
- `duration:` - duration in milliseconds

## Optional

- `easing:` - easing atom or `{:cubic_bezier, ...}`. Default: `:ease_in_out`
- `delay:` - delay before start in ms. Default: 0
- `repeat:` - repeat count or `:forever`
- `auto_reverse:` - reverse on each repeat cycle

## Example

    Tween.new(from: 0.0, to: 1.0, duration: 300, easing: :ease_out)

# redirect(anim, opts)

    @spec redirect(animation :: t(), opts :: keyword()) :: t()

Redirects the animation to a new target, starting from the current interpolated value. Resets the timer.

Use this for smooth interruption: the animation continues from where it is rather than jumping.

## Options

- `to:` - new target value (required)
- `at:` - current timestamp (required)
- `easing:` - optionally change easing
- `duration:` - optionally change duration

# running?(tween)

    @spec running?(animation :: t()) :: boolean()

Returns true if the animation is actively running (started and not finished).

# spring(opts)

    @spec spring(opts :: keyword()) :: t()

Creates a spring-mode animation (SDK-side spring solver).

## Required options

- `from:` - start value
- `to:` - end value

## Optional

- `stiffness:` - spring constant. Default: 100
- `damping:` - friction. Default: 10
- `mass:` - mass. Default: 1.0
- `velocity:` - initial velocity. Default: 0.0

## Example

    Tween.spring(from: 0.0, to: 1.0, stiffness: 200, damping: 20)

# start(anim, timestamp)

    @spec start(animation :: t(), timestamp :: integer()) :: t()

Starts the animation at the given timestamp. Resets value to `from`.

If already started, restarts from the beginning.

# start_once(anim, timestamp)

    @spec start_once(animation :: t(), timestamp :: integer()) :: t()

Starts the animation only if it hasn't been started yet.

Convenience for the common pattern of starting on the first frame.

# value(tween)

    @spec value(animation :: t()) :: number() | nil

Returns the current interpolated value.