Motion¶
Worklets, shared values, animations, layout animations, and shared-element transitions (RFC 0009). Interaction logic runs on the UI thread inside the renderer core as worklets, Python functions in a subset the core compiles, verifies, and interprets, so motion keeps up with the finger while the Python thread is busy. The Motion guide explains the model and walks through every piece.
The everyday names are exported from pythonnative itself:
worklet,
SharedValue,
AnimatedStyle,
FrameInfo, the hooks, the with_*
animations, Easing, Spring, and
the helpers. The layout animation presets,
Keyframes,
SharedTransition,
LayoutAnimationConfig,
and WorkletError live in
pythonnative.motion:
import pythonnative as pn
from pythonnative.motion import FadeIn, FadeOut, LinearTransition, SharedTransition
The components built on Motion,
BottomSheet and
Swipeable, are documented with the other
components.
Worklets¶
worklet
¶
Mark fn as a worklet, so callbacks that accept one run it on the UI thread.
The function is unchanged: calling it from Python runs it as Python.
Event props and gesture callbacks run a worklet in the renderer core,
on the UI thread, instead of in Python. It's compiled the first time
it's installed, and a construct the UI thread can't run raises
WorkletError with its location.
is_worklet
¶
Whether value was marked with @worklet (or passed as an inline lambda to a worklet API).
WorkletError
¶
WorkletError(
message: str,
filename: str = "",
lineno: int = 0,
col: int = 0,
error_type: str = "",
)
Bases: Exception
A worklet uses Python the UI thread can't run, or failed while running there.
Attributes:
| Name | Type | Description |
|---|---|---|
message |
What went wrong. |
|
filename |
The worklet's source file. |
|
lineno |
The line, or 0 when unknown. |
|
col |
The column, or 0 when unknown. |
|
error_type |
For a runtime failure, the Python exception it raised
( |
Shared values¶
SharedValue
¶
SharedValue(initial: T, *, readonly: bool = False)
Bases: Generic[T]
A value the UI thread owns and Python can read, write, and animate.
Create one with use_shared_value.
In a worklet, .value reads and writes the UI thread's copy directly.
In Python:
- Reading
.valuereturns the latest value the UI thread published (it publishes after each frame and event, never waiting for a reader). A read right after a write from Python returns the written value until the UI thread has applied it. - Writing
.valuequeues the write behind pending mounts. Writing an animation (value = pn.with_spring(0)) starts it; writing a plain value cancels a running animation. await shared.animate(animation)starts an animation and returnsTrueonce it finishes, orFalseif something interrupted it. Canceling the awaiting task cancels the animation.
Reading .value during a render doesn't re-render when it changes;
drive the view with use_animated_style
instead.
Methods:
| Name | Description |
|---|---|
animate |
Run |
cancel |
Stop a running animation where it is. |
Attributes:
| Name | Type | Description |
|---|---|---|
value |
T
|
The latest value: the UI thread's, or a write from Python it hasn't applied yet. |
Hooks¶
Hooks that connect components to the motion runtime (RFC 0009).
Classes:
| Name | Description |
|---|---|
FrameInfo |
What a |
Functions:
| Name | Description |
|---|---|
use_shared_value |
A |
use_derived_value |
A read-only shared value the UI thread recomputes whenever a shared value it reads changes. |
use_animated_style |
A style the UI thread recomputes whenever a shared value it reads changes. |
use_animated_reaction |
Run |
use_frame_callback |
Run a worklet on the UI thread every display frame while |
FrameInfo
dataclass
¶
What a use_frame_callback worklet receives each frame.
Attributes:
| Name | Type | Description |
|---|---|---|
timestamp_ms |
float
|
The display frame's time, in milliseconds. |
delta_ms |
float
|
Milliseconds since the previous frame. |
use_shared_value
¶
use_shared_value(initial: T) -> SharedValue[T]
A SharedValue that lives as long as the component.
Its identity is stable across renders, like use_ref; the core
releases it when the component unmounts.
use_derived_value
¶
use_derived_value(
worklet: Callable[[], T],
) -> SharedValue[T]
use_animated_style
¶
use_animated_style(
worklet: Callable[[], Style],
) -> AnimatedStyle
A style the UI thread recomputes whenever a shared value it reads changes.
Put it in any built-in component's style list, after the static
styles it overrides. The worklet runs once when the view mounts, before
its first frame is drawn.
use_animated_reaction
¶
Run react(current, previous) on the UI thread whenever prepare()'s result changes.
prepare tracks the shared values it reads. react typically
writes another shared value or calls back with run_on_python.
use_frame_callback
¶
AnimatedStyle, what
use_animated_style returns, is documented with the other
style types.
Animations¶
Animation descriptors, easing curves, and the helpers worklets share with Python (RFC 0009).
with_timing, with_spring, with_decay, with_delay,
with_sequence, and with_repeat return frozen descriptors. Assign
one to a shared value (offset.value = pn.with_spring(0)) in a worklet or
in Python, or await it from Python (await offset.animate(...)), and the
renderer core runs it on the UI thread. Inside a worklet the same calls
build the same descriptors in the core.
interpolate, interpolate_color, clamp, and snap_point are
plain functions here and intrinsics in the core, so a worklet computes the
same result in both places.
Classes:
| Name | Description |
|---|---|
Spring |
Spring parameters for |
Animation |
Base class of the descriptors |
Functions:
| Name | Description |
|---|---|
with_timing |
Animate to |
with_spring |
Spring to |
with_decay |
Coast from the current value at |
with_delay |
Start |
with_sequence |
Run animations one after another, each from where the last ended. |
with_repeat |
Repeat |
Attributes:
| Name | Type | Description |
|---|---|---|
ReduceMotion |
How an animation treats the system's reduce-motion setting. |
ReduceMotion
module-attribute
¶
ReduceMotion = Literal['system', 'always', 'never']
How an animation treats the system's reduce-motion setting.
Spring
dataclass
¶
Spring(
damping: float = 10,
stiffness: float = 100,
mass: float = 1,
overshoot_clamping: bool = False,
rest_displacement: float = 0.01,
rest_speed: float = 2,
duration: Optional[float] = None,
damping_ratio: Optional[float] = None,
)
Spring parameters for with_spring and spring-driven layout animations.
Pass duration (milliseconds) and damping_ratio for a spring that
settles in about that time (damping_ratio=1 doesn't overshoot), or
the physical damping, stiffness, and mass.
Animation
¶
Base class of the descriptors with_* return.
with_timing
¶
with_timing(
to: T,
*,
duration: float = 300,
easing: Union[EasingSpec, str, None] = None,
reduce_motion: ReduceMotion = "system",
on_finish: Optional[Callable[[bool], None]] = None
) -> T
Animate to to over duration milliseconds along easing (ease-in-out by default).
to may be a number or a color. on_finish is a worklet that
receives whether the animation finished (False when interrupted).
The return type is to's type so shared.value = with_timing(1)
type-checks; the value is a descriptor the core runs.
with_spring
¶
with_spring(
to: T,
*,
damping: float = 10,
stiffness: float = 100,
mass: float = 1,
velocity: float = 0,
overshoot_clamping: bool = False,
rest_displacement: float = 0.01,
rest_speed: float = 2,
duration: Optional[float] = None,
damping_ratio: Optional[float] = None,
reduce_motion: ReduceMotion = "system",
on_finish: Optional[Callable[[bool], None]] = None
) -> T
Spring to to.
velocity is in units per second (a gesture's velocity_x carries
over directly). Give duration (milliseconds) with damping_ratio
for a spring that settles in about that time, or the physical
damping, stiffness, and mass. A spring that's still moving
when another animation replaces it hands over its velocity.
with_decay
¶
with_decay(
*,
velocity: float,
deceleration: float = 0.998,
clamp: Optional[Tuple[float, float]] = None,
rubber_band_factor: float = 0.6,
reduce_motion: ReduceMotion = "system",
on_finish: Optional[Callable[[bool], None]] = None
) -> Any
Coast from the current value at velocity (units per second), slowing by deceleration per millisecond.
With clamp=(lower, upper), the value springs back from a bound it
crosses when rubber_band_factor is above zero, or stops at it.
with_delay
¶
with_delay(
delay_ms: float,
animation: T,
*,
reduce_motion: ReduceMotion = "system"
) -> T
Start animation after delay_ms milliseconds.
Under reduced motion the delay is skipped; reduce_motion="never"
keeps it. The inner animation follows its own reduce_motion.
with_sequence
¶
Run animations one after another, each from where the last ended.
with_repeat
¶
with_repeat(
animation: T,
*,
times: Optional[int] = None,
reverse: bool = False,
on_finish: Optional[Callable[[bool], None]] = None,
reduce_motion: ReduceMotion = "system"
) -> T
Repeat animation times times, or forever when times is None.
With reverse, every other run heads back to where the first began.
Under reduced motion, a finite repeat jumps to where it would end and an
endless one stays put; reduce_motion="never" keeps it running.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Easing¶
Easing holds the curves with_timing and the layout animation
presets accept. Each is an EasingSpec,
which crosses to the core as data and evaluates the same way in Python.
A curve's name as a string ("ease_out") works wherever a curve does.
| Curve | Shape |
|---|---|
Easing.linear |
t |
Easing.ease, Easing.ease_in |
bezier(0.42, 0, 1, 1) |
Easing.ease_out |
bezier(0, 0, 0.58, 1) |
Easing.ease_in_out |
bezier(0.42, 0, 0.58, 1), the default |
Easing.quad |
t * t |
Easing.cubic |
t * t * t |
Easing.bounce |
Penner's bounce-out |
Easing.bezier(x1, y1, x2, y2) |
A CSS cubic Bézier; y values may overshoot |
Easing.custom(fn) |
A progress -> eased Python function, sampled at 64 points the core interpolates |
EasingSpec
dataclass
¶
A serializable easing curve.
Build one through Easing
(Easing.ease_in_out, Easing.bezier(...), Easing.custom(fn)).
It crosses to the core as data, and evaluates identically in Python.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
A named curve, |
points |
Optional[Tuple[float, ...]]
|
The control points of a bezier, or the samples of a custom curve. |
Methods:
| Name | Description |
|---|---|
evaluate |
The eased value for |
Helpers¶
These are plain functions in Python and intrinsics in the core, so a worklet computes the same result in both places.
interpolate
¶
interpolate(
x: float,
input_range: Sequence[float],
output_range: Sequence[float],
extrapolate: Extrapolate = "extend",
) -> float
Map x from input_range onto output_range piecewise linearly.
Outside the input range, "extend" continues the nearest segment,
"clamp" holds the end value, and "identity" returns x.
Pass (left, right) to choose each side separately.
interpolate_color
¶
Blend between colors as x moves through input_range (clamped at the ends).
snap_point
¶
The point a release at value moving at velocity (per second) lands nearest.
The release is projected a fifth of a second ahead, so a flick carries past the nearest point, the way a sheet or carousel settles.
run_on_python
¶
Call callback(*args) on the Python thread.
In a worklet, this queues the call from the UI thread; the callback must be a name the worklet captures. Called from Python, it calls the callback directly.
Extrapolate
module-attribute
¶
How interpolate behaves outside its input range: one mode, or (left, right).
Layout animations¶
Layout animations: views entering, exiting, and moving (RFC 0009).
Every view-like component takes entering=, exiting=, and
layout=:
enteringruns when the view is created. Inside a subtree created in the same commit, only the outermost entering animation runs, and views created along with a list row don't animate, so scrolling never triggers them.exitingruns when the view is removed. The component unmounts at once; the renderer holds the native view, animates it out, and then destroys it with its subtree. Only the outermost removed view animates.layoutanimates the view from its old frame to its new one when a commit moves or resizes it.
from pythonnative.motion import FadeIn, FadeOut, LinearTransition
pn.View(
*rows,
entering=FadeIn(duration=200),
exiting=FadeOut(duration=150),
layout=LinearTransition(spring=pn.Spring(damping_ratio=0.8, duration=350)),
)
FlatList and SectionList animate their rows natively with
item_entering, item_exiting, and item_layout, which take the
presets here (not keyframes or custom worklets).
Classes:
| Name | Description |
|---|---|
LayoutValues |
What a custom layout animation worklet receives. |
FadeIn |
Fade from transparent to the view's opacity. |
FadeOut |
Fade to transparent. |
ZoomIn |
Grow from nothing to full size. |
ZoomOut |
Shrink to nothing. |
SlideIn |
Slide in from beyond the window's |
SlideOut |
Slide out past the window's |
LinearTransition |
Move and resize from the old frame to the new one. |
FadingTransition |
Fade out at the old frame, then fade in at the new one. |
Keyframes |
Animate through styles at points from 0 to 1 of |
Functions:
| Name | Description |
|---|---|
LayoutAnimationConfig |
Turn off layout animations for a subtree. |
Attributes:
| Name | Type | Description |
|---|---|---|
LayoutAnimation |
What |
LayoutAnimation
module-attribute
¶
LayoutAnimation = Union[
FadeIn,
FadeOut,
ZoomIn,
ZoomOut,
SlideIn,
SlideOut,
LinearTransition,
FadingTransition,
Keyframes,
Callable[[LayoutValues], Mapping[str, Any]],
]
What entering, exiting, and layout accept.
A custom worklet receives LayoutValues
and returns {"initial": {...}, "animate": {...}}: the starting style,
and an animation or value per key. Frame keys are x, y, width,
and height.
LayoutValues
dataclass
¶
LayoutValues(
target_x: float = 0,
target_y: float = 0,
target_width: float = 0,
target_height: float = 0,
current_x: float = 0,
current_y: float = 0,
current_width: float = 0,
current_height: float = 0,
window_width: float = 0,
window_height: float = 0,
)
What a custom layout animation worklet receives.
target_* is where the view ends (entering and layout), current_*
where it is (exiting and layout), and window_* the window's size.
FadeIn
dataclass
¶
FadeIn(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Fade from transparent to the view's opacity.
FadeOut
dataclass
¶
FadeOut(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Fade to transparent.
ZoomIn
dataclass
¶
ZoomIn(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Grow from nothing to full size.
ZoomOut
dataclass
¶
ZoomOut(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Shrink to nothing.
SlideIn
dataclass
¶
SlideIn(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
edge: Edge = "right",
)
Bases: _Preset
Slide in from beyond the window's edge.
SlideOut
dataclass
¶
SlideOut(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
edge: Edge = "right",
)
Bases: _Preset
Slide out past the window's edge.
LinearTransition
dataclass
¶
LinearTransition(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Move and resize from the old frame to the new one.
FadingTransition
dataclass
¶
FadingTransition(
duration: float = 300,
delay: float = 0,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
reduce_motion: ReduceMotion = "system",
)
Bases: _Preset
Fade out at the old frame, then fade in at the new one.
Keyframes
dataclass
¶
Keyframes(
frames: Mapping[float, Style],
duration: float = 300,
delay: float = 0,
easings: Mapping[
float, Union[EasingSpec, str]
] = dict(),
reduce_motion: ReduceMotion = "system",
)
Animate through styles at points from 0 to 1 of duration.
Each key interpolates between the keyframes that set it. Numbers and
colors blend, and transform lists blend entry by entry when their
shapes match.
Example
Attributes:
| Name | Type | Description |
|---|---|---|
frames |
Mapping[float, Style]
|
Style at each point of progress. |
duration |
float
|
Total milliseconds. |
delay |
float
|
Milliseconds before it starts. |
easings |
Mapping[float, Union[EasingSpec, str]]
|
The curve leading into a keyframe, by its progress point. |
LayoutAnimationConfig
¶
LayoutAnimationConfig(
*children: Node,
skip_entering: bool = False,
skip_exiting: bool = False,
style: StyleProp = None,
key: Optional[str] = None
) -> Element
Turn off layout animations for a subtree.
skip_entering skips the entering animations of views mounted along
with this one, such as a list's first rows. skip_exiting removes
views inside it at once instead of animating them out. It renders a
View that takes style.
Shared-element transitions¶
Shared-element transitions between stack screens (RFC 0009).
Give a view on the screen you push from and a view on the screen you push
to the same shared_tag. During the push (and the pop back), the
platform lifts the destination view into a transition overlay at the
source's place on screen, hides the source, and flies the destination to
where it belongs while the screens slide, blending corner radius,
background color, and opacity. On iOS the back swipe drives the flight
interactively.
# The journal row
pn.Image(photo, shared_tag=f"photo-{entry.id}", style=styles.thumbnail)
# The entry screen
pn.Image(photo, shared_tag=f"photo-{entry.id}", style=styles.hero,
shared_transition=SharedTransition(spring=pn.Spring(damping_ratio=0.9, duration=450)))
Classes:
| Name | Description |
|---|---|
SharedTransition |
How a shared element flies between screens. |
SharedTransition
dataclass
¶
SharedTransition(
duration: Optional[float] = None,
easing: Union[EasingSpec, str, None] = None,
spring: Optional[Spring] = None,
props: Tuple[str, ...] = _DEFAULT_PROPS,
)
How a shared element flies between screens.
Set it on the destination view's shared_transition (or the
source's). Without one, the flight matches the platform's push.
Attributes:
| Name | Type | Description |
|---|---|---|
duration |
Optional[float]
|
Milliseconds, for a timed flight. |
easing |
Union[EasingSpec, str, None]
|
The timed flight's curve (ease-in-out by default). |
spring |
Optional[Spring]
|
A spring instead of a timed flight. |
props |
Tuple[str, ...]
|
Paint properties that blend from the source to the destination. |
Release precompilation¶
Release builds ship without source, so pn build compiles every worklet
while staging. These modules are for tooling and contributors; apps
don't call them.
Compile every worklet in a staged tree before release builds drop the sources (RFC 0009).
Release builds ship sourceless bytecode, so a worklet can't be compiled
from its source on the device. Staging runs
precompile_tree first:
it finds every @worklet function and every inline lambda passed to an
API that only accepts worklets, compiles them, and writes
pn_worklets.json at the root of the tree, which is on sys.path in
the app. At run time a code object is looked up by its file, first line,
and name, and a lambda by where its body starts, which both ast and
co_positions() report.
Functions:
| Name | Description |
|---|---|
find_worklets |
Every |
precompile_source |
Compile the worklets in one module's source into table entries. |
precompile_tree |
Write |
Attributes:
| Name | Type | Description |
|---|---|---|
WORKLET_APIS |
Calls whose inline lambda arguments are worklets. |
WORKLET_APIS
module-attribute
¶
WORKLET_APIS = frozenset(
{
"worklet",
"use_animated_style",
"use_derived_value",
"use_animated_reaction",
"use_frame_callback",
}
)
Calls whose inline lambda arguments are worklets.
find_worklets
¶
Every FunctionDef marked @worklet and every Lambda passed to a worklet API.
precompile_source
¶
Compile the worklets in one module's source into table entries.
precompile_tree
¶
precompile_tree(
root: Path, label: str = ""
) -> List[WorkletError]
Write root/pn_worklets.json for every module under root; return the worklets that failed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Path
|
The staged directory (the app, or a packages directory). |
required |
label
|
str
|
The bundle-relative directory the bytecode records for
|
''
|
The compiler¶
Compile worklets: Python functions the renderer core runs on the UI thread (RFC 0009).
A worklet is ordinary, type-checked Python written in a subset the core can
verify and bound. This module parses its source with ast, checks it
against the subset, and emits a Program:
stack-machine code plus the names it reads from outside (its captures),
which pythonnative.motion.worklets resolves
each time the worklet is installed.
The subset:
- Parameters: positional only, without defaults. An annotation on an event parameter is for the type checker; the core reads the event's fields by name.
- Literals:
int,float,bool,None,str, tuples, lists, and dicts. - Operators: arithmetic (
+ - * / // % **), unary-,+, andnot, comparisons (chained,in,is),and,or, and conditional expressions. - Statements: assignment (including augmented and tuple unpacking),
if/elif/else,foroverrange(...)or a sequence,break,continue,return,pass, and expression statements. - List and tuple comprehensions over
range(...)or a sequence. - Calls to other worklets,
abs,min,max,round,int,float,bool,len,mathfunctions, and the motion helpers. .valueon shared values,.cancel()on a shared value, and fields of event parameters.
Anything else raises WorkletError
with the file, line, and column, and a suggestion.
Functions:
| Name | Description |
|---|---|
compile_function |
Compile a function from its source. |
compile_node |
Compile a |
compile_function
¶
Compile a function from its source.
Raises:
| Type | Description |
|---|---|
WorkletError
|
When the source isn't available or uses Python a worklet can't. |
Program
dataclass
¶
Program(
name: str,
file: str,
line: int,
params: int,
locals: int,
consts: List[Any],
code: List[int],
lines: List[int],
captures: List[Capture] = list(),
)
A compiled worklet: what the core runs, and what Python resolves at install.
Methods:
| Name | Description |
|---|---|
document |
The JSON document the core decodes and verifies. |
disassemble |
A readable listing (debugging and tests). |
to_json |
Everything needed to rebuild the program without its source (release builds). |
from_json |
Rebuild a program written by |
to_json
¶
Everything needed to rebuild the program without its source (release builds).
See also¶
- The Motion guide covers the threading model,
the worklet language, animatable properties, testing, and migrating
from
Animated. - Gestures lists the recognizers whose callbacks take worklets.
- The collapsing header example
drives a header from an
on_scrollworklet.