无需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
相关产品推荐
相关产品推荐

