光标仿真器

发布于:2021-02-04 14:40:20

0

63

0

react javascript 初学者 前端

那么我们今天要创建什么呢?

我们将在这个项目中使用React来模拟用户输入效果。

我们的最终输出将如下所示:

  

项目大纲:

  • 组件将描述的数组作为输入。

  • 组件将有一个闪烁的文本光标(“|”)。

  • 文本开始以指定的延迟出现在屏幕中。

  • 光标随着文本类型移动。

  • 光标在句末闪烁一段时间。

  • 最后一句结束时没有光标闪烁。

import React from 'react';
import PropTypes from 'prop-types';

class CursorImitator extends React.Component {

 static defaultProps = {
   content: [],
   nextLetterTime: 50,
   waitAtEnd: 500,
   waitAtStart: 200,
   blinkSpeed: 130,
   letterWrapClass: ''
 }

 constructor(props) {
   super(props);
   this.loadIntro = this.loadIntro.bind(this);
   this.loadCursorBlink = this.loadCursorBlink.bind(this);
   this.clearAllTimeoutsAndIntervals = this.clearAllTimeoutsAndIntervals.bind(this);
   this.state = {
     mounted: false
   };
   this.ttw = 0;             // Time to Wait.
   this.timeoutList = [];    // List of all timeouts triggered.
 }

 componentDidMount() {
   this.setState({ mounted: true });
 }

 loadIntro(content) {
   const contentLast = content.length - 1;
   return({
         content.map((sentence, index) => {
           const refName = 'line_' + index;
           const isLast = contentLast === index;
           return ({this.state.mounted && this.loadLetter(sentence, refName, isLast)})
         })
       })
 }

 render() {
   const {content, className, style} = this.props;
   return ({this.loadIntro(content)});
 }
}

到目前为止我们做了什么?

  • defaultProps具有属性的初始值(如果未提供)。

  • content-描述数组。每个索引存储一个要出现的句子。

  • nextLetterTime-下一个字母出现之前的时间。

  • waitAtEnd-每个句子结束时等待的时间。

  • waitAtStart-每个句子开始前等待的时间。

  • blinkSpeed-光标出现并重新出现[闪烁]的时间。

  • letterWrapClass-用于< div >包装句子的类。

  • constructor()

  • this.ttw-等待时间是一个实例变量,因为它必须持久化。

  • this.timeoutList-触发的所有超时的列表,以便我们可以在需要时清除。

  • render() -负载 loadIntro()

  • loadIntro()-对于每个句子,我们都迭代并加载字母,但前提是已安装组件。这是因为我们需要引用每个句子,并且只有在安装组件之后才会创建它们。我们正在使用对每个句子的div的引用来更新它,而不是将其作为状态变量。

下一步:逐个字母地加载

loadLetter(sentence, refName, isLastSentence) {
   /* To retain content when accessed from within setTimeout */
   let sentenceLength = sentence.length;
   sentence.split('').forEach((letter, index) => {
     let nextLetter = letter;
     let ttw = this.ttw++;
     let reference = refName;
     const {nextLetterTime, waitAtEnd, waitAtStart} = this.props;
     let self = this;
     let currIndex = index;
     /* To create a Blink at the start of every Sentence */
     if (index === 0) {
       this.loadCursorBlink(self.refs[reference], this.ttw);
       this.ttw = this.ttw + (waitAtStart / nextLetterTime);
       ttw = this.ttw;
     }
     const nextTimeout = setTimeout(() => {
       if (self.interval) {
         clearInterval(self.interval);       // Clear any previous Intervals and removing blink
       }
       if (currIndex === 0 && self.refs && self.refs[reference]) { // Adding '|' in the beginning of every sentence and  inserting incoming texts before that
         self.refs[reference].innerText = '|';
       }
       if (nextLetter === ' ' && self.refs && self.refs[reference]) {  // Handling space
         return self.refs[reference][removed] = self.refs[reference][removed].substring(0, self.refs[reference][removed].length - 1) + ' |';
       } else if (self.refs && self.refs[reference]) {                 // adding next digit
         return self.refs[reference].innerText = self.refs[reference].innerText.substring(0,self.refs[reference].innerText.length - 1) + nextLetter + '|';
       }
     }, ttw * nextLetterTime);  // incremented value for every sentence
     this.timeoutList.push(nextTimeout); // To clear it all at once if required
     if (index === sentenceLength - 1) {
       /* To create a Blink at the End of every Sentence */
       this.loadCursorBlink(this.refs[reference], this.ttw, true, isLastSentence);
       this.ttw = this.ttw + (waitAtEnd / nextLetterTime);
     }
   })
 }

loadLetter() 接受3个参数。

  • sentence 是出现在单独一行上的句子。

  • refName 获取对其应加载内容的div的引用。

  • islastSentence 用于避免最后加载闪烁的光标。

在这里,我们应该注意闭包,因为我们将settimeout不遗漏父范围的每个字母。因此,我们使用let和const使其绑定到setTimeout。

this.ttw = this.ttw + (waitAtStart / nextLetterTime);

  • waitAtStart/nextLetterTime 给出迭代次数,之后必须出现下一个字母。

  • 我们this.ttw为每个字母递增。每个字母出现的时间是其在this.ttw和中的位置的倍数nextLetterTime

  • index === 0之所以进行检查,是因为无论何时我们使用新句子,光标都应该在开头闪烁一会儿。然后我们计算新的this.ttw时间,应该在眨眼时间到期之后。

  • nextTimeout保留当前触发的超时,该超时在所需时间过去后触发,并被推入this.timeoutList以便稍后清除。

  • 在这里,我们清除所有以前的(self.interval如果存在的话),以确保不发生闪烁。this.interval保留创建的闪烁间隔。

  • currIndex === 0检查是否已添加“ |” 在每个句子的开头,并在前面插入传入的字母。

  • 如果我们到达句子的末尾,则最后检查完成。如果是,我们可以使光标闪烁。

loadCursorBlink(ref, ttw, end, isLastSentence) {
 let reference = ref;
 let self = this;
 let isEnd = end;
 const {nextLetterTime, blinkSpeed} = this.props;
 const nextTimeOut = setTimeout(() => {
   if (self.interval) {
     clearInterval(self.interval);
     // 'self.lastReference' stores last shown sentence's reference, we remove the '|' symbol before creating a new interval
     if (self.lastReference && self.lastReference.innerText.substring(self.lastReference.innerText.length-1) === '|') {
       self.lastReference.innerText = self.lastReference.innerText.substring(0, self.lastReference.innerText.length - 1);
     }
   }
   if (!isLastSentence) {
     self.interval = setInterval(() => {
       self.lastReference = reference;
       if (isEnd) {
         if (reference.innerText.substring(reference.innerText.length - 1) === '|') {
           reference.innerText = reference.innerText.substring(0, reference.innerText.length - 1);
         } else if (reference.innerText.substring(reference.innerText.length - 1) !== '|') {
           reference.innerText = reference.innerText + '|';
         }
       } else {
         if (reference.innerText === '|') {
           reference.innerText = '';
         } else if (reference.innerText === '') {
           reference.innerText = '|';
         }
       }
     }, blinkSpeed);
   }
 }, ttw * nextLetterTime);
 this.timeoutList.push(nextTimeOut);}

loadCursorBlink()需要4个参数。对div的引用,等待时间,句子结尾以及是否为最后一个句子。

  • setTimeout 对于此方法,请保持闪烁出现并消失之后的时间。

  • nextTimeout保留当前触发的超时,该超时在所需时间过去后触发,并被推入this.timeoutList以便稍后清除。

  • 在这里,我们清除以前的任何间隔(如果存在)并self.lastReference存储最后显示的句子的引用,我们删除“ |” 符号,然后再创建一个新间隔。

  • 如果不是最后一句话,则我们按照给定的间隔启动眨眼blinkSpeed。

  • 我们处理句子中所有字母的结尾和句子中第一个字母的开头的眨眼。

componentWillUnmount() {
 this.clearAllTimeoutsAndIntervals()
}

clearAllTimeoutsAndIntervals() {
 if (this.timeoutList) {
   this.timeoutList.forEach((timeout) => {
     clearTimeout(timeout);
   });
 }
 clearInterval(this.interval);
}

clearAllTimeoutsAndIntervals() 如果在启动所有触发的超时之前已卸载组件,则有助于清除所有间隔。