滚动文字显现

随着滚动,文字一个接一个亮起来,让读者按照自己滚动的节奏读完一句话。

  • GSAP
  • 文字
  • 滚动
  • 收录于
向下滚动 ↓

AI 提示词

用 GSAP ScrollTrigger 制作随滚动擦洗的文字显现效果。把段落拆成以词为单位的 span(没有空格的语言按字拆分),初始 opacity 为 0.15。注册 ScrollTrigger,把这些 span 补间到 opacity 1,stagger 0.1,ease "none",scrollTrigger 设置为 { trigger: 段落, start: "top 80%", end: "bottom 45%", scrub: true },让每个字按阅读顺序随滚动变亮,往回滚动时再变暗。如果文字位于一个可滚动的面板中而不是页面上,把该元素作为 scroller 传入。使用大号展示字体。把完整句子放在段落的 aria-label 中,并对屏幕阅读器隐藏这些 span。在 prefers-reduced-motion 下所有文字直接完全显示。组件卸载时 revert GSAP context。文字为“动效应该说明发生了什么,引导视线,然后安静退场。”

源代码

Demo.tsx
'use client'

import { useEffect, useRef } from 'react'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import s from './demo.module.css'

gsap.registerPlugin(ScrollTrigger)

export default function ScrollTextReveal({ labels }: { labels: Record<string, string> }) {
  const frame = useRef<HTMLDivElement>(null)
  const text = useRef<HTMLParagraphElement>(null)
  const spaced = labels.text.includes(' ')
  const words = spaced ? labels.text.split(' ') : Array.from(labels.text)

  useEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
    const ctx = gsap.context(() => {
      gsap.fromTo(
        `.${s.word}`,
        { opacity: 0.15 },
        {
          opacity: 1,
          stagger: 0.1,
          ease: 'none',
          scrollTrigger: { trigger: text.current, scroller: frame.current, start: 'top 80%', end: 'bottom 45%', scrub: true },
        },
      )
    }, frame)
    return () => ctx.revert()
  }, [labels.text])

  return (
    <div ref={frame} className={s.frame} tabIndex={0}>
      <span className={s.hint}>{labels.hint} ↓</span>
      <p ref={text} className={s.text} aria-label={labels.text}>
        {words.map((w, i) => (
          <span key={i} className={s.word} aria-hidden="true">
            {w}
            {spaced && i < words.length - 1 ? ' ' : ''}
          </span>
        ))}
      </p>
    </div>
  )
}
demo.module.css
.frame {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

.frame:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -4px; }

.hint {
  display: block;
  font: 500 var(--text-xs) / 1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--stage-muted);
}

.frame { padding: 24px 24px 0; }

.text {
  max-width: 300px;
  margin: 220px auto 200px;
  font: 600 var(--text-xl) / 1.35 var(--font-display);
  color: var(--stage-ink);
}

.word { display: inline; }

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。