You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于滚动的文本颜色动画:如何实现逐行而非整块动画效果?

解决逐行滚动文本动画的严格序列问题

我明白你想要的效果——严格按照阅读顺序,逐行从左到右完成变色动画,上一行完全结束后下一行才开始,而不是当前实现中可能出现的多行同时触发或者动画不同步的问题。咱们来调整代码逻辑,完美复刻目标效果:

问题根源

  1. 文本行未响应窗口resize:当前代码只在DOM加载时拆分文本为行,窗口大小变化时不会重新划分,导致行结构错误,动画逻辑失效。
  2. 进度映射不够精准:虽然你的进度计算逻辑理论上是逐行,但可能因为元素与视口的高度比例,导致动画进度推进过快,看起来像是多行同时动。

修改后的完整代码

HTML(保持不变)

<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>

CSS(微调优化)

.quote-text {
  max-width: 800px; /* 可根据需求调整,确保文本换行符合预期 */
  margin: 100vh auto; /* 增加上下margin,让滚动时有足够空间展示动画 */
  font-size: 1.5rem;
  line-height: 1.6;
}

.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;
  background-clip: text; /* 增加标准属性,提升浏览器兼容性 */
}

JavaScript(核心逻辑调整)

document.addEventListener("DOMContentLoaded", () => {
  const quote = document.querySelector(".quote-text");
  if (!quote) return;

  // 封装文本拆分行的函数,方便重复调用
  function splitTextIntoLines() {
    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;
      }
    });
  }

  // 初始化拆分文本
  splitTextIntoLines();
  let lines = quote.querySelectorAll(".line");

  function updateProgress() {
    const rect = quote.getBoundingClientRect();
    const windowHeight = window.innerHeight;
    // 调整进度计算:元素顶部进入视口时progress=0,元素底部离开视口时progress=1
    let progress = (windowHeight - rect.top) / (windowHeight + rect.height);
    progress = Math.min(Math.max(progress, 0), 1);
    const totalLines = lines.length;

    lines.forEach((line, i) => {
      // 为每一行分配独立的进度区间,确保严格顺序
      const lineStartProgress = i / totalLines;
      const lineEndProgress = (i + 1) / totalLines;

      if (progress <= lineStartProgress) {
        // 未进入当前行动画区间,进度为0
        line.style.setProperty("--progress", "0%");
      } else if (progress >= lineEndProgress) {
        // 已完成当前行动画,进度为100%
        line.style.setProperty("--progress", "100%");
      } else {
        // 在当前行动画区间内,计算相对进度
        const lineProgress = (progress - lineStartProgress) / (lineEndProgress - lineStartProgress);
        line.style.setProperty("--progress", `${lineProgress * 100}%`);
      }
    });
  }

  window.addEventListener("scroll", updateProgress);
  window.addEventListener("resize", () => {
    // resize时重新拆分文本并更新行列表
    splitTextIntoLines();
    lines = quote.querySelectorAll(".line");
    updateProgress();
  });

  // 初始化进度
  updateProgress();
});

关键调整说明

  1. 响应式文本行拆分:把拆分行的逻辑封装成函数,在resize时重新执行,确保窗口大小变化后行结构始终正确。
  2. 精准的进度区间映射:每一行分配一个独立的进度区间(比如3行的话,分别对应总进度的0-33%、33%-66%、66%-100%),只有当滚动进度进入该行的区间时,才会触发该行的动画,上一行必须完全完成后下一行才开始。
  3. 优化CSS体验:增加了margin: 100vh auto,让元素在页面中有足够的滚动空间,方便观察动画效果;同时添加标准的background-clip: text属性,提升浏览器兼容性。

这样调整后,就能实现你想要的:同一时间只有一行动画,每行从左到右填充变色,下一行仅在上一行完成后开始的效果。

内容的提问来源于stack exchange,提问作者newuser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:38:35