javascript - 如何设置多行打字效果以响应行间延迟

标签 javascript arrays reactjs typescript settimeout

我在 React 中创建了一个基于类的组件,它在加载时启动多行输入效果。

它工作正常,但问题是两行同时开始输入,考虑到 setTimout 执行方式的性质,这是有道理的。

所以我正在寻找关于如何让它等待第一行完成输入后再继续下一行的任何想法/建议?

import React from 'react'
import TerminalCursor from 'icons/TerminalCursor'
import TerminalPrompt from 'icons/TerminalPrompt'
import { v4 as uuidv4 } from 'uuid'

type MyProps = {}
type MyState = {
  introArray: string[]
}
class Terminal extends React.Component<MyProps, MyState> {
  state: MyState = {
    introArray: [],
  }

  componentDidMount() {
    // Data to import from sanity
    const starterArray = [
      'The very first line that needs to finish typing before going to the next line',
      'The second line that needs to start being typed after the first line',
    ]

    const createTypingEffect = (text: string, index: number) => {
      for (let i = 0; i < text.length; i++) {
        setTimeout(() => {
          let arrayCopy = this.state.introArray.slice()
          this.setState((state) => ({
            introArray: [...state.introArray.slice(0, index), arrayCopy[index] + text[i], ...state.introArray.slice(index + 1)],
          }))
        }, 100 * i)
      }
    }
    starterArray.forEach((starterText, starterIndex) => {
      // Setting empty string for each line in starterArray so we dont get undefined as first character
      this.setState((state) => ({
        introArray: [...state.introArray, ''],
      }))

      // Need to wait for first line to finish typing before starting the second line
      createTypingEffect(starterText, starterIndex)
    })
  }
  render() {
    return (
      <div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
        <div className="flex flex-col">
          {this.state.introArray.map((introLine) => (
            <div className="flex items-center" key={uuidv4()}>
              <TerminalPrompt />
              <p className="text-white">{introLine}</p>
            </div>
          ))}
          {/* Actual prompt starts here */}
          <div className="flex">
            <TerminalPrompt />
            <TerminalCursor />
          </div>
        </div>
      </div>
    )
  }
}

export default Terminal

这是一个沙盒演示(不知道为什么有些字符在沙盒中重复,本地没有这个问题......)

https://codesandbox.io/s/recursing-star-iohjw9?file=/src/Terminal.tsx

最佳答案

这种任务你需要使用异步方式(我没有检查你的打字效果逻辑,我只是​​让整个过程异步):

import React from "react";

type MyProps = {};
type MyState = {
  introArray: string[];
};
class Terminal extends React.Component<MyProps, MyState> {
  state: MyState = {
    introArray: []
  };

  componentDidMount() {
    // Data to import from sanity
    const starterArray = [
      "The very first line that needs to finish typing before going to the next line",
      "The second line that needs to start being typed after the first line"
    ];

    const createTypingEffect = async (text: string, index: number) => {
      return Promise.all(
        text.split("").map(
          (c, i) =>
            new Promise((res) => {
              setTimeout(() => {
                let arrayCopy = this.state.introArray.slice();
                this.setState((state) => ({
                  introArray: [
                    ...state.introArray.slice(0, index),
                    arrayCopy[index] + c,
                    ...state.introArray.slice(index + 1)
                  ]
                }));
                res(null);
              }, 100 * i);
            })
        )
      );
    };

    const cycle = async () => {
      let i = 0;
      for (const starterText of starterArray) {
        // Setting empty string for each line in starterArray so we dont get undefined as first character
        this.setState((state) => ({
          introArray: [...state.introArray, ""]
        }));

        await createTypingEffect(starterText, i);
        i++;
      }
    };

    cycle();
  }
  render() {
    console.log(this.state.introArray);
    return (
      <div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
        <div className="flex flex-col">
          {this.state.introArray.map((introLine, index) => (
            <div className="flex items-center" key={index}>
              <p className="text-white">{introLine}</p>
            </div>
          ))}
        </div>
      </div>
    );
  }
}

export default Terminal;

您可以使用多种方法来处理异步逻辑,我在这里使用了 Promise.all 方法,为超时提供了一个增量计时器,以跟随您的操作。另一种解决方案是使用简单的 for...of 循环并等待内部的promisified 超时。这样您就不需要为超时提供增量时间,因为它们将相对同步执行。

工作演示是 HERE (无法在此处添加工作片段,因为 SO 不支持 TS)

关于javascript - 如何设置多行打字效果以响应行间延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72467752/

相关文章:

arrays - 查找节点在数组中的位置(作为二进制堆)

arrays - 如何使用 Swift 显示数组的唯一元素?

c++ - 删除和移动数组 C++ 中的剩余元素

javascript - React-Google-Maps 在容器 div 调整大小时显示灰色区域

javascript - 重定向完成后如何调度操作(Redux)?

javascript - 调用函数时如何添加参数?

javascript - 如何格式化数组中的多个 JSON 对象

javascript - 使用下面两个定义的方法的区别

javascript - 私有(private) javascript 小部件

JavaScript 对象和 getter 属性