需求
由于不滿上一家公司不思進取的前端技術棧,從上一家公司離職。目前多了不少時間,想着是否可以将之前廢棄的個人部落格拾起來,就準備重構自己的個人部落格。
首先碰到的一個問題是,我想實作一個打字機效果,但是又可以自動倒退,列印下一個單詞的效果。網上隻有列印出來字型的效果,但是沒有倒退,是以,使用一段時間,自己寫了一個。
技術
技術上使用的是原生的
js(ES6)
以及
React
。
代碼
廢話不多說,直接上代碼:
/**
* 一個用來模拟線程睡眠的方法
* @param {Number} time 睡眠時間 - 必須
* @param {Function} callback 回調函數 - 非必須
* @author qianqian
*/
const sleep = (time = 0, callback) => {
return new Promise((resolve, reject) => {
if (!time) {
reject('the sleep time is required!');
}
if (callback) {
setTimeout(() => {
callback();
resolve();
}, time);
} else {
setTimeout(() => {
resolve();
}, time);
}
});
}
/**
* 開始列印字元,可以将 Node 一個個添加進 DOM 樹
* @param {Array} insertNodeList 需要插入的 Node (必須)
* @param {Node} insertedNode 需要被插入的 Node (必須)
* @param {Number} time 插入兩個字元之間的間隔 (必須)
* @param {Node} insertBeforeNode 需要插入的在什麼 Node 之前 (非必須)
* @author qianqian
*/
const printChar = async (insertNodeList = [], insertedNode = document.body, time = 0, insertBeforeNode) => {
if (insertBeforeNode) {
insertNodeBefore(insertNodeList, insertedNode, time, insertBeforeNode);
} else {
insertNodeIn(insertNodeList, insertedNode, time);
}
// 直接插入的情況
async function insertNodeIn(insertNodeList, insertedNode, time) {
for (let i = 0; i < insertNodeList.length; i ++) {
const insertNode = insertNodeList[i];
await sleep(time);
insertedNode.appendChild(insertNode);
}
}
// 需要插入在特定元素之前的情況
async function insertNodeBefore(insertNodeList, insertedNode, time, insertBeforeNode) {
for (let i = 0; i < insertNodeList.length; i ++) {
const insertNode = insertNodeList[i];
await sleep(time);
insertedNode.insertBefore(insertNode, insertBeforeNode);
}
}
}
/**
* 一個一個開始删除字元,模拟列印删除,當然也可以從 DOM 樹中一個個删除 Node
* @param {String} className 類名
* @param {Number} time 删除字元時之間的間隔
* @author qianqian
*/
const deleteChar = async (className = '', time = 0) => {
const pList = document.getElementsByClassName(className) || [];
const length = pList.length;
for (let i = length - 1; i >= 0; i --) {
await sleep(time);
if (pList[i]) {
pList[i].remove();
} else {
continue;
}
}
}
export {
sleep,
printChar,
deleteChar,
}
這裡面主要使用的技術是利用
setTimeout()
方法來實作打字機的效果。但是有一個問題,如果直接再循環中使用
setTimeout()
函數來建立列印效果,那麼最後的結果會是,經過一段時間後,所有的字幾乎在同時顯示出來。這是由于,
setTimeout()
方法是異步的,利用循環進行建立相當于在一瞬間建立數個計時器,并且這些計時器有等待時間,在等待時間結束之後,一起執行。
為了防止這種情況,我使用了 Promise 類以及async await,來建立一個類似于
Java
中線程睡眠的效果。
主要注意的是,不能使用
forEach
循環,因為
forEach
是同步方法。
在 React 中使用
React 需要挂在元件,是以,如果需要使用這些方法來模拟打字機效果,需要在
useEffect()
—— 無狀态元件,或者在
componentDidMount()
中使用:
import React, { useEffect, useState } from 'react';
import { printChar, deleteChar, sleep } from '../../../tools/sleep';
import { betweenDelete, betweenPrint, betweenTime } from '../../../constant/common';
import './Paragraph.scss';
export default function Paragraph(props) {
const [content, setContent] = useState(['coder', 'reader']);
useEffect(() =>{
autoShow(content);
}, []);
// 自動一個一個展示代碼
const autoShow = async (content = []) => {
const insertedNode = document.getElementsByClassName('paragraph-show')[0];
const beforeNode = document.getElementsByClassName('paragraph-shaking-cursor')[0];
for (let i = 0; i < content.length; i ++) {
const nodeList = [];
const str = content[i];
for (let j = 0; j < str.length; j ++) {
const p = document.createElement('p');
p.classList.add('paragraph-auto-p');
const value = document.createTextNode(str[j]);
p.appendChild(value);
nodeList.push(p);
}
// allTime += str.length * betweenPrint;
printChar(nodeList, insertedNode, betweenPrint, beforeNode);
// allTime += betweenTime;
await sleep(str.length * betweenPrint + betweenTime);
const pList = insertedNode.getElementsByClassName('paragraph-auto-p');
// allTime += pList.length * betweenDelete + betweenTime;
deleteChar('paragraph-auto-p', betweenDelete);
await sleep(pList.length * betweenDelete + betweenTime);
if (i === content.length - 1) {
i = -1;
}
}
}
return (
<div className='paragraph'>
<div className='paragraph-title'>
<p className='title'>I am a</p>
</div>
<div className='paragraph-autoShow'>
{/* { autoShow(content) } */}
<div className='paragraph-show'>
<p className='paragraph-shaking-cursor'></p>
</div>
</div>
</div>
)
}
問題
目前已知的問題:
由于使用了異步方式,當頁面來回切換,可能出現列印順序亂掉的情況。
并不是 react 内部元件,可能沒有辦法利用 React 的生命周期,無法正常的解除安裝。