Text splitting and animation system for creating character, word, and line-based text animations with accessibility support.
Split text into animatable lines, words, and characters with customizable templates and accessibility features.
/**
* Split text into animatable segments (lines, words, characters)
* @param target - Element(s) containing text to split
* @param parameters - Text splitting configuration
* @returns TextSplitter instance
*/
function splitText(
target: HTMLElement | NodeList | string | Array<HTMLElement>,
parameters?: TextSplitterParams
): TextSplitter;
/** Alias for splitText */
function split(
target: HTMLElement | NodeList | string | Array<HTMLElement>,
parameters?: TextSplitterParams
): TextSplitter;Usage Example:
import { splitText, animate } from "animejs";
// Split text into words and characters
const splitter = splitText(".heading", {
type: "words, chars",
});
// Animate characters with stagger
animate(splitter.chars, {
opacity: [0, 1],
translateY: [20, 0],
stagger: 20,
});Split text into animated segments with full control over output structure and behavior.
/**
* TextSplitter class for text animation preparation
*/
class TextSplitter {
// ===== Properties =====
/** Target element containing the text */
$target: HTMLElement;
/** Original HTML before splitting */
html: string;
/** Array of split line elements */
lines: Array<HTMLElement>;
/** Array of split word elements */
words: Array<HTMLElement>;
/** Array of split character elements */
chars: Array<HTMLElement>;
/** Whether splitter is ready (split completed) */
ready: boolean;
// ===== Configuration Properties =====
/**
* Include space characters as separate elements
* When true, spaces become animatable elements
*/
includeSpaces: boolean;
/**
* Maintain accessibility for screen readers
* Preserves original text structure for assistive technologies
*/
accessible: boolean;
/**
* Only split into lines, skip words and characters
* Performance optimization when only line splitting needed
*/
linesOnly: boolean;
/**
* Template for line wrapper elements
* String: HTML template with {{line}} placeholder
* Function: Receives line element, returns wrapper element
* false: No line wrapper
*/
lineTemplate: string | false | SplitFunctionValue;
/**
* Template for word wrapper elements
* String: HTML template with {{word}} placeholder
* Function: Receives word element, returns wrapper element
* false: No word wrapper
*/
wordTemplate: string | false | SplitFunctionValue;
/**
* Template for character wrapper elements
* String: HTML template with {{char}} placeholder
* Function: Receives char element, returns wrapper element
* false: No character wrapper
*/
charTemplate: string | false | SplitFunctionValue;
/**
* Enable debug mode
* Shows visual boundaries of split elements
*/
debug: boolean;
// ===== Methods =====
/**
* Perform text split operation
* @param clearCache - Clear internal cache before splitting
* @returns TextSplitter instance for chaining
*/
split(clearCache?: boolean): TextSplitter;
/**
* Add animation effect to split text
* @param effect - Effect configuration
* @returns TextSplitter instance for chaining
*/
addEffect(effect: TextEffectParams): TextSplitter;
/**
* Refresh split after content or layout changes
* @returns TextSplitter instance for chaining
*/
refresh(): TextSplitter;
/**
* Revert to original text and cleanup
* @returns TextSplitter instance for chaining
*/
revert(): TextSplitter;
}Usage Examples:
import { splitText, animate, stagger } from "animejs";
// Basic character split
const chars = splitText(".title", {
type: "chars",
});
animate(chars.chars, {
opacity: [0, 1],
stagger: 50,
});
// Word and character split
const text = splitText(".paragraph", {
type: "words, chars",
});
animate(text.words, {
opacity: [0, 1],
translateY: [20, 0],
stagger: 30,
});
// Line split only
const lines = splitText(".content", {
type: "lines",
linesOnly: true, // Performance optimization
});
animate(lines.lines, {
translateX: [-100, 0],
opacity: [0, 1],
stagger: 100,
});Configure which text segments to create:
// Split into characters only
splitText(".text", {
type: "chars",
});
// Split into words only
splitText(".text", {
type: "words",
});
// Split into lines only
splitText(".text", {
type: "lines",
});
// Combine multiple types
splitText(".text", {
type: "lines, words, chars",
});
// Alternative syntax
splitText(".text", {
type: ["lines", "words", "chars"],
});Customize wrapper elements with templates:
// String templates with placeholders
splitText(".text", {
type: "chars",
charTemplate: '<span class="char">{{char}}</span>',
});
splitText(".text", {
type: "words, chars",
wordTemplate: '<span class="word" data-word="{{word}}">{{word}}</span>',
charTemplate: '<span class="char" data-char="{{char}}">{{char}}</span>',
});
// Function templates for dynamic control
splitText(".text", {
type: "chars",
charTemplate: (char, index) => {
const span = document.createElement("span");
span.className = "char";
span.textContent = char.textContent;
span.style.setProperty("--index", index);
return span;
},
});
// Disable wrappers
splitText(".text", {
type: "words",
wordTemplate: false, // No wrapper for words
});Make spaces animatable:
const text = splitText(".heading", {
type: "chars",
includeSpaces: true, // Spaces become separate elements
});
// Now spaces can be animated independently
animate(text.chars, {
opacity: [0, 1],
stagger: 20,
delay: (el, i) => {
return el.textContent === " " ? i * 30 : i * 20;
},
});Maintain screen reader compatibility:
splitText(".content", {
type: "words, chars",
accessible: true, // Preserves text for screen readers
});
// Accessible mode adds aria attributes and preserves
// original text structure for assistive technologiesPerformance optimization for line splitting:
// When you only need lines, skip word/char processing
const lines = splitText(".article", {
type: "lines",
linesOnly: true, // Faster splitting
});
animate(lines.lines, {
opacity: [0, 1],
stagger: 200,
});Add pre-configured effects to split text:
/**
* Add animation effect to split text
*/
interface TextEffectParams {
/** Effect name or custom animation */
name?: string;
/** Target segment: 'lines', 'words', 'chars' */
target?: "lines" | "words" | "chars";
/** Animation parameters */
[key: string]: any;
}Usage Example:
const text = splitText(".heading", {
type: "words, chars",
});
// Add effect
text.addEffect({
target: "chars",
opacity: [0, 1],
translateY: [50, 0],
stagger: 20,
ease: "outQuad",
});Access split segments for animation:
const text = splitText(".content", {
type: "lines, words, chars",
});
// Access split elements
console.log(text.lines); // Array of line elements
console.log(text.words); // Array of word elements
console.log(text.chars); // Array of character elements
// Animate different segments
animate(text.lines, {
opacity: [0, 1],
stagger: 200,
});
animate(text.chars, {
scale: [0, 1],
stagger: 10,
delay: 500,
});Update split after content changes:
const text = splitText(".dynamic-text");
// Update text content
document.querySelector(".dynamic-text").textContent = "New text";
// Refresh split
text.refresh();Restore original text structure:
const text = splitText(".text", {
type: "chars",
});
// Animate...
// Restore original
text.revert();Visual debugging of split elements:
splitText(".text", {
type: "words, chars",
debug: true, // Shows element boundaries
});const text = splitText(".title", { type: "chars" });
animate(text.chars, {
opacity: [0, 1],
translateY: [20, 0],
stagger: 30,
ease: "outQuad",
});const text = splitText(".heading", { type: "chars" });
animate(text.chars, {
translateY: [0, -20, 0],
duration: 600,
stagger: {
value: 50,
from: "center",
},
loop: true,
});const text = splitText(".text", { type: "words" });
animate(text.words, {
opacity: [0, 1],
translateX: [-50, 0],
stagger: 50,
ease: "outCubic",
});const text = splitText(".title", {
type: "chars",
charTemplate: '<span class="char-wrap">{{char}}</span>',
});
animate(text.chars, {
opacity: [0, 1],
rotate: [-90, 0],
stagger: 40,
ease: "outBack",
});import { splitText, animate, random } from "animejs";
const text = splitText(".text", { type: "chars" });
animate(text.chars, {
opacity: [0, 1],
translateX: () => random(-50, 50),
translateY: () => random(-50, 50),
rotate: () => random(-180, 180),
stagger: 20,
ease: "outQuad",
});const text = splitText(".article", {
type: "lines",
linesOnly: true,
});
animate(text.lines, {
opacity: [0, 1],
translateY: [30, 0],
stagger: 150,
ease: "outCubic",
});const text = splitText(".code", {
type: "chars",
includeSpaces: true,
});
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const originals = text.chars.map((el) => el.textContent);
animate(text.chars, {
duration: 2000,
stagger: 50,
update: (anim) => {
text.chars.forEach((el, i) => {
if (anim.progress < 0.7) {
el.textContent = chars[Math.floor(Math.random() * chars.length)];
} else {
el.textContent = originals[i];
}
});
},
});const text = splitText(".heading", {
type: "words, chars",
});
// Animate each word's characters sequentially
text.words.forEach((word, wordIndex) => {
const wordChars = word.querySelectorAll(".char");
animate(wordChars, {
opacity: [0, 1],
scale: [0, 1],
stagger: 20,
delay: wordIndex * 200, // Delay between words
});
});Handle responsive layouts:
const text = splitText(".responsive-text", {
type: "lines, words",
});
// Refresh on resize to recalculate lines
let resizeTimer;
window.addEventListener("resize", () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
text.refresh();
}, 200);
});Split multiple elements at once:
// Split all headings
const headings = splitText(".heading", {
type: "words",
});
// Animate each separately
document.querySelectorAll(".heading").forEach((heading, i) => {
const words = heading.querySelectorAll(".word");
animate(words, {
opacity: [0, 1],
translateY: [20, 0],
stagger: 30,
delay: i * 500,
});
});/**
* Text splitter configuration parameters
*/
interface TextSplitterParams {
/** Split type: 'lines', 'words', 'chars' or combinations */
type?: string | Array<"lines" | "words" | "chars">;
/** Include space characters as elements */
includeSpaces?: boolean;
/** Maintain accessibility for screen readers */
accessible?: boolean;
/** Only split lines (performance optimization) */
linesOnly?: boolean;
/** Line wrapper template */
lineTemplate?: string | false | SplitFunctionValue;
/** Word wrapper template */
wordTemplate?: string | false | SplitFunctionValue;
/** Character wrapper template */
charTemplate?: string | false | SplitFunctionValue;
/** Enable debug mode */
debug?: boolean;
}
/**
* Split template function type
*/
type SplitFunctionValue = (
element: HTMLElement,
index: number,
total: number
) => HTMLElement;
/**
* Text effect parameters
*/
interface TextEffectParams {
/** Target segment type */
target?: "lines" | "words" | "chars";
/** Effect name */
name?: string;
/** Animation parameters */
[key: string]: any;
}