發(fā)布于:2021-02-04 14:40:20
0
80
0
那么我們今天要?jiǎng)?chuàng)建什么呢?
我們將在這個(gè)項(xiàng)目中使用React來模擬用戶輸入效果。
我們的最終輸出將如下所示:
項(xiàng)目大綱:
組件將描述的數(shù)組作為輸入。
組件將有一個(gè)閃爍的文本光標(biāo)(“|”)。
文本開始以指定的延遲出現(xiàn)在屏幕中。
光標(biāo)隨著文本類型移動(dòng)。
光標(biāo)在句末閃爍一段時(shí)間。
最后一句結(jié)束時(shí)沒有光標(biāo)閃爍。
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-描述數(shù)組。每個(gè)索引存儲(chǔ)一個(gè)要出現(xiàn)的句子。
nextLetterTime
-下一個(gè)字母出現(xiàn)之前的時(shí)間。
waitAtEnd
-每個(gè)句子結(jié)束時(shí)等待的時(shí)間。
waitAtStart
-每個(gè)句子開始前等待的時(shí)間。
blinkSpeed
-光標(biāo)出現(xiàn)并重新出現(xiàn)[閃爍]的時(shí)間。
letterWrapClass
-用于< div >包裝句子的類。
constructor()
this.ttw
-等待時(shí)間是一個(gè)實(shí)例變量,因?yàn)樗仨毘志没?/span>
this.timeoutList
-觸發(fā)的所有超時(shí)的列表,以便我們可以在需要時(shí)清除。
render() -負(fù)載 loadIntro()
loadIntro()-對(duì)于每個(gè)句子,我們都迭代并加載字母,但前提是已安裝組件。這是因?yàn)槲覀冃枰妹總€(gè)句子,并且只有在安裝組件之后才會(huì)創(chuàng)建它們。我們正在使用對(duì)每個(gè)句子的div的引用來更新它,而不是將其作為狀態(tài)變量。
下一步:逐個(gè)字母地加載
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個(gè)參數(shù)。
sentence 是出現(xiàn)在單獨(dú)一行上的句子。
refName 獲取對(duì)其應(yīng)加載內(nèi)容的div的引用。
islastSentence 用于避免最后加載閃爍的光標(biāo)。
在這里,我們應(yīng)該注意閉包,因?yàn)槲覀儗ettimeout不遺漏父范圍的每個(gè)字母。因此,我們使用let和const使其綁定到setTimeout。
this.ttw = this.ttw + (waitAtStart / nextLetterTime);
waitAtStart/nextLetterTime 給出迭代次數(shù),之后必須出現(xiàn)下一個(gè)字母。
我們this.ttw為每個(gè)字母遞增。每個(gè)字母出現(xiàn)的時(shí)間是其在this.ttw和中的位置的倍數(shù)nextLetterTime
index === 0之所以進(jìn)行檢查,是因?yàn)闊o論何時(shí)我們使用新句子,光標(biāo)都應(yīng)該在開頭閃爍一會(huì)兒。然后我們計(jì)算新的this.ttw時(shí)間,應(yīng)該在眨眼時(shí)間到期之后。
nextTimeout保留當(dāng)前觸發(fā)的超時(shí),該超時(shí)在所需時(shí)間過去后觸發(fā),并被推入this.timeoutList以便稍后清除。
在這里,我們清除所有以前的(self.interval如果存在的話),以確保不發(fā)生閃爍。this.interval保留創(chuàng)建的閃爍間隔。
currIndex === 0檢查是否已添加“ |” 在每個(gè)句子的開頭,并在前面插入傳入的字母。
如果我們到達(dá)句子的末尾,則最后檢查完成。如果是,我們可以使光標(biāo)閃爍。
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個(gè)參數(shù)。對(duì)div的引用,等待時(shí)間,句子結(jié)尾以及是否為最后一個(gè)句子。
setTimeout 對(duì)于此方法,請(qǐng)保持閃爍出現(xiàn)并消失之后的時(shí)間。
nextTimeout保留當(dāng)前觸發(fā)的超時(shí),該超時(shí)在所需時(shí)間過去后觸發(fā),并被推入this.timeoutList以便稍后清除。
在這里,我們清除以前的任何間隔(如果存在)并self.lastReference存儲(chǔ)最后顯示的句子的引用,我們刪除“ |” 符號(hào),然后再創(chuàng)建一個(gè)新間隔。
如果不是最后一句話,則我們按照給定的間隔啟動(dòng)眨眼blinkSpeed。
我們處理句子中所有字母的結(jié)尾和句子中第一個(gè)字母的開頭的眨眼。
componentWillUnmount() {
this.clearAllTimeoutsAndIntervals()
}
clearAllTimeoutsAndIntervals() {
if (this.timeoutList) {
this.timeoutList.forEach((timeout) => {
clearTimeout(timeout);
});
}
clearInterval(this.interval);
}
clearAllTimeoutsAndIntervals() 如果在啟動(dòng)所有觸發(fā)的超時(shí)之前已卸載組件,則有助于清除所有間隔。
作者介紹
熱門博客推薦