renderer handles
Adapters
The same Chop character projection animated with GSAP, Anime.js, Motion, and CSS keyframes.
One split, four renderers
Chop owns the projection. The animation engine is swappable.
GSAP
gsap.to(chars) Split once animate anywhere
Timeline-grade control over the same Chop character handles.
Anime.js
animate(chars) Split once animate anywhere
Millisecond timing with native stagger helpers.
Motion
animate(char) Split once animate anywhere
Per-handle playback controls using seconds-based timing.
CSS animation
@keyframes Split once animate anywhere
Plain keyframes driven by Chop index variables.
Same Chop handles, different renderers
1
import { chop } from '@tonybonet/chop'
2
import { gsap } from 'gsap'
3
import { animate as animeAnimate } from 'animejs/animation'
4
import { cubicBezier as animeEase } from 'animejs/easings/cubic-bezier'
5
import { stagger } from 'animejs/utils'
6
import { animate as motionAnimate } from 'motion/mini'
7
8
const split = chop(title)
9
const chars = split.chars
10
11
gsap.set(chars, { y: 16, opacity: 0, filter: 'blur(12px)' })
12
gsap.to(chars, {
13
y: 0,
14
opacity: 1,
15
filter: 'blur(0px)',
16
duration: 0.9,
17
stagger: 0.025,
18
ease: 'power3.out'
19
})
20
21
animeAnimate(chars, {
22
translateY: [16, 0],
23
opacity: [0, 1],
24
filter: ['blur(12px)', 'blur(0px)'],
25
duration: 900,
26
delay: stagger(25),
27
ease: animeEase(0.22, 1, 0.36, 1)
28
})
29
30
chars.forEach((char, index) => {
31
motionAnimate(char, {
32
opacity: [0, 1],
33
transform: ['translate3d(0, 16px, 0)', 'translate3d(0, 0, 0)'],
34
filter: ['blur(12px)', 'blur(0px)']
35
}, {
36
delay: index * 0.025,
37
duration: 0.9,
38
ease: [0.22, 1, 0.36, 1]
39
})
40
})
41
42
chars.forEach((char, index) => char.style.setProperty('--adapter-index', String(index)))
43
card.classList.add('is-running')