Nest responsive SplitText animations inside a timeline
What if a SplitText animation gets nested inside a Timeline, and then later that SplitText instance re-splits due to something like a viewport resize (exactly what autoSplit is for)? The animation must get re-created to animate the new elements from the re-split but how do we get that new animation to swap itself into the timeline at exactly the same spot as the original animation (and kill() that previous animation)? That's precisely what this SplitTextAnimator() helper function does.
Feed SplitTextAnimator exactly the same parameters as you would a normal SplitText; it just returns a SplitText that has an additional animate() method that you give a callback for creating your animation(s). It'll immediately call that callback and return whatever GSAP animation you return in that callback so that it can be added to your timeline.
/*
Helper function that creates a single SplitText instance and then allows you to create
as many animations of that SplitText as you want, and it'll dynamically update them when
the SplitText instance splits (responsive). It adds an animate() function that you pass a
function that creates (AND RETURNS!) your animation that you can place into timelines, like:
let split = SplitTextAnimator(target, {type: "words,lines", autoSplit: true});
let tl = gsap.timeline();
tl.add( split.animate((self) => {
return gsap.to(self.words, {...});
})
.add( split.animate((self) => {
return gsap.to(self.words, {...});
});
*/
function SplitTextAnimator(target, config) {
let originalOnSplit = config.onSplit,
subscribers = [],
self = SplitText.create(target, {
...config,
autoSplit: true,
onSplit(self) {
subscribers.forEach((f) => f(self));
originalOnSplit && originalOnSplit(self);
},
});
self.animate = (create) => {
let animation,
onSplit = (self) => {
let parent, startTime;
if (animation) {
parent = animation.parent;
startTime = animation.startTime();
animation.kill();
}
animation = create && create(self);
parent && parent.add(animation, startTime || 0);
};
subscribers.push(onSplit);
onSplit(self);
return animation;
};
return self;
}
You can use the animate() method multiple times to insert multiple animations into your timeline (perhaps one for words, one for lines, for example).
Then, whenever the SplitText instance re-splits, it will call that callback you passed to the animate() method again to generate a new animation and swap it directly into the same place in the parent timeline.
Usage
const tl = gsap.timeline();
const split = SplitTextAnimator(boxText, {
type: "words, lines",
});
tl.add(
split.animate((self) => {
return gsap.from(self.chars, {
autoAlpha: 0,
y: 25,
stagger: {
amount: 0.5,
},
});
}),
);
Simple Demo
Resize the Codepen preview by toggling any of the panels in order to see it in action.
