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

JavaScript实现逐字滚动文本效果遇阻,请求技术支援

问题描述

我想要实现这样的效果:在输入框输入内容并点击Go按钮后,输入内容会在runningWords容器中逐字显示,每个字符需从alphabets数组的元素依次滚动,匹配到目标字符后停止,再进行下一个字符的滚动(比如输入“test”时,先滚动到“t”停止,再滚动到“e”停止,以此类推)。

我编写的代码如下:

HTML代码

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Project</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <div class="runningWords"></div>
    <div class="inputs">
      <input type="text" name="text" id="textInput" value="test" placeholder="Enter String...">
      <button onclick="handleClick()">Go...</button>
    </div>
  </div>

  <script src="main.js"></script>
</body>
</html>

CSS代码

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

* {
  margin: 0;
  padding: 0;
}

body {
  background: #333;
  font-family: 'Poppins', sans-serif;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
  position: relative;
}

.container .inputs {
  display: flex;
  flex-direction: column;
}

.container .inputs input {
  width: 30em;
  height: 3em;
  margin-bottom: 1em;
  border-radius: 0.5em;
  padding-left: 1em;
  border: 0.25em solid transparent;
  transition: all 300ms ease-in-out;
}

.container .inputs input:hover,
.container .inputs input:focus {
  outline: none;
  border: 0.25em solid rgb(0, 140, 255);
}

.container .inputs button {
  width: 7em;
  height: 2.5em;
  margin: auto;
  border: 1px solid rgb(0, 140, 255);
  background-color: #333;
  color: rgb(0, 140, 255);
  border-radius: 0.5em;
  transition: all 300ms ease-in-out;
  cursor: pointer;
}

.container .inputs button:hover {
  background-color: rgb(0, 140, 255);
  color: #333;
}

.container .runningWords {
  position: absolute;
  top: 25%;
  width: 60%;
  margin: 0 20%;
  height: 80px;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  color: rgb(0, 140, 255);
  font-size: 3em;
  font-weight: 700;
}

JavaScript代码

const alphabets = [' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

const handleClick = async () => {
  let inputVal = document.getElementById('textInput').value.toLowerCase().split('')

  if (inputVal.length <= 0) return alert('Please Type something!')

  for (val in inputVal) {
    if (alphabets.includes(inputVal[val]) === false) return alert('Use only letters and space')
  }

  let inLen = inputVal.length
  let echTxt = 0
  let runningWordsDiv = document.getElementsByClassName('runningWords')[0]

  do {
    console.log(echTxt)
    for (let letter = 0; letter <= alphabets.length; letter++) {
      setTimeout(() => {
        if (alphabets[letter] === undefined) return
        runningWordsDiv.innerHTML = alphabets[letter].toUpperCase()
      }, 200 * letter);
    }
    echTxt++
  }
  while (echTxt < inLen)
}

当前代码存在的问题:console.log(echTxt)会瞬间执行到输入内容的长度,之后才开始向runningWords容器中更新内容,无法实现预期的逐字滚动效果。


解决方案

问题根源

setTimeout是异步任务,会被放入事件队列等待执行,而同步的do...while循环会瞬间跑完所有迭代,所有setTimeout任务会在循环结束后才开始依次执行,导致所有字符的滚动任务被同时触发,无法按顺序逐个完成。

修改后的JavaScript代码

const alphabets = [' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

// 封装单个字符的滚动逻辑为异步函数
async function scrollToTargetChar(targetChar, displayEl) {
  for (let letter of alphabets) {
    displayEl.innerHTML = letter.toUpperCase();
    // 等待200ms再进行下一次滚动
    await new Promise(resolve => setTimeout(resolve, 200));
    // 匹配到目标字符则停止当前字符的滚动
    if (letter === targetChar) break;
  }
}

const handleClick = async () => {
  let inputVal = document.getElementById('textInput').value.toLowerCase().split('')

  if (inputVal.length <= 0) return alert('Please Type something!')

  for (let val of inputVal) {
    if (!alphabets.includes(val)) return alert('Use only letters and space')
  }

  let runningWordsDiv = document.getElementsByClassName('runningWords')[0]

  // 逐个处理输入字符,等待当前字符滚动完成再处理下一个
  for (let char of inputVal) {
    await scrollToTargetChar(char, runningWordsDiv);
  }
}

改动说明

  1. 将单个字符的滚动逻辑封装成scrollToTargetChar异步函数,用await Promise实现每一步滚动的等待,确保每个字符滚动到目标后再继续。
  2. 主函数中用for...of循环遍历输入字符,每个字符都等待scrollToTargetChar完成后再处理下一个,实现逐字滚动的顺序执行。
  3. 替换原有的for...in循环(不适合遍历数组),改用for...of更安全规范。
  4. 移除原有的do...while和批量触发setTimeout的逻辑,避免异步任务堆积导致的顺序混乱。

修改后即可实现先滚动到第一个目标字符停止,再滚动到第二个目标字符的预期效果。

内容的提问来源于Stack Exchange,提问作者RANTO BERK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:10:50