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

How to setup multiline typing effect for react with delay between lines

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

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

所以我正在寻找 ideas/suggestions 如何让它在继续下一行之前等待第一行完成输入?

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)