如何实现Stripe官网模拟真人打字运行代码的代码片段动画?
Stripe滚动触发动态代码效果实现方案
核心实现逻辑
这个效果本质是滚动触发的逐字打字动画+运行态模拟,核心可以拆成三个模块实现:
- 滚动触发判定:用IntersectionObserver监听代码容器的可见性,当容器进入视口比例达到预设值(比如30%)时才启动动画,既符合滚动展示的交互逻辑,性能也远优于传统的scroll事件监听,还可以避免动画重复执行。
- 真人输入模拟:提前把要展示的代码拆分为逐行的序列,逐字符插入到代码容器中,每次插入的间隔设为30~80ms的随机值,不要用固定间隔,能最大程度还原真人打字的错落感,再搭配CSS动画实现的闪烁光标,效果更逼真。
- 运行效果模拟:某一行输入代码打完后,隐藏光标,插入100~1000ms不等的等待时长,也可以加个小点跳动的加载动效,延迟结束后直接输出运行结果(比如返回值、控制台日志等),再恢复光标继续下一行的打字即可。
关键代码示例
HTML结构
<div class="code-card"> <pre><code class="code-content"></code></pre> <span class="typing-cursor"></span> </div>
核心CSS
.typing-cursor { display: inline-block; width: 2px; height: 1em; background: #ffffff; margin-left: 3px; vertical-align: text-bottom; animation: blink 1s step-end infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
JS逻辑
// 提前定义代码序列,type区分是输入代码还是运行输出 const codeList = [ { type: 'input', content: "import Stripe from 'stripe';" }, { type: 'input', content: "const stripe = new Stripe('sk_test_xxxxxxxxxx');" }, { type: 'input', content: "const paymentIntent = await stripe.paymentIntents.create({" }, { type: 'input', content: " amount: 1999," }, { type: 'input', content: " currency: 'cny'," }, { type: 'input', content: " payment_method_types: ['card']" }, { type: 'input', content: "});" }, { type: 'output', content: "// ✅ 请求成功,PaymentIntent ID:pi_3KDxxxxxxxxx" } ] const codeEl = document.querySelector('.code-content') const cursorEl = document.querySelector('.typing-cursor') // 逐字打印函数 async function typeSingleLine(text) { for (let char of text) { codeEl.textContent += char // 随机间隔模拟真人打字 await new Promise(resolve => setTimeout(resolve, Math.random() * 50 + 30)) } codeEl.textContent += '\n' } // 启动完整动画 async function runCodeAnimation() { for (let item of codeList) { if (item.type === 'input') { cursorEl.style.display = 'inline-block' await typeSingleLine(item.content) } else if (item.type === 'output') { cursorEl.style.display = 'none' // 模拟代码运行等待时间 await new Promise(resolve => setTimeout(resolve, 800)) codeEl.textContent += item.content + '\n' } } } // 滚动监听触发动画 const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { runCodeAnimation() // 触发一次后取消监听,避免重复执行 observer.unobserve(entry.target) } }) }, { threshold: 0.3 }) observer.observe(document.querySelector('.code-card'))
可优化方向
- 可以接入语法高亮库,每打完一行代码就调用一次单行高亮方法,不用等全部代码打完再处理,展示效果更贴近真实编辑器。
- 如果需要做多语言/多场景的代码标签切换,只需要提前定义多组codeList,点击对应标签时清空容器内容,再执行对应序列的动画即可。
- 动画执行过程中尽量直接操作textContent,不要频繁插入DOM节点,能减少不必要的重排重绘,提升流畅度。
内容的提问来源于stack exchange,提问作者ROHIT K F
相关产品推荐
相关产品推荐

