Timeline storyboard
Four cards each perform their own signature move, then join for a closing wave. The whole thing plays, pauses, restarts and scrubs from a single control, the way an editor cuts separately-shot clips onto one timeline.
What's happening
Each card plays its own short move, like four dancers who each learned a separate routine. Those routines are lined up on one shared timeline, the way a video editor lines up clips on one track, and the four finish together with a rippling bow. Drag the scrub bar and you move through all of it at once, so halfway through you might catch Card 02 mid-flip while Card 03 hasn't started yet.
How it's built
The goal was to write and tune each card's routine without worrying about
the other three, so each one gets its own
useAnimeTimeline, built with
autoplay: false so it sits ready but doesn't play on its own. The closing
wave is a fifth timeline of the same kind, staggered across all four boxes
at once.
Those five get dropped onto a sixth, master timeline with
.sync(childTimeline, offsetMs), where offsetMs is how far into the
master that routine starts. The offsets climb (0ms, 350ms, 850ms, 1450ms),
so each card starts a beat after the last one, and the wave lands at 2300ms
once the solos are done.
From there, the play, pause, and scrub controls only ever talk to that one
master timeline. Calling .seek(progress * duration) on it is enough to
scrub every card at once, because they're all riding the same clock.
Building it yourself
- Give each card its own
useAnimeTimelineinstance, created withautoplay: false, and author its keyframes independently of the others. - Optionally split each card into two nested elements: an outer "mover"
divfory(position), and an inner "rotator"divforscaleandrotate. This isn't required. Anime.js keeps one shared transform cache per element, so two separate.add()calls for position and rotation would still merge cleanly into onetransformeven on a singlediv. The split is there for organization: position steps and rotation steps rarely share the same step count or duration, so giving each its own element keeps the two.add()calls independent and easy to read, instead of forcing one keyframe schedule to fit both. - Add one more
autoplay: falsetimeline for a closing move that covers every card at once, usingstagger()on the shared selector rather than per-card offsets. - Create a final
useAnimeTimeline, alsoautoplay: false, to act as the master. Give itonUpdatefor progress reporting andonBegin/onPause/onCompletefor play state. - Call
.sync(childTimeline, offsetMs)on the master for each card and for the closing move, staggering the offsets so the routines play in sequence and the closing move lands after the solos. - Wire Play, Pause, and Restart buttons to the master's
.play(),.pause(), and.restart(), and a range slider to.seek(value * master.duration).
Using the nanime Nuxt module, build a Vue 3 component with 4 card elements.
Give each card its own useAnimeTimeline instance (autoplay: false) with a
distinct multi-step animation (position, rotation, scale) using arrays of
keyframes with an individual duration and ease per step. Use Anime.js's own
easing names, like 'outQuad', 'outBounce', 'outElastic', 'inOutSine', and
'linear'. Skip the 'easeOutQuad' or 'easeInOutSine' naming convention some
other animation libraries use. An unrecognized ease string fails silently
and falls back to linear, so get this from the module's actual easing list
rather than guessing. Optionally split each card into a "mover" element for
position and a "rotator" element for scale and rotation, purely to keep
each `.add()` call's keyframe schedule independent since they rarely share
the same step count or duration.
Add one more autoplay: false timeline for a closing wave that animates every
card at once, using stagger() on the shared box selector so the boxes ripple
rather than moving together.
Create a master useAnimeTimeline (autoplay: false) with onUpdate reporting
progress from 0 to 1, and onBegin / onPause / onComplete toggling an
isPlaying flag. Sync each card's timeline into the master with
.sync(childTimeline, offsetMs), staggering the offsets so the cards animate
one after another instead of all at once, and sync the closing wave last so
it plays after the solos finish.
Add Play, Pause, and Restart buttons calling the master's .play(),
.pause(), and .restart(), and a range slider that calls
.seek(value * master.duration) to scrub through the whole composition.