跳过主要内容

SplitText

快速入门

CDN 链接

gsap.registerPlugin(SplitText) 

最简用法

// split elements with the class "split" into words and characters
let split = SplitText.create(".split", { type: "words, chars" });

// now animate the characters in a staggered fashion
gsap.from(split.chars, {
duration: 1,
y: 100, // animate from 100px below
autoAlpha: 0, // fade in from opacity: 0 and visibility: hidden
stagger: 0.05 // 0.05 seconds between each
});

或者使用v3.13.0+版本中引入的新onSplit()可用在 v3.13.0+ 版本中的语法,来实现完全相同的功能:

SplitText.create(".split", {
type: "words, chars",
onSplit(self) {
gsap.from(self.chars, {
duration: 1,
y: 100,
autoAlpha: 0,
stagger: 0.05
});
}
});

SplitText 是一个小型 JavaScript 库,它可以将 HTML 元素中的文本拆分为单独的字符、单词和/或行(每个都位于其自己的新创建的元素中),从而让您创建精美的交错动画效果。它高度可配置,并且由于具备自动屏幕阅读器无障碍访问功能、遮罩显示效果、响应式重新拆分等多项特性,比其他文本拆分工具更智能。

详细操作指南 - 重大改写于v3.13.0- 体积减半,新增了 14 项功能!

特性

特性亮点

下方标记为 "*" 的内容是 v3.13.0+ 新增功能

  • 屏幕阅读器无障碍访问* - 在拆分元素上添加aria-label,并在新创建的行/词/字符元素上添加aria-hidden
  • 响应式重新拆分* - 通过配合autoSplitonSplit()使用,避免调整大小或字体加载时出现异常的换行。还提供自动清理和动画继续播放功能!
  • 穿透嵌套元素* - 像<span>, <strong>,以及<a>这类跨越多行的元素可以轻松地被deepSlice处理,以确保它们不会使行垂直拉伸。
  • 遮罩* - 使用额外的剪裁元素包裹字符、单词或行,以便实现简单的遮罩/显示效果。
  • 无缝集成与 GSAP 的context(), matchMedia()useGSAP()
  • 灵活的目标设置- 可以为字符、单词或行应用自定义类名。附加"++"来自动递增编号(例如word1, word2,等)。启用propIndex* 以应用类似--word: 3.
  • 忽略特定元素* - 比如您可以选择让<sup>元素不被拆分。
  • 支持表情符号及其他字符- SplitText 同样能出色处理非英文字符。
  • 随时还原- 随时通过innerHTML还原元素原始的revert()
  • 处理复杂的边缘情况使用自定义RegExp* 或prepareText()*

拆分

基本用法

提供您希望拆分的SplitText.create()元素,该方法会返回一个 SplitText 实例,实例包含chars, words,以及lines属性,您可以从中访问生成的各个元素。

// the target can be selector text, an element, or an Array of elements
let split = SplitText.create(".headline");

// Array of characters
split.chars

// Array of words
split.words

// Array of lines
split.lines

配置(Configuration)

默认情况下,SplitText 将按照type: "lines, words, chars"(即行、词、字符)进行拆分,但为了最大限度提升性能,您应该只拆分真正需要的内容。使用配置对象精确控制哪些组件被拆分、调整无障碍设置、应用自定义类,甚至应用遮罩效果。

let split = SplitText.create(".split", {
type: "words, lines", // only split into words and lines (not characters)
mask: "lines", // adds extra wrapper element around lines with overflow: clip (v3.13.0+)
linesClass: "line++", // adds "line" class to each line element, plus an incremented one too ("line1", "line2", "line3", etc.)

// there are many other options - see below for a complete list
});

配置对象

    属性

    描述

  • aria*

    "auto" | "hidden" | "none"- SplitText 可以自动向拆分元素及其内部的行/词/字符元素添加aria属性以改善无障碍访问体验。选项包括:
    • "auto"(默认)- 向拆分元素添加aria 标签,并基于其textContent内容填充;同时向拆分元素内的行/词/字符元素添加aria hidden。这可确保大多数屏幕阅读器能够正常访问文本内容。此方法将忽略嵌套元素的语义或功能。如果需要确保文字内容中的链接对屏幕阅读器可见,我们建议启用aria: "hidden"并创建一份仅供屏幕阅读器使用的文本副本。
    • "hidden": 向拆分元素及内部所有行/词/字符元素添加aria hidden
    • "none"- 不向拆分元素或其内部的行/词/字符元素添加任何aria属性。
    默认值:"auto"
  • autoSplit*

    Boolean- 有助于避免字体加载完成后或者元素宽度变化后因文本重排导致的奇怪换行问题。如果开启true,当字体加载完成或满足以下两个条件时,SplitText 将还原并重新拆分:同时满足以下两个条件时恢复并重新分割:
    1. 被分割元素的宽度发生变化
    2. "lines"已经被拆分成行。
    SplitText 甚至会在您尝试在字体加载完成之前进行拆分而没有设置console.warn()时提醒您。autoSplit: true

    注意事项

    当使用autoSplit: true时,请确保在onSplit()回调函数内创建所有动画,以确保实际动画作用于最新拆分出的行/词/字符元素。如果您在return动画中直接使用这些元素,onSplit()SplitText 将自动在每次重新拆分时清理并同步动画。

    SplitText.create(".split", {
    type: "lines",
    autoSplit: true,
    onSplit: (self) => {
    return gsap.from(self.lines, {
    y: 100,
    opacity: 0,
    stagger: 0.05
    });
    }
    });
    默认值:false
  • charsClass

    字符串- 应用于每个字符的<div>上的CSS类,方便选择。如果在类名末尾添加"++"SplitText还将添加一个第二类名称,该名称具有递增的数字后缀开始于1。例如,如果charsClass设置为"char++",第一个字符将获得class="char char1"下一个是class="char char2"状态设置,class="char char3"等。默认值:undefined.
  • deepSlice*

    Boolean- 如果像<strong>这样的嵌套元素跨多行显示,SplitText 将根据实际情况对其进一步拆分,以防止该元素在垂直方向上拉长行高。因此技术上一个嵌套元素可能会被拆分成多个元素。该项仅适用于lines。默认值:true.
  • ignore*

    字符串 | 元素- 拆分时忽略的后代元素(可以使用类似".split"的选择器文本或元素数组)。这些元素仍然存在——只是不会被拆分。示例演示在这里默认值:undefined
  • linesClass

    字符串- 应用于每行的<div>上的CSS类,方便选择。如果在类名末尾添加"++"SplitText还将添加一个第二类名称,该名称具有递增的数字后缀开始于1。例如,如果linesClass设置为"line++",第一行会有class="line line1"下一个是class="line line2"状态设置,class="line line3"等。默认值:undefined.
  • mask*

    "lines" | "words" | "chars"- 将每一行、每一个词或字符包裹在元素中元素内部,请确保编写能适应重新父级的 CSS 规则。visibility: clip的元素中,从而更容易实现显示效果。可在 SplitText 实例的 "masks" 数组中访问它们。如果您为行/词/字符设置了类名,则系统会自动为其追加"-mask"方便选择。你不能同时屏蔽多个类型,所以这个值应该是 "lines" 或 "words" 或者 "chars",但不能是它们的组合。默认值:undefined
  • onRevert*

    函数- 当SplitText实例恢复时会调用的一个函数
  • onSplit*

    函数- 每当SplitText实例完成分割时调用的函数,包括当字体加载完成后或被分割元素的宽度发生变化导致重新分割时(这通常会使行重新排列)。如果你返回一个GSAP动画(tween或timeline),它将自动保存其状态。autoSplit: true导致其重新分割的情况,比如字体加载完成或者被分割元素的宽度发生了改变(这往往会使得行重新流动)。如果你返回一个GSAP动画(补间或时间轴),它会自动保存该动画的状态。totalTime()revert()在SplitText恢复时,设置新的动画状态。totalTime()返回在回调中。onSplit,使其看起来相对无缝!
  • prepareText*

    函数- 在分割过程中对每个文本块调用的函数,允许你在SplitText执行其分割逻辑之前修改每个文本块。例如,你可能想要插入一些特殊字符来标记单词断点的位置。该prepareText()函数接收原始文本作为第一个参数,父元素作为第二个参数。你应当返回修改后的文本。这对于像中文这样的非拉丁语言特别有用,在这些语言中词之间没有空格。示例演示在这里
  • propIndex*

    Boolean- 为每个分割元素添加一个包含其索引的CSS变量,例如--word: 1, --word: 2等等。这对所有类型(行、词和字符)都有效。默认值:false
  • reduceWhiteSpace

    Boolean- 将连续的空白字符合并为一个,大多数浏览器通常都是这样处理的。设为false如果你想保留多个连续的空白字符。由于v3.13.0reduceWhiteSpace 会保留额外的空格并自动插入<br>标签来进行换行,这对<pre>内容很有用。默认值:true
  • smartWrap*

    Boolean- 如果仅按"chars"字符进行分割,可能会出现奇怪的断行情况,尤其是在单个单词中的字符流转到下一行时,这些词没有自然的词组绑定。smartWrap: true将会在<span>元素中包裹单词white-space: nowrap来保持它们的组合(仅当你不按词或行拆分时才起作用)。如果按"words"或者"lines"分割,则此功能会被忽略,因为没有必要。默认值:false
  • tag

    字符串- 默认情况下,SplitText使用<div>元素包裹内容,但你可以定义任何标签如tag: "span"。请注意,浏览器不会在内联元素上渲染旋转、缩放、倾斜等变换效果。
  • type

    字符串- 以逗号分隔的分割类型的列表,可以是以下任意组合:chars, words,或者lines。这表示你希望将哪些组件分割成独立的元素。例如,若要分割字符和词语(不包括行),可以使用type: "chars,words"或者只分割行,可以设置为type: "lines"。为了避免奇怪的换行断开,最好不要单独按字符分割(如果按字符分割的话,还应包括词语或行),或者直接设置smartWrap: true。注意:空格不被视为字符。默认值:"chars,words,lines".
  • wordDelimiter

    正则表达式 | "字符串" | 对象- 通常情况下,词语会在每一个空格字符处分割。通过wordDelimiter属性你可以指定自定义的词语分隔符。例如,如果你想分割类似#IReallyLoveGSAP的标签为词语,可以在每个词语之间插入一个零宽度词连接符(&#8205;)例如:#&#8205;I&#8205;Really&#8205;Love&#8205;GSAP然后在SplitText配置对象中设置wordDelimiter: String.fromCharCode(8205)。由于v3.13.0支持正则表达式,你可以非常灵活地指定分割位置以及替换的内容,比如
    wordDelimiter: {delimiter: yourRegExp, replaceWith: "yourReplacement"}
    默认值:" "(空格)
  • wordsClass

    字符串- 应用于每个词的<div>上的CSS类,方便选择。如果在类名末尾添加"++"SplitText还将添加一个第二类名称,该名称具有递增的数字后缀开始于1。例如,如果wordsClass设置为"word++"那么第一个单词将是class="word word1"下一个是class="word word2"状态设置,class="word word3"等。默认值:undefined.

动画

加载中...

一旦你的文本被分割,你可以使用GSAP来为每行、每个词或字符制作动画:

// split all elements with the class "split" into words and characters
let split = SplitText.create(".split", { type: "words, chars" });

// now animate the characters in a staggered fashion
gsap.from(split.chars, {
duration: 1,
y: 100, // animate from 100px below
autoAlpha: 0, // fade in from opacity: 0 and visibility: hidden
stagger: 0.05, // 0.05 seconds between each
});

或者使用v3.13.0+版本中引入的新onSplit()语法做完全相同的事情——主要优点是onSplit()中的代码将在SplitText实例重新分割时执行在未来(例如,如果你设置了autoSplit: true或者手动调用split()):

SplitText.create(".split", {
type: "words, chars",
onSplit(self) { // runs every time it splits
gsap.from(self.chars, {
duration: 1,
y: 100,
autoAlpha: 0,
stagger: 0.05
});
}
});

响应式行拆分*

如果仅当单词和/或字符被分割时,当容器调整大小时它们自然重排;但如果按分割,每一行元素都会包围一组特定的单词/字符。如果容器随后变窄了或者例如在文本分割之后字体加载进来,文本可能重新流动,导致某些单词属于不同的不同的行(某行最后一个词可能跳转到下一行)。避免奇怪换行的唯一方法是重新分割(恢复原文本)innerHTML并让SplitText再次运行其分割逻辑,以便行元素包含正确的单词。

别担心!SplitText的autoSplit功能拯救这一切!🥳 启用后,它将在字体加载完成或同时满足以下两个条件时恢复并重新分割:

  • 被分割元素的宽度发生变化
  • 分割了“lines”。

启用autoSplit后你应该下列信息将始终被记录:onSplit()回调中创建你的动画,这样当以后重新分割时,生成的动画会影响新创建的行/字/字符元素,而不是前一次分割的元素。如果返回你的tween或者timeline动画在onSplit()回调内部,旧的动画会在创建新动画之前安全地reverted()停止,SplitText会自动保存之前的动画状态totalTime()在回退之前,并将其应用到新的动画上,以便一切看起来相对无缝!SplitText实例会传递给onSplit()(下面,我们称它为self)这样你就可以访问它的属性:

// whenever you use autoSplit: true, ALWAYS create your animations in the onSplit()
SplitText.create(".split", {
type: "words,lines",
autoSplit: true,
onSplit(self) {
return gsap.from(self.lines, { // a returned animation gets cleaned up and time-synced on each onSplit() call
yPercent: 20,
opacity: 0,
stagger: 1,
duration: 3,
onComplete: () => self.revert() // revert the element to its original (unsplit) state
});
}
});

加载中...

遮罩*

遮罩会将每一行、每个单词或字符包裹在额外的元素中元素内部,请确保编写能适应重新父级的 CSS 规则。visibility: clip以实现有趣的展现效果。

SplitText.create(".split", {
type: "words,lines",
mask: "words", // <-- this can be "lines" or "words" or "chars"
});

加载中...

屏幕阅读器可访问性

盲人或视力受限的人可能会使用屏幕阅读器,该工具会分析网站内容并将其转换为语音,帮助他们浏览网页。屏幕阅读器会读取以下标题标签并大声朗读出来。

<h1>Heading</h1>

大多数文本拆分库只是将文本分成多个 div,屏幕阅读器会逐个字母地口述这些内容。非常慢地,逐字逐句……

<h1>
<div>H</div>
<div>e</div>
<div>a</div>
<div>d</div>
<div>i</div>
<div>n</div>
<div>g</div>
</h1>

内置 Aria*

为了解决这个问题,SplitText 会在父元素上添加一个aria-label并使用aria-hidden来隐藏子元素。aria-label这样可以确保当视障人士浏览您的网站时,屏幕阅读器会朗读出

<h2 aria-label="My Accessible Heading">
<div aria-hidden="true">My</div>
<div aria-hidden="true">Accessible</div>
<div aria-hidden="true">Heading</div>
</h2>

最大化嵌套元素可访问性的替代策略

SplitText's built in aria: "auto" solution is ideal for most common scenarios, but it won't surface the functionality and meaning of nested elements (like links) to screen readers. If you have complex nested text, you can use the duplication approach described below. Exercise restraint here as duplicating lots of DOM elements can lead to performance lags.

Treat text splitting with care and ensure you test thoroughly!

In the example below, the link may not be recognized as such by some screen readers:

<h2 aria-label="This link isn't accessible">
<div aria-hidden="true">This</div>
<div aria-hidden="true"><a href="#">link</a></div>
<div aria-hidden="true">isn't</div>
<div aria-hidden="true">accessible</div>
</h2>

If you need to preserve the semantics and functionality of nested elements - like links, <strong> tags or <em> tags - we recommend disabling the default aria settings for the SplitText with aria: "none"*, and creating a screen reader-only duplicate of your element instead. This way, sighted users will see the animated text, while visually impaired people will get the screenreader-only content announced to them.

加载中...

还原操作

Performance-wise, it can be expensive for browsers to render a lot of nodes/elements, so it's often a good idea to revert() your split elements to their original state when you're done animating them. Simply call revert() on the SplitText instance to restore the original innerHTML:

let split = SplitText.create(".split", {type: "words"});
gsap.from(split.words, {
x: "random(-100, 100)",
y: "random(-100, 100)",
stagger: 0.1,
onComplete: () => split.revert() // <-- restores original innerHTML
})

提示与限制

提示与限制
  • Characters shift slightly when splitting? - Some browsers apply kerning between certain characters which is lost when each character is put into its own element, thus the spacing shifts slightly. You can typically eliminate that shift by disabling the kerning with this CSS:

    font-kerning: none; 
    text-rendering: optimizeSpeed;
  • Custom Fonts - If you split before your web fonts are ready, the layout may shift or misalign. To avoid this, either:

    • Wait for the fonts to load before splitting by placing your code inside document.fonts.ready.then(() => {...your code here...}),或者
    • 设置autoSplit: true to have SplitText re-split once fonts finish loading. Don't forget to put your animation code inside the onSplit() callback!
  • 只拆分你需要的内容- 拆分成千上万个元素会消耗大量性能。如果你只需要动画化单词或行,请跳过字符拆分以获得更好的性能。

  • SEO- 如果你拆分了页面的主<h1/>元素,请确保你的页面有适当的标题和描述 meta 标签,并且你的 SplitText 已启用aria: "auto"(默认)功能。如果没有这些设置,你的拆分标题可能会以分段的形式出现在谷歌搜索结果中。

  • 避免使用 text-wrap: balance- 它会干扰干净的文本拆分。

  • SVG- SplitText 不适用于 SVG<text>节点。

  • 独立插件- SplitText 是 GSAP 插件中少数几个可以在不加载 GSAP 核心的情况下使用的插件之一。可以被使用不带加载 GSAP 的核心。

示例演示

查看完整系列的文本动画演示在 CodePen 上。