PraxisJS

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/motion
pnpm add @praxisjs/motion
yarn add @praxisjs/motion
bun 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.

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.

OptionTypeDefaultDescription
durationnumber300Animation duration in ms
easingEasing'easeOut'Easing function name or custom (t: number) => number
delaynumber0Delay 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
Storybook
Live demo — @Tween

@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)}
      />
    )
  }
}
OptionTypeDefaultDescription
stiffnessnumber0.15Spring strength — higher = snappier. Must be > 0.
dampingnumber0.8Resistance — lower = more bounce and overshoot
massnumber1Object mass — higher = more inertia
precisionnumber0.001Settlement threshold — how close to the target before animation stops

Validation

stiffness must be greater than 0. Passing 0 or a negative value throws immediately.

Storybook
Live demo — @Spring

On this page