基于滚动的文本颜色动画——如何实现逐行动画,而不是对整个区块一次性进行动画?

前端开发 2026-07-11

我在尝试重现SXSW网站上使用的基于滚动的文本动画,当用户滚动时文本会从灰色过渡到黑色。

我想要的效果:
  • 文本应逐行呈现动画效果
  • 每行应从左到右逐步填充
  • 各行应按顺序逐个触发动画(如同阅读时的节奏)
    (第一行完成后 → 第二行再开始 → 依此类推。)
问题:

我的当前实现要么:
- 同时对所有行进行动画,或者
- 以垂直方向(从上到下)进行动画,但不能按行逐个正确地实现

我办不到实现“每次只有一行在动”的效果。
期望的行为:
- 仅有一行在动画(类似于 https://sxsw.com/ 的效果——sxsw的 850+部分)
- 每一行从左到右逐步填充
- 下一行只有在前一行完成后才开始

下面是我的代码:
document.addEventListener("DOMContentLoaded", () => {
  const quote = document.querySelector(".quote-text");
  if (!quote) return;

  // ----------------------------
  // STEP 1: Split text into lines
  // ----------------------------
  const text = quote.innerText.trim();
  quote.innerHTML = "";

  const words = text.split(" ");
  let lineSpan = document.createElement("span");
  lineSpan.className = "line";
  quote.appendChild(lineSpan);

  let lineText = "";

  words.forEach((word) => {
    const testLine = lineText + word + " ";
    lineSpan.textContent = testLine;

    if (lineSpan.scrollWidth > quote.clientWidth) {
      lineText = word + " ";
      lineSpan = document.createElement("span");
      lineSpan.className = "line";
      lineSpan.textContent = lineText;
      quote.appendChild(lineSpan);
    } else {
      lineText = testLine;
    }
  });

  const lines = quote.querySelectorAll(".line");

  // ----------------------------
  // STEP 2: Scroll animation (STRICT SEQUENCE)
  // ----------------------------
  function updateProgress() {
    const rect = quote.getBoundingClientRect();
    const windowHeight = window.innerHeight;

    let progress =
      (windowHeight - rect.top) / (windowHeight + rect.height);

    progress = Math.min(Math.max(progress, 0), 1);

    const totalLines = lines.length;

    // 👉 Determine which line is active
    const currentLineIndex = Math.floor(progress * totalLines);

    lines.forEach((line, i) => {
      if (i < currentLineIndex) {
        // fully completed lines
        line.style.setProperty("--progress", "100%");
      } else if (i === currentLineIndex) {
        // active line (animate left → right)
        const lineProgress = (progress * totalLines) - i;
        line.style.setProperty("--progress", `${lineProgress * 100}%`);
      } else {
        // not started yet
        line.style.setProperty("--progress", "0%");
      }
    });
  }

  window.addEventListener("scroll", updateProgress);
  window.addEventListener("resize", updateProgress);
  updateProgress();
});
.quote-text .line {
  display: block;

  --progress: 0%;

  background: linear-gradient(
    to right,
    #000 var(--progress),
    #bbb var(--progress)
  );

  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
<section> <blockquote class="quote-text"> Community banks play a critical role in expanding access to financial services and supporting the economic vitality of the communities they serve. Ongoing education for directors and senior management strengthens board oversight and promotes effective governance. </blockquote> </section>

解决方案

看看他们的HTML。可见文本的每个单词都放在一个独立的 <div> 标签中。也请检查CSS样式。我敢打赌,JavaScript会检测每个DIV元素是否进入视口,如果进入就设置它的不透明度。每个DIV也是 display: inline-block

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章