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

JavaScript文本循环动画异常:重置索引后Bug修复咨询

循环打字/删除文本动画异常修复及优化方案

问题说明

需要实现循环动画:逐个字符添加文本 → 逐个字符删除文本 → 切换新文本,重复该流程。但在重置stackIndex和i并重启动画时,出现文本混乱、跳转异常的问题。

当前代码

HTML

<h1 id="title">Desenvolvedor </h1>

JavaScript

const title = document.getElementById("title");
const stacks = ["Front-End", "Back-End", "Full Stack"];
const speed = 200;
let stackIndex = 0;
let i = 0;

function addChar() {
  setTimeout(() => {
    title.innerHTML += stacks[stackIndex].charAt(i);
    i++;
    typeWriter(stackIndex);
  }, speed);
}

function deleteChar() {
  setTimeout(() => {
    title.innerHTML = title.innerHTML.slice(0, -1);
    
    if (title.innerText.length > 13) {
      deleteChar();
    } else {
      if (stackIndex > 1) {
        i = 0;
        stackIndex = 0;
        typeWriter(stackIndex);
      }

      i = 0;
      typeWriter(stackIndex++);
    }

  }, speed);
}

function typeWriter(stackIndex) {
  if (i < stacks[stackIndex].length) {
    addChar();
  } else {
    deleteChar();
  }
}

问题根源

  1. 异步定时器未清理:setTimeout是异步任务,重置索引时,之前的定时器回调仍在队列中等待执行,会干扰新的动画流程。
  2. 硬编码文本长度:判断title.innerText.length > 13依赖初始文本的固定长度,一旦初始文本修改就会出错。
  3. 逻辑分支错误:deleteChar中,无论是否重置stackIndex,都会执行stackIndex++,导致重置后立刻跳转到下一个索引,出现异常。

修复方案

const title = document.getElementById("title");
const stacks = ["Front-End", "Back-End", "Full Stack"];
const speed = 200;
const initialText = "Desenvolvedor "; // 保存初始文本,避免硬编码长度
let stackIndex = 0;
let i = 0;

// 封装延迟函数,简化异步流程
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function addChar() {
  while (i < stacks[stackIndex].length) {
    title.innerHTML += stacks[stackIndex].charAt(i);
    i++;
    await delay(speed);
  }
  deleteChar();
}

async function deleteChar() {
  while (title.innerText.length > initialText.length) {
    title.innerHTML = title.innerHTML.slice(0, -1);
    await delay(speed);
  }
  // 循环切换文本索引
  stackIndex = (stackIndex + 1) % stacks.length;
  i = 0;
  addChar();
}

// 启动动画
addChar();

修复要点

  • 用async/await替代嵌套定时器,逻辑更线性,避免回调地狱。
  • 保存初始文本长度,动态判断删除终止条件,摆脱硬编码限制。
  • 使用取余运算实现索引循环,无需手动判断边界,逻辑更简洁。

更简洁的可复用实现

可以封装为通用函数,支持自定义初始文本、文本列表和动画速度:

async function typewriterLoop(element, initialText, textList, speed = 200) {
  let currentIndex = 0;
  element.innerHTML = initialText;

  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

  while (true) {
    // 打字阶段
    const currentText = textList[currentIndex];
    for (const char of currentText) {
      element.innerHTML += char;
      await delay(speed);
    }
    // 打字完成后停顿(可选优化)
    await delay(speed * 3);
    // 删除阶段
    while (element.innerHTML.length > initialText.length) {
      element.innerHTML = element.innerHTML.slice(0, -1);
      await delay(speed);
    }
    // 切换到下一个文本
    currentIndex = (currentIndex + 1) % textList.length;
    await delay(speed);
  }
}

// 调用示例
typewriterLoop(
  document.getElementById("title"),
  "Desenvolvedor ",
  ["Front-End", "Back-End", "Full Stack"],
  200
);

这个版本模块化程度更高,便于维护和复用,同时加入了打字完成后的停顿,动画体验更自然。

内容的提问来源于stack exchange,提问作者vinícios

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 00:20:27