如何设置多行打字效果以响应行间延迟
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)
我在 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)