Skip to content

Animated

PythonNative's Animated API mirrors React Native's. Build performant animations declaratively by binding AnimatedValue instances to the style of an Animated.View, Animated.Text, or Animated.Image. The reconciler holds a ref to the underlying native view. Connected values, derived expressions, and style bindings form a graph the renderer core evaluates on the UI thread, so animations bypass Python reconciliation on each frame. Unattached values and values with Python listeners have the core report each frame's value to Python, and a callable easing function is sampled into a curve the core interpolates.

A few types frame the surface:

  • Easing is a namespace of serializable EasingSpec curves (Easing.ease_out, Easing.bezier(...)) accepted by Animated.timing(easing=...); a misspelled easing name raises ValueError instead of falling back.
  • await handle and handle.start(callback) deliver an AnimationResult whose finished flag tells a stopped or superseded animation from one that ran to completion.
  • ANIMATABLE_PROPS lists the style keys an animated node may drive; binding a layout key such as width raises ValueError at element construction.
  • Animated.decay uses React Native's model on every platform: velocity in points per millisecond decays as v0 * deceleration ** t, with deceleration defaulting to 0.998.

Animated values, derived animated nodes, and native-driven animation.

Modeled on React Native's Animated API with an async-aware completion contract. The core primitives are:

  • AnimatedValue: a numeric cell attached to native view properties; animations drive it over time.
  • Derived nodes: every animated node supports interpolate (range mapping with numeric, color, and angle outputs) and Python arithmetic (opacity * 0.5, x + y, -value), producing read-only AnimatedNode instances that update whenever their inputs change.
  • Animated.timing / Animated.spring / Animated.decay: animation factories. The objects they return implement __await__, so you can write await Animated.timing(v, to=1.0) to suspend until the animation finishes. Awaiting yields an AnimationResult whose finished flag is False when the animation was stopped or interrupted; handle.start(callback) passes the same value.
  • Animated.sequence / Animated.parallel / Animated.stagger / Animated.delay / Animated.loop: composition; also awaitable. A composite is finished only if every child finished.
  • Easing: serializable easing descriptors for Animated.timing (Easing.ease_in_out, Easing.bezier(0.2, 0.8, 0.2, 1.0), ...). Every renderer evaluates the same curve definitions; a plain Python callable is still accepted and crosses the bridge as a sampled curve.
  • Animated.event: build an event-prop callback that copies event fields into animated values (on_scroll=pn.Animated.event(y=v)).
  • Animated.diff_clamp: accumulate an input's deltas into a clamped range (the collapsing-header primitive).
  • Animated.View / Animated.Text / Animated.Image: components whose style may contain animated nodes, including inside transform entries.

Driver architecture:

Every animation runs in the renderer core's animator on the platform's UI thread (RFC 0007), at the display's refresh rate. Mounted bindings install a serialized graph of connected values, arithmetic, interpolation, and view properties; starting an animation hands its timing, spring, or decay specification to the core through the backend's start_animation hook (see BridgeBackend). The core evaluates the graph and applies its bindings without Python work on every frame. Completion settles the AnimatedValue and resolves awaiting tasks.

A value with Python listeners, or one no view is bound to, asks the core to report its value every frame, and Python updates the value and runs the listeners. A Python easing callable is sampled into a curve the core interpolates. Headless code without a mounted screen animates on the host build of the same core, so tests see the shipping math.

Scroll and gesture bindings feed graph values on the UI thread before input is queued to Python.

Example
import pythonnative as pn


@pn.component
def FadeIn():
    opacity = pn.use_animated_value(0.0)

    async def fade_in():
        await pn.Animated.timing(opacity, to=1.0, duration=400)
        await pn.Animated.timing(opacity, to=0.5, duration=200)

    pn.use_effect(fade_in, [])

    return pn.Animated.View(
        pn.Text("Hello!"),
        style={"opacity": opacity, "padding": 20},
    )

Classes:

Name Description
EasingSpec

A serializable easing curve for Animated.timing.

AnimatedNode

Base class for every animated node (settable leaves and derived nodes).

AnimatedValue

A numeric cell that can be attached to native view properties.

AnimatedInterpolation

Read-only node mapping a parent node through an input/output range.

AnimatedEvent

Callable event handler copying event fields into animated values.

AnimationResult

Outcome of an animation, delivered by await handle and handle.start(callback).

Functions:

Name Description
native_animation_message

Route an animation message from the core: a frame report or a completion.

use_animated_value

Return an AnimatedValue that is stable across renders.

Attributes:

Name Type Description
DECAY_REST_VELOCITY
DEFAULT_DECAY_DECELERATION
EASING_NAMES Tuple[str, ...]

The easing names Animated.timing accepts as strings.

Easing
ANIMATABLE_PROPS FrozenSet[str]

Style keys an AnimatedNode may be bound to.

Animated

DECAY_REST_VELOCITY module-attribute

DECAY_REST_VELOCITY = 0.001

DEFAULT_DECAY_DECELERATION module-attribute

DEFAULT_DECAY_DECELERATION = 0.998

EASING_NAMES module-attribute

EASING_NAMES: Tuple[str, ...] = tuple(
    _NAMED_EASING_FUNCTIONS
)

The easing names Animated.timing accepts as strings.

Easing module-attribute

Easing = _EasingNamespace()

EasingLike module-attribute

EasingLike = Union[
    EasingSpec, str, Callable[[float], float]
]

What Animated.timing(easing=...) accepts.

CompletionCallback module-attribute

CompletionCallback = Callable[[AnimationResult], Any]

Signature of the optional callback accepted by start().

ANIMATABLE_PROPS module-attribute

ANIMATABLE_PROPS: FrozenSet[str] = (
    frozenset({"opacity", "background_color", "color"})
    | _ANIMATED_TRANSFORM_KEYS
)

Style keys an AnimatedNode may be bound to.

These are the properties every renderer can drive on its UI thread without a layout pass. Layout keys (width, margin, padding, top, ...) and the remaining transform channels (skew_x, perspective, ...) are rejected at element construction time so an animation never silently degrades to a re-render per frame.

Animated module-attribute

Animated = _AnimatedNamespace()

EasingSpec dataclass

EasingSpec(
    name: str,
    points: Optional[
        Tuple[float, float, float, float]
    ] = None,
)

A serializable easing curve for Animated.timing.

Build instances through the Easing namespace (Easing.ease_in_out, Easing.bezier(...)) rather than directly. The descriptor crosses the bridge as data, so the renderer core can run the curve on the UI thread, and it evaluates identically in Python for tests.

Attributes:

Name Type Description
name str

One of the named curves ("linear", "ease", "ease_in", "ease_out", "ease_in_out", "quad", "cubic", "bounce") or "bezier".

points Optional[Tuple[float, float, float, float]]

The (x1, y1, x2, y2) control points when name is "bezier"; None otherwise.

Methods:

Name Description
evaluate

Return the eased value for progress in [0, 1].

to_wire

Return the bridge representation: the name, or [x1, y1, x2, y2] for a bezier.

name instance-attribute

name: str

points class-attribute instance-attribute

points: Optional[Tuple[float, float, float, float]] = None

evaluate

evaluate(progress: float) -> float

Return the eased value for progress in [0, 1].

to_wire

to_wire() -> Any

Return the bridge representation: the name, or [x1, y1, x2, y2] for a bezier.

AnimatedNode

AnimatedNode()

Base class for every animated node (settable leaves and derived nodes).

An animated node holds a current output value and a set of (tag, prop) attachments binding it to native view properties. Whenever the node's output changes (a leaf was set or animated, or an input of a derived node changed), the new value is pushed to every attachment through the registry's set_animated_property and to every Python-side listener, then propagated to derived nodes built from this one.

Derived nodes are constructed with interpolate, with Python arithmetic operators (+, -, *, /, %, unary -), or with Animated.diff_clamp. They are read-only: only AnimatedValue leaves can be set or animated directly.

Methods:

Name Description
attach

Bind this node to prop of the native view under tag.

attachments

Snapshot of the current (tag, prop) bindings.

add_listener

Register callback for Python-driven changes to this node.

has_listeners

Whether any Python-side listeners are registered.

interpolate

Map this node's value through an input/output range.

Attributes:

Name Type Description
value Any

Return the node's current output value.

value property

value: Any

Return the node's current output value.

attach

attach(
    tag: int, prop: str, backend: Any = None
) -> Callable[[], None]

Bind this node to prop of the native view under tag.

The current value is pushed immediately so the view reflects it even if no animation is running. backend is the backend that owns the view (the process's backend by default). Returns a detach callable.

attachments

attachments() -> List[Tuple[int, str]]

Snapshot of the current (tag, prop) bindings.

add_listener

add_listener(
    prop: str, callback: Callable[[Any], None]
) -> Callable[[], None]

Register callback for Python-driven changes to this node.

Returns an unsubscribe callable. prop is metadata only; it lets the subscriber differentiate this binding from others on the same node.

has_listeners

has_listeners() -> bool

Whether any Python-side listeners are registered.

interpolate

interpolate(
    input_range: Sequence[float],
    output_range: Sequence[Any],
    extrapolate: str = "extend",
    extrapolate_left: Optional[str] = None,
    extrapolate_right: Optional[str] = None,
) -> "AnimatedInterpolation"

Map this node's value through an input/output range.

Mirrors React Native's interpolate. output_range may contain numbers, colors ("#RRGGBB" / "#AARRGGBB"), or angle strings ("45deg" / "0.5rad", emitted as numeric degrees for the rotate transform).

Parameters:

Name Type Description Default
input_range Sequence[float]

Monotonically non-decreasing breakpoints for this node's value. At least two entries.

required
output_range Sequence[Any]

Output breakpoints, same length as input_range.

required
extrapolate str

Behavior outside the input range: "extend" (continue the edge segment's slope, default), "clamp" (pin to the edge output), or "identity" (return the input unchanged).

'extend'
extrapolate_left Optional[str]

Override extrapolate below the range.

None
extrapolate_right Optional[str]

Override extrapolate above the range.

None

Returns:

Type Description
'AnimatedInterpolation'

A derived, read-only animated node.

Example
header_height = scroll_y.interpolate(
    input_range=[0, 120],
    output_range=[160, 56],
    extrapolate="clamp",
)

AnimatedValue

AnimatedValue(initial: float = 0.0)

Bases: AnimatedNode

A numeric cell that can be attached to native view properties.

Animated components (Animated.View et al.) attach the value to (tag, prop) bindings after mount. Setting the value pushes the new number to every attached native view through the registry's set_animated_property (and through every derived node built from this value), and when an animation can be driven natively, the platform animates those same bindings directly.

Python-side listeners registered via add_listener observe every Python-driven change. Natively-driven animations intentionally skip per-frame Python callbacks (that's the point); listeners see the final settled value.

Methods:

Name Description
set_value

Set the value immediately, pushing to native views and listeners.

stop_animation

Cancel any in-flight animation on this value.

Attributes:

Name Type Description
value float

Return the current numeric value (without subscribing).

value property

value: float

Return the current numeric value (without subscribing).

set_value

set_value(new_value: float) -> None

Set the value immediately, pushing to native views and listeners.

stop_animation

stop_animation() -> None

Cancel any in-flight animation on this value.

AnimatedInterpolation

AnimatedInterpolation(
    parent: AnimatedNode,
    input_range: Sequence[float],
    output_range: Sequence[Any],
    extrapolate: str = "extend",
    extrapolate_left: Optional[str] = None,
    extrapolate_right: Optional[str] = None,
)

Bases: AnimatedNode

Read-only node mapping a parent node through an input/output range.

Built via AnimatedNode.interpolate; see that method for the semantics of the arguments.

Attributes:

Name Type Description
value Any

Return the interpolated output for the parent's current value.

value property

value: Any

Return the interpolated output for the parent's current value.

AnimatedEvent

AnimatedEvent(
    listener: Optional[Callable[..., None]] = None,
    **bindings: AnimatedValue
)

Callable event handler copying event fields into animated values.

Built via pn.Animated.event(...). Each keyword argument names a field on the incoming event payload (a dict key for scroll payloads such as {"x": ..., "y": ...}, or an attribute for GestureEvent instances) and maps it onto an AnimatedValue.

Because the result is an ordinary callable, it can be passed to any event prop:

scroll_y = pn.use_animated_value(0.0)
pn.ScrollView(..., on_scroll=pn.Animated.event(y=scroll_y))

tx = pn.use_animated_value(0.0)
gestures.Pan(on_change=pn.Animated.event(translation_x=tx))

AnimationResult dataclass

AnimationResult(finished: bool)

Outcome of an animation, delivered by await handle and handle.start(callback).

Mirrors the {finished} object React Native passes to animation callbacks.

Attributes:

Name Type Description
finished bool

True when the animation ran to completion. False when it was stopped with stop() or stop_animation(), superseded by another animation on the same value, or interrupted by the platform. Composite animations (sequence, parallel, stagger, loop) are finished only if every child finished.

finished instance-attribute

finished: bool

release_view_backend

release_view_backend(backend: Any, tags: Any) -> None

Forget that backend owns tags (its views were destroyed or its surface reset).

native_animation_message

native_animation_message(message: Dict[str, Any]) -> None

Route an animation message from the core: a frame report or a completion.

Completions carry {"id", "finished", "value"} (finished is False when the driver was cancelled or superseded); reports carry {"id", "value"}. Unknown ids are ignored (an animation cancelled moments before its completion arrived).

use_animated_value

use_animated_value(initial: float = 0.0) -> AnimatedValue

Return an AnimatedValue that is stable across renders.

Convenience wrapper for the common pattern pn.use_memo(lambda: AnimatedValue(initial), []). The same instance is returned on every render of the same component, so you can drive it from event handlers without recreating it.

Parameters:

Name Type Description Default
initial float

The starting numeric value.

0.0

Returns:

Type Description
AnimatedValue

A mount-stable AnimatedValue.

Example
import pythonnative as pn


@pn.component
def FadeIn():
    opacity = pn.use_animated_value(0.0)

    async def fade_in():
        await pn.Animated.timing(opacity, to=1.0, duration=300)

    pn.use_effect(fade_in, [])
    return pn.Animated.View(
        pn.Text("Hello"),
        style=pn.style(opacity=opacity),
    )

See also

  • The Animations guide walks through fade-ins, springs, sequences, easing, decay, and gesture-driven animations.
  • use_ref explains the ref semantics that back Animated.View.