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

无需JavaScript实现动画暂停?多行打字机标点停顿方案咨询

多行打字机效果(带标点停顿)的优化方案

纯CSS实现(无JavaScript)

如果你的文本是固定不变的,可以用CSS动画结合关键帧手动控制停顿,完全不需要额外DOM元素。核心是利用steps()函数拆分动画进度,在标点对应的位置增加停留帧:

.typewriter {
  overflow: hidden;
  border-right: 2px solid #000;
  white-space: pre-wrap; /* 保留换行,支持多行 */
  width: 0;
  animation: type 8s steps(30, end) forwards;
}

@keyframes type {
  0% { width: 0; }
  /* 逗号在第5个字符位置,停留0.5s */
  20% { width: 5ch; }
  25% { width: 5ch; }
  /* 句号在第11个字符位置,停留0.5s */
  45% { width: 11ch; }
  50% { width: 11ch; }
  /* 最终展开所有文本 */
  100% { width: 30ch; }
}

HTML只需要一个容器:

<div class="typewriter">Hello, world. This is a test.
Another line here, with a pause at the comma.</div>

缺点是需要手动计算每个标点的字符位置和动画百分比,适合静态文本场景。

轻量JavaScript方案(简洁无冗余DOM)

如果文本是动态的,或者不想手动计算位置,用几行JS就能实现灵活控制,完全不需要大量span标签。思路是逐个字符追加文本,遇到标点时延长间隔时间:

<div class="typewriter" data-text="Hello, world. This is a test.
Another line here, with a pause at the comma."></div>
.typewriter {
  border-right: 2px solid #000;
  padding-right: 5px;
  white-space: pre-wrap; /* 支持多行换行 */
}
const el = document.querySelector('.typewriter');
const text = el.dataset.text;
let idx = 0;
const normalSpeed = 150; // 普通字符输入间隔
const pauseSpeed = 800; // 标点停顿间隔

function type() {
  if (idx < text.length) {
    el.textContent += text[idx];
    // 判断是否为需要停顿的标点
    const isPause = /[,.!?。,!?]/.test(text[idx]);
    setTimeout(type, isPause ? pauseSpeed : normalSpeed);
    idx++;
  } else {
    el.style.borderRight = 'none'; // 结束后移除光标
  }
}

type();

这个方案的优势:

  • 代码极简,无冗余DOM
  • 支持动态文本(可从data-text或接口获取)
  • 可自由调整输入速度和停顿时长
  • 自动处理多行文本

混合方案(CSS动画+JS控制,平衡流畅性与灵活性)

如果想要CSS动画的流畅过渡,同时避免大量span,可以将文本拆分为「普通字符段+标点」的组合,每个组合对应一个DOM元素,大幅减少节点数量:

<div class="typewriter"></div>
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.typewriter {
  border-right: 2px solid #000;
  padding-right: 5px;
  white-space: pre-wrap;
}
const el = document.querySelector('.typewriter');
const text = "Hello, world. This is a test.\nAnother line here, with a pause at the comma.";
// 拆分文本为普通段和标点的数组
const segments = text.split(/([,.!?。,!?])/).filter(s => s);

segments.forEach((seg, i) => {
  const span = document.createElement('span');
  span.textContent = seg;
  span.style.opacity = '0';
  span.style.animation = 'fadeIn 0.1s forwards';
  
  // 计算每个片段的动画延迟
  let delay = 0;
  for (let j = 0; j < i; j++) {
    delay += segments[j].length * 150;
    // 标点片段额外增加停顿时间
    if (/[,.!?。,!?]/.test(segments[j])) {
      delay += 650; // 总停顿800ms = 150ms(字符本身) + 650ms(额外停顿)
    }
  }
  span.style.animationDelay = `${delay}ms`;
  el.appendChild(span);
});

这种方案既保留了CSS动画的平滑,又避免了每个字符一个span的臃肿问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:45:33