拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue 打字特效进阶:用 setInterval 动态变速 + CSS 闪烁光标实现多行文本模拟

Vue 打字特效进阶:用 setInterval 动态变速 + CSS 闪烁光标实现多行文本模拟 1. 为什么原生 setInterval 打字机看起来像机器人做 Vue 项目时产品经常提一个需求首页那句 slogan 要像真人在敲键盘一个字一个字蹦出来偶尔快偶尔慢光标还要一闪一闪。很多人第一反应是写个setInterval固定 100ms 往外吐字符结果上线后被吐槽「像打印机不像人」。问题出在固定间隔上。真人打字有节奏起伏想词的时候停顿长熟悉的词连打很快标点后还会顿一下。固定setInterval(fn, 100)每个字符间隔完全一致视觉上就是机械匀速缺少「呼吸感」。另一个坑是多行文本用\n换行时如果容器没设white-space: pre-wrap换行符会被浏览器吞掉所有文字挤成一行光标也跟着乱跑。这篇就围绕 Vue 3 组件把三件事讲透用setInterval动态改变时间间隔模拟随机变速、用 CSSkeyframes做闪烁光标、用pre-wrap保住多行换行。核心检索词先摆出来Vue 打字特效、setInterval 动态变速、CSS 闪烁光标、多行文本逐字输出。适合正在做官网首页、登录页标语、AI 对话打字效果的 Vue 开发者跟着做就能跑起来。我试过把间隔写死成随机数但忘了先clearInterval结果定时器越叠越多字符成片往外冒这个坑后面排障章节会专门讲。2. 前置准备Vue 项目与 TaoToken 接入位组件本身不依赖任何第三方库Vue 3 的script setup或 Options API 都能写。如果你只是想要打字特效直接跳到第 3 节复制组件即可。但很多同学做这个特效是为了配合 AI 对话场景——打字机效果展示大模型流式返回。这时候你需要一个稳定的模型调用入口。TaoToken 提供统一的模型 API 接入官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。它的作用是让你用一套 Key 调用多家模型省去分别对接的麻烦。适合需要在前端做打字机展示、后端做流式转发的场景。接入前先去控制台创建密钥入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 密钥管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到 Key 后接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言的请求示例。想先验证模型通不通可以直接用模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 试一句。注意本篇的打字特效是纯前端渲染逻辑和模型调用是两层。模型负责产出文本打字机负责把文本「演」出来。两者解耦你换任何文本源都不影响特效。如果你打算长期做编码类 Agent 或批量生成可以了解 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 这里不展开重点还是回到组件本身。3. 可复制配置Vue 组件骨架 动态变速 闪烁光标先给完整组件再逐段拆解。新建TypewriterText.vuetemplate div classcontentBox span{{ showContent }}/span span v-ifshowBlinkCursor classblinkCursor|/span /div /template script setup import { ref, onMounted, onBeforeUnmount } from vue const props defineProps({ text: { type: String, default: }, minDelay: { type: Number, default: 60 }, maxDelay: { type: Number, default: 320 } }) const showContent ref() const showBlinkCursor ref(true) let timer null let index 0 const typeNext () { clearInterval(timer) if (index props.text.length) { const delay Math.random() * (props.maxDelay - props.minDelay) props.minDelay showContent.value props.text[index] index timer setInterval(typeNext, delay) } else { showBlinkCursor.value false } } onMounted(() { timer setInterval(typeNext, props.minDelay) }) onBeforeUnmount(() { clearInterval(timer) }) /script style scoped .contentBox { white-space: pre-wrap; font-size: 18px; line-height: 1.8; color: #333; } .blinkCursor { display: inline-block; margin-left: 2px; animation: Blink 1s ease-in 0s infinite; } keyframes Blink { 0% { opacity: 0; } 100% { opacity: 1; } } /style父组件这样用template TypewriterText :textpoem :min-delay60 :max-delay320 / /template script setup import TypewriterText from ./TypewriterText.vue const poem 多想一醒来 晨风就能抚平忧伤 多想一转身 目光就融化掉冰霜 多想一抬头 看见你微笑的脸庞 生活总该迎着光亮 再跌跌撞撞也要坚强 /script关键点逐个说。white-space: pre-wrap是保住\n换行的核心没有它多行文本会塌成一行。Math.random() * (maxDelay - minDelay) minDelay把随机间隔限制在 60 到 320ms 之间避免出现 0ms 疯狂输出或 500ms 卡顿感。每次进入typeNext先clearInterval(timer)再重建这是动态改变间隔的唯一正确姿势——setInterval一旦创建间隔就固定了想变只能清掉重来。光标用v-if控制打字结束后隐藏符合真人输入完移开光标的直觉。keyframes Blink用opacity从 0 到 1 循环ease-in让闪烁不那么生硬。4. 验证请求浏览器里确认多行、变速与光标组件挂载后打开浏览器开发者工具按下面几步验证。第一步看多行。把poem换成带\n的多行字符串刷新页面。如果所有文字挤在一行检查.contentBox的white-space是否生效Scoped 样式有没有被父级覆盖。正常效果是每行独立换行光标跟在最后一个字符后面。第二步看变速。在typeNext里临时加一行console.log(delay)观察控制台输出的间隔值是否在 60 到 320 之间跳动。你会看到数字忽大忽小对应视觉上就是打字忽快忽慢。如果所有值都一样说明Math.random()没生效检查是不是把 delay 写成了常量。第三步看光标。打字过程中光标持续闪烁最后一个字符输出后光标消失。如果光标一直不消失检查index props.text.length的判断可能是text长度算错了。如果光标不闪检查animation有没有被其他样式覆盖或者keyframes名字拼写。第四步看内存。反复切换路由挂载卸载组件在 Performance 面板看定时器有没有泄漏。onBeforeUnmount里的clearInterval就是防这个的漏了它定时器会在组件销毁后继续跑。如果你要把这个特效接到真实模型输出上可以先用模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 生成一段多行文本复制到poem里测试换行和变速。确认前端渲染没问题后再走 API 接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 把流式返回接进来。5. 本篇常见错排查定时器叠加导致字符成片冒出。最常见。原因是在setInterval回调里又调了一次setInterval却没清旧的。记住铁律每次改间隔前先clearInterval(timer)。上面组件里typeNext第一行就是它。多行文本不换行。90% 是white-space没设或设成了normal。pre-wrap保留换行和空格pre-line只保留换行不保留连续空格按需选。Scoped 样式下如果父组件有white-space: nowrap会继承下来需要显式覆盖。光标闪烁和打字不同步。光标是独立 CSS 动画和 JS 定时器本来就不同步这是正常的。如果你希望光标在打字瞬间「亮」一下可以在typeNext里给光标加个短暂的 class 切换但通常没必要独立闪烁更自然。随机间隔范围设错。如果maxDelay小于minDelayMath.random() * (max - min) min会算出负数setInterval收到负数按 0 处理字符瞬间全出。确保maxDelay minDelay。组件销毁后定时器还在跑。报错Cannot read property of null或者内存持续增长。补上onBeforeUnmount(() clearInterval(timer))。中文和英文混排时节奏怪。中文一个字一个间隔没问题英文单词逐字母打会显得碎。可以在typeNext里判断当前字符遇到空格或标点时把 delay 调大一点模拟真人停顿。6. 把特效接到真实业务里的下一步打字特效本身不复杂难的是和真实数据流配合。如果你做的是 AI 对话界面模型返回是流式的你不需要自己逐字定时直接把流式 chunk 追加到showContent即可光标逻辑照搬。这时候前端打字机退化成「渲染层」真正的节奏由模型吐字速度决定。如果你要做的是固定文案的官网动效就用本篇的setInterval动态变速方案可控性最强。想调更细腻的节奏可以把minDelay/maxDelay做成 props不同段落传不同值标题慢一点、正文快一点。需要统一模型入口时密钥在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 管理接入细节看 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。长期做编码 Agent 的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 可以了解下额度方案。最后留个实用技巧把typeNext里的随机逻辑抽成一个getDelay(char)函数遇到。返回更大的值遇到字母返回更小的值打出来的节奏会比纯随机更像真人。这个改动只有几行效果提升明显你可以直接在当前组件上试。
返回列表