@wix/motion
v2.1.7
Published
Low-level, web-first, animation toolkit, with support for triggered, scroll-driven, pointer-tracking, WAAPI & CSS animations.
Downloads
1,448
Readme
@wix/motion
Low-level, web-native animation engine — WAAPI, CSS, scroll-driven, and pointer-tracking animations with a single dependency.
Why Motion?
- Native-first — Built directly on the Web Animations API and CSS Animations.
- ViewTimeline — First-class scroll-driven animations via the ViewTimeline API, with a scrub fallback when the API is unavailable.
- Pointer-driven —
pointer-moveanimations map cursor(x, y)progress to effects, with optional transition smoothing. - Custom effects — Plug in programmatic render callbacks — no preset registration required.
- Dual rendering — Choose CSS for declarative effects or WAAPI for fine-grained control, using the same options shape.
- Performance —
fastdombatches DOM reads/writes; norequestAnimationFrameloop (except for customEffect callbacks). - Pluggable presets —
registerEffects()accepts any effect module. Use@wix/motion-presetsor create your own.
Install
npm install @wix/motionQuick Start
Time-based animation (WAAPI)
import { getWebAnimation } from '@wix/motion';
const animation = getWebAnimation(document.getElementById('hero'), {
keyframeEffect: {
name: 'fade-up',
keyframes: [
{ opacity: 0, transform: 'translateY(20px)' },
{ opacity: 1, transform: 'translateY(0)' },
],
},
duration: 600,
easing: 'ease-out',
});
animation.play();Scroll-driven (ViewTimeline)
import { getWebAnimation } from '@wix/motion';
const scrollRoot = document.getElementById('scrollRoot')!;
const animation = getWebAnimation(
document.getElementById('parallax'),
{
keyframeEffect: {
name: 'parallax',
keyframes: [{ transform: 'translateY(80px)' }, { transform: 'translateY(-80px)' }],
},
startOffset: { name: 'cover', offset: { value: 0, unit: 'percentage' } },
endOffset: { name: 'cover', offset: { value: 100, unit: 'percentage' } },
},
{ trigger: 'view-progress', element: scrollRoot },
);Scroll-driven (polyfill / custom scrubbing)
import { getScrubScene } from '@wix/motion';
const scrollRoot = document.getElementById('scrollRoot')!;
const scenes = getScrubScene(
document.getElementById('parallax'),
{
keyframeEffect: {
name: 'parallax',
keyframes: [{ transform: 'translateY(80px)' }, { transform: 'translateY(-80px)' }],
},
startOffset: { name: 'cover', offset: { value: 0, unit: 'percentage' } },
endOffset: { name: 'cover', offset: { value: 100, unit: 'percentage' } },
},
{ trigger: 'view-progress', element: scrollRoot },
);
// Drive each scene's `effect(scene, progress)` from your own scroll/IO listener
// when ViewTimeline is unavailable.Quickstart examples use keyframeEffect (inline keyframes) so they run without registering presets.
Animation Modes
| Mode | Driver | API |
| -------------- | ----------------------------- | ---------------------------------------------- |
| Time-based | Duration + easing | getWebAnimation() / getCSSAnimation() |
| Scroll-driven | ViewTimeline / external scrub | getScrubScene() with view-progress trigger |
| Pointer-driven | Mouse / touch position | getScrubScene() with pointer-move trigger |
Core API
| Function | Purpose |
| -------------------- | ----------------------------------------------------------- |
| getWebAnimation() | Create WAAPI-backed animations (time- or scroll-linked) |
| getCSSAnimation() | Generate CSS animation descriptors for stylesheet injection |
| getScrubScene() | Build scroll-polyfill or pointer-driven scrub scenes |
| prepareAnimation() | Pre-measure / mutate DOM via fastdom before animating |
| getAnimation() | Auto-select CSS (if present) or WAAPI path |
| getSequence() | Coordinate staggered groups with easing-based offsets |
| registerEffects() | Register named effect modules into the global registry |
See docs/api/ for full signatures and options.
Custom Effects
Three ways to define what an animation does:
- Inline keyframes — pass
keyframeEffect: { name, keyframes }directly. Zero registration. - Custom callback — pass
customEffect: (element, progress) => voidfor full programmatic control per frame. - Named presets — pass
namedEffect: { type: '…', …params }referencing effects you've registered viaregisterEffects()(use@wix/motion-presetsor your own modules).
Sequences and Staggering
getSequence() plays multiple animations with staggered start times. Pass offset (ms between each start) and an optional offsetEasing to shape how the offsets are distributed across the sequence.
import { getSequence } from '@wix/motion';
const sequence = getSequence(
{ offset: 150, offsetEasing: 'quadIn' },
Array.from(document.querySelectorAll('.card')).map((el) => ({
target: el,
options: {
duration: 600,
easing: 'ease-out',
keyframeEffect: {
name: 'fade-up',
keyframes: [
{ opacity: 0, transform: 'translateY(20px)' },
{ opacity: 1, transform: 'translateY(0)' },
],
},
},
})),
);
sequence.play();See docs/api/get-sequence.md for the full stagger model.
ViewTimeline and Polyfills
Motion is built around progressive enhancement:
- Native path — when
window.ViewTimelineis available,getWebAnimation()with aview-progresstrigger returns a WAAPI animation linked to the scroll timeline. - Polyfill path —
getScrubScene()withview-progressreturnsScrubScrollScene[]objects exposingstart,end,viewSource, andeffect(scene, progress). Drive these from your own IntersectionObserver/scroll listener. If using@wix/interact, its bundled scroll polyfill -fizban- handles this automatically. - Pointer smoothing —
ScrubPointerSceneacceptstransitionDurationandtransitionEasingso pointer-tracking effects don't snap to the cursor.
Performance Notes
prepareAnimation()runsfastdommeasure/mutate phases before the animation starts, avoiding layout thrash.- The CSS rendering path (
getCSSAnimation) offloads work to the compositor thread. - No
requestAnimationFrameloop runs unless acustomEffectcallback is used.
Browser Support
Modern evergreen browsers with Web Animations API support (Chrome, Edge, Firefox, Safari). The ViewTimeline API is used where available; pair getScrubScene() with an external driver for older browsers.
Related Packages
Motion is the engine layer. The other packages in this repo build on top of it:
@wix/interact— declarative, config-driven interaction layer built on Motion.@wix/motion-presets— ready-made effect catalog (entrance, ongoing, scroll, mouse, background-scroll).
