mcpField
S
staggered-word-reveal

staggered-word-reveal

github.com/mengto设计与 UI7.1k1.6k 次安装3 天前更新
代码仓库

关于 staggered-word-reveal

用于文字进入视口时逐词显现的前端设计技能,提供克制且仅播放一次的动画实现指导,并考虑无障碍与减少动态效果设置。

主要功能

  1. 01逐词拆分与显现
  2. 02进入视口时触发一次
  3. 03CSS 与 JavaScript 实现
  4. 04屏幕阅读器与减少动态效果指导

适用场景

  1. 01滚动时动画呈现短标题
  2. 02逐词显示引言
  3. 03为标签添加克制动效

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mengto/skills --skill staggered-word-reveal

安装后在 Claude Code 中输入/staggered-word-reveal确认已加载。

附带文件demo/index.htmldemo/PROMPT.md

详细介绍

错落式逐词显现

适用场景

  • 短标题、引言或引用语需要逐词显现。
  • 动效应显得有编辑感、高级且克制。
  • 文本进入视口时,动效只触发一次。
  • 项目不需要 GSAP SplitText 的复杂行为。

动效默认值

  • 初始状态:opacity: 0、transform: translateY(20px)。
  • 最终状态:opacity: 1、transform: translateY(0)。
  • 时长:0.8s。
  • 缓动:cubic-bezier(0.16, 1, 0.3, 1)。
  • 错落间隔:每个词 0.06s 到 0.08s。默认使用 0.07s。
  • 触发时机:元素约有 20% 可见时开始,并略微偏向视口下方。
  • 重播:仅一次。

HTML

<h1 class="word-reveal" data-word-reveal>
  Build interfaces that feel calm, cinematic, and alive.
</h1>

CSS

确保无 JS 时内容仍可见。仅在 JavaScript 启用且文本拆分前隐藏内容。

.word-reveal {
  visibility: visible;
}

html.js .word-reveal[data-word-reveal]:not(.is-ready) {
  opacity: 0;
}

.word-reveal__word {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition:
    opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--word-index) * 0.07s);
  will-change: opacity, transform;
}

.word-reveal.is-visible .word-reveal__word {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

@media (prefers-reduced-motion: reduce) {
  html.js .word-reveal[data-word-reveal]:not(.is-ready),
  .word-reveal__word {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

JavaScript

此拆分器会保留空格、避免使用 innerHTML、向屏幕阅读器提供原始句子,并在首次显现后取消观察。

document.documentElement.classList.add("js");

function splitWordReveal(element) {
  if (element.dataset.wordRevealReady === "true") return;

  const text = element.textContent || "";
  const parts = text.split(/(\s+)/);
  let wordIndex = 0;

  element.textContent = "";
  element.setAttribute("aria-label", text.trim());

  parts.forEach((part) => {
    if (!part.trim()) {
      element.appendChild(document.createTextNode(part));
      return;
    }

    const word = document.createElement("span");
    word.className = "word-reveal__word";
    word.setAttribute("aria-hidden", "true");
    word.style.setProperty("--word-index", wordIndex);
    word.textContent = part;

    element.appendChild(word);
    wordIndex += 1;
  });

  element.dataset.wordRevealReady = "true";
  element.classList.add("is-ready");
}

function initWordReveals(selector = "[data-word-reveal]") {
  const elements = Array.from(document.querySelectorAll(selector));
  const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

  if (reduceMotion || !("IntersectionObserver" in window)) {
    elements.forEach((element) => {
      element.classList.add("is-ready", "is-visible");
    });
    return;
  }

  const observer = new IntersectionObserver(
    (entries, io) => {
      entries.forEach((entry) => {
        if (!entry.isIntersecting) return;

        entry.target.classList.add("is-visible");
        io.unobserve(entry.target);
      });
    },
    {
      threshold: 0.2,
      rootMargin: "0px 0px -10% 0px",
    }
  );

  elements.forEach((element) => {
    splitWordReveal(element);
    observer.observe(element);
  });
}

document.addEventListener("DOMContentLoaded", () => {
  initWordReveals();
});

框架说明

  • React/Vue/Svelte:在挂载后运行拆分器,并在路由更改时清理观察器实例。
  • Framer Motion:保持相同参数:y: 20、opacity: 0、时长 0.8、缓动 [0.16, 1, 0.3, 1]、错落间隔 0.06 到 0.08、once: true。
  • GSAP:使用 fromTo(words, { y: 20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.8, ease: "expo.out", stagger: 0.07 })。

风格准则

  • 用于短文本:标题、副标题、标签和引用语。避免用于长段落。
  • 按词而非按字母错落显现,营造更平和、高级的观感。
  • 位移保持轻微。不要添加弹跳、旋转或大范围模糊。
  • 仅为 transform 和 opacity 添加动画。
  • 不要拆分包含链接、按钮或有意义的行内标记的文本。
  • 如果换行效果很重要,请在网页字体加载完成后初始化。

快速检查

  • 禁用 JavaScript 时,文本仍然可见。
  • 各词从 translateY(20px) 和 opacity: 0 状态开始。
  • 每个词仅显现一次,间隔为 0.06s 到 0.08s。
  • 重复滚动不会重播动画。
  • 减少动态效果的用户会看到静态且易读的文本。

评论

支持 Markdown