Motion
@praxisjs/motion — signal-driven animations via @Tween and @Spring field decorators. Assigning a value triggers a smooth animated transition.
Motion
Animated field decorators. Assign a new value and the transition plays automatically — no imperative animation API, no requestAnimationFrame loops, no timeline management.
npm install @praxisjs/motionpnpm add @praxisjs/motionyarn add @praxisjs/motionbun add @praxisjs/motion@Tween(options?)
Animates a numeric field from its current value to a new one using a duration-based easing curve. Reading the field returns the current interpolated value. A field starts at rest — creating the component schedules no animation frame — and a running animation is stopped when the component unmounts.
import { Tween } from '@praxisjs/motion'
@Component()
class ProgressBar extends StatefulComponent {
@Tween({ duration: 600, easing: 'easeOut' })
progress = 0
load() {
this.progress = 100 // smoothly animates from current value to 100
}
render() {
return (
<div
class="bar"
style={() => ({ width: `${this.progress}%` })}
/>
)
}
}Assigning a new value starts a new animation from wherever the field currently is — even if a previous animation is still in progress.
| Option | Type | Default | Description |
|---|---|---|---|
duration | number | 300 | Animation duration in ms |
easing | Easing | 'easeOut' | Easing function name or custom (t: number) => number |
delay | number | 0 | Delay before the animation starts in ms |
Available easings
'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInCubic' | 'bounce' | 'elastic'Custom easing — any function from [0, 1] to [0, 1]:
@Tween({ easing: (t) => t * t * t, duration: 400 })
scale = 1@Spring(options?)
Physics-based spring animation. The field settles toward its target value with natural momentum and configurable bounce. Assign a value and let the physics do the work.
import { Spring } from '@praxisjs/motion'
@Component()
class DragHandle extends StatefulComponent {
@Spring({ stiffness: 0.2, damping: 0.7 })
x = 0
@Spring()
y = 0
onPointerMove(e: PointerEvent) {
this.x = e.clientX
this.y = e.clientY
}
render() {
return (
<div
class="handle"
style={() => ({ transform: `translate(${this.x}px, ${this.y}px)` })}
onPointermove={(e) => this.onPointerMove(e)}
/>
)
}
}| Option | Type | Default | Description |
|---|---|---|---|
stiffness | number | 0.15 | Spring strength — higher = snappier. Must be > 0. |
damping | number | 0.8 | Resistance — lower = more bounce and overshoot |
mass | number | 1 | Object mass — higher = more inertia |
precision | number | 0.001 | Settlement threshold — how close to the target before animation stops |
Validation
stiffness must be greater than 0. Passing 0 or a negative value throws immediately.
createTransition(options?)
Creates CSS-class transitions for elements entering and leaving the DOM. enter(el) and leave(el) return a Promise that resolves when the transition is over. Pass the result to the transition prop of For and Index to animate added and removed rows: a removed row stays in place until leave resolves.
import { createTransition } from '@praxisjs/motion'
import { For } from '@praxisjs/runtime'
const fade = createTransition({ name: 'fade', duration: 200 })
<For each={() => this.toasts} by="id" transition={fade}>
{(toast) => <div class="toast">{() => toast().message}</div>}
</For>.toast { transition: opacity .2s ease, transform .2s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; transform: translateY(-4px); }On enter, ${name}-enter-from is added, swapped for ${name}-enter-to on the next frame, and removed after duration. leave does the same with ${name}-leave-from and ${name}-leave-to.
| Option | Type | Default | Description |
|---|---|---|---|
name | string | "transition" | Prefix of the classes added during the transition |
duration | number | 300 | Milliseconds until the Promise resolves and the classes are removed |
onEnter | (el: HTMLElement) => void | — | Called before the enter classes are added. Throwing rejects the Promise. |
onLeave | (el: HTMLElement) => void | — | Called before the leave classes are added. Throwing rejects the Promise. |
Dependency Injection
@praxisjs/di — decorator-based DI with @Injectable, @Inject, @InjectContainer, and @Scope for per-instance scoped containers. Use inject() for imperative resolution outside components.
State Machines
@praxisjs/fsm — finite state machines via @StateMachine and @Transition decorators, with typed states/events, reactive state signal, guards, per-transition actions, and per-state lifecycle hooks.