reactjs React中的打字机效果

70gysomp  于 2023-05-06  发布在  React
关注(0)|答案(4)|浏览(426)

我正在尝试在React组件中创建打字效果。我传入一个字符串数组,并尝试在某个时间间隔后逐个字符地呈现它们(使用setTimeout)。下面是我的代码

state = {
    typeColor: {
      color: "blue"
    },
    typed: ""
  };

  componentDidMount() {
    this.props.typewriter.map(string => {
      console.log("1", string);
      return this.stringChecker(string);
    });
  }

  typeWriter(string) {
    if (string.length === 0) {
      return;
    }
    console.log("3", string);
    this.setState((state, props) => ({ typed: state.typed.concat(string[0]) }));
    console.log(this.state.typed);

    setTimeout(() => this.typeWriter(string.slice(1)), 120);
  }
  stringChecker(string) {
    console.log("2", string);
    if (string === "Qurram") {
      this.setState({ typeColor: { color: "blue" } });
    } else {
      this.setState({ typeColor: { color: "pink" } });
    }
    this.typeWriter(string);
  }

  render() {
    return <div style={this.state.typeColor}>{this.state.typed}</div>;
  }
}

现在,我希望它的执行流程是这样的:string数组的第一个元素在map函数中被'selected'-〉i call stringchecker for that first element -〉i call typewriter for that first element,并遍历字符。然后我返回到map函数并为next做同样的事情。看起来流程根本不是这样的,相反,它在每个字符片的两个字符串之间交替。如果有人能给我解释一下我会很感激的。非常感谢
沙盒链接:https://codesandbox.io/s/morning-dust-18nq5

qlfbtfca

qlfbtfca1#

您可以简单地使用useEffect()钩子,它将在每次重新呈现(随机延迟)后增加文本的可见部分(存储在组件的状态中),直到整个文本被删除。
您可以在下面找到此概念的现场演示:

const { useState, useEffect } = React,
      { render } = ReactDOM
      

const srcString = `That's the text I'm going to print out`

const Typewriter = ({srcString}) => {

  const [{content,carriage}, setContent] = useState({content:'',carriage:0})
       
  useEffect(() => {
    if(carriage == srcString.length) return
    const delay = setTimeout(() => {
      setContent({content:content+srcString[carriage], carriage: carriage+1})
      clearTimeout(delay)
    }, 0|(Math.random()*200+50))
  }, [content])
  
  return <span>{content}<span className="cursor">|</span></span>
  
}

render (
  <Typewriter {...{srcString}} />,
  document.getElementById('root')
)
.cursor{animation:1s blink step-end infinite}@keyframes blink{from,to{color:transparent}50%{color:#000}}@-moz-keyframes blink{from,to{color:transparent}50%{color:#000}}@-webkit-keyframes blink{from,to{color:transparent}50%{color:#000}}@-ms-keyframes blink{from,to{color:transparent}50%{color:#000}}@-o-keyframes blink{from,to{color:transparent}50%{color:#000}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
e5nqia27

e5nqia272#

输入逻辑是好的,但是,注意setTimeout是异步的,所以它不会等待整个单词被输入后再切换到下一个单词。解决方案是使整个输入流程异步,这样您就不会在前一个单词输入之前切换到下一个单词。
下面是sandbox demo:https://codesandbox.io/s/boring-greider-824nd
主要的变化是这样的:

if (string.length === 0) {
  words = words.slice(1);
  words[0] && this.typeWord(words[0], words);
}

检查我们是否完成了输入一个单词,然后将其切片,以获得下一个单词。如果我们还有单词,我们调用typeWord,其中包含应该输入的单词和接下来应该输入的单词数组。如果没有剩余的单词,我们退出键入。

wooyq4lh

wooyq4lh3#

是的,setTimeout()在处理需要同步执行的任务时可能会很棘手(比如打字效果)。
下面是使用async/await的另一种方法。我将typeWriter方法设置为异步,因此它只会在字符串中的所有字符都被键入后返回。之后,将允许键入NEXT字符串。要实现这一点,stringChecker也需要异步。检查一下,让我知道如果有什么困惑。
https://codesandbox.io/s/youthful-davinci-d5bk5?fontsize=14&hidenavigation=1&theme=dark
另外......如果你只是想让它运行起来,我做了一个TypeIt,一个JS类型库,还有一个React组件版本。如果有用,请查看:
https://github.com/alexmacarthur/typeit-react

j8yoct9x

j8yoct9x4#

这对我很有效,我写的一个小的自定义钩子

export const useTypeWriter = (content = '') => {
    const [prevContent, setPrevContent] = useState(content);
    const [animatedContent, setAnimatedContent] = useState('');
    const hasContentChanged = prevContent === content;

    const generateContent = () => {
        setAnimatedContent((prevAnimatedContent) => {
            const charCount = prevAnimatedContent.length;
            if (prevAnimatedContent.length + 1 !== content.length) setTimeout(generateContent, 90);

            return prevAnimatedContent + content[charCount];
        });
    };

    useEffect(() => {
        if (!hasContentChanged) return;

        setPrevContent(content);
        generateContent(content);
    }, [hasContentChanged]);

    return animatedContent;
};

相关问题