讓文字像打字機(jī)一樣一個(gè)個(gè)的顯示在頁(yè)面上扔字,應(yīng)該會(huì)給用戶一種比較炫酷的感覺(jué)
直接上代碼
<!-- html代碼 -->
<script src="./index.js"></script>
<style>
.easy-typed-cursor {
margin-left: 10px;
opacity: 1;
-webkit-animation: blink 0.7s infinite;
-moz-animation: blink 0.7s infinite;
animation: blink 0.7s infinite;
}
.output-wrapper {
margin-top: 30vh;
text-align: center;
font-size: 20px;
}
.img {
width: 300px;
}
.img-2 {
width: 100px;
}
@keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-webkit-keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-moz-keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
<div class="output-wrapper"><span id="output"></span><span class="easy-typed-cursor">|</span></div>
<script>
const obj = {
output: '', // 輸出內(nèi)容 使用MVVM框架時(shí)可以直接使用
type: 'rollback',
isEnd: false,
speed: 80,
backSpeed: 80,
sleep: 3000,
singleBack: true,
sentencePause: true
}
const textList = [`黎明前的黑暗是最深不見(jiàn)底的黑暗潮剪!`, `世界上本沒(méi)有無(wú)用的齒輪,只有齒輪自身能決定它的用途足丢!`, `天不生我彭小呆,萬(wàn)古長(zhǎng)青一生狂!`]
const typing = new EasyTyper(obj, textList, ()=>{
// 此回調(diào)結(jié)束了easyTyper的生命周期
console.log('結(jié)束了胀溺,我的使命幢痘!')
}, (output, instance) => {
// 鉤子函數(shù)唬格,每一幀的數(shù)據(jù)獲取和實(shí)例EasyTyper的獲取
console.log(output)
document.getElementById('output').innerHTML = `${output}`
})
</script>
/*js代碼*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class EasyTyper {
constructor(obj, input, fn, hooks) {
checkKeyIsNull(obj);
checkFieldIsError(obj);
this.obj = obj;
this.input = typeof input === 'string' ? [input] : input;
this.fn = typeof fn === 'function' ? fn : function () { };
this.hooks = typeof hooks === 'function' ? hooks : function () { };
this.timer = 0;
this.typeAction = {
rollback: this.typedBack.bind(this),
normal: this.play.bind(this),
custom: this.fn
};
// 實(shí)例化完后立即執(zhí)行打字輸出
this.init();
}
init() {
this.play();
}
// 打字
play() {
if (!this.input.length)
return this.fn(this);
let i = 0, stop = false, input = this.input.shift() || '';
this.timer = setInterval(() => {
if (i === input.length) {
i = 0;
stop = true;
this.closeTimer();
}
if (this.obj.isEnd)
return this.closeTimer();
if (stop)
return this.nextTick();
this.obj.output = input.slice(0, i + 1);
this.hooks(input.slice(0, i + 1), this);
i++;
}, this.obj.speed);
}
// 回滾方法
typedBack() {
// 如果句子出書(shū)完畢,且是句子暫停模式
if (!this.input.length && this.obj.sentencePause)
return this.fn(this);
let input = this.obj.output;
let i = input.length, stop = false;
this.timer = setInterval(() => {
if (i === -1) {
this.obj.output = '';
this.hooks('', this);
i = 0;
stop = true;
this.closeTimer();
}
if (this.obj.isEnd) {
this.closeTimer();
return this.obj.singleBack = false;
}
if (stop) {
this.obj.singleBack = false;
return (() => {
const { length } = this.input;
return length ? this.play() : this.fn(this);
})();
}
this.obj.output = input.slice(0, i + 1);
this.hooks(input.slice(0, i + 1), this);
i--;
}, this.obj.backSpeed);
}
// 下一次觸發(fā)方式
nextTick() {
return __awaiter(this, void 0, void 0, function* () {
// 等待
yield this.sleep(this.obj.sleep);
return this.obj.singleBack ? this.typedBack() : this.getOutputType();
});
}
// 輸出方式
getOutputType() {
return this.typeAction[this.obj.type](this);
}
// 關(guān)閉定時(shí)器
closeTimer() {
clearInterval(this.timer);
}
// 線程等待
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 結(jié)束
close() {
return this.obj.isEnd = true;
}
}
// 錯(cuò)誤提示語(yǔ)
const errorTip = (message) => {
throw new Error(message);
};
// 校驗(yàn)參數(shù)完整性
const checkKeyIsNull = (obj) => {
const props = {
output: '',
type: '',
isEnd: false,
speed: 80,
backSpeed: 40,
sleep: 3000,
singleBack: false,
sentencePause: false
};
const propsKeys = Object.keys(props);
const objKeys = Object.keys(obj);
if (propsKeys.length !== objKeys.length) {
errorTip('配置對(duì)象錯(cuò)誤: 字段數(shù)量不正確颜说!');
}
propsKeys.forEach(key => {
if (obj[key] === undefined || obj[key] === null) {
errorTip('配置對(duì)象錯(cuò)誤:字段值為null或undefined购岗!');
}
});
};
// 檢驗(yàn)參數(shù)類(lèi)型
const checkFieldIsError = (obj) => {
Object.keys(obj).forEach(key => {
const proxy = EasyTyperStrategy[key](obj);
if (proxy.check()) {
proxy.showTip(key);
}
});
};
// 策略分發(fā)
const EasyTyperStrategy = (() => ({
output: (obj) => {
return new CheckField(`string`, obj.output);
},
type: (obj) => {
return new CheckField(`string`, obj.type);
},
isEnd: (obj) => {
return new CheckField(`boolean`, obj.isEnd);
},
speed: (obj) => {
return new CheckField(`number`, obj.speed);
},
backSpeed: (obj) => {
return new CheckField(`number`, obj.backSpeed);
},
sleep: (obj) => {
return new CheckField(`number`, obj.sleep);
},
singleBack: (obj) => {
return new CheckField(`boolean`, obj.singleBack);
},
sentencePause: (obj) => {
return new CheckField(`boolean`, obj.sentencePause);
},
}))();
// 字段校驗(yàn)類(lèi)
class CheckField {
constructor(type, field) {
this.type = type;
this.field = field;
}
check() {
return typeof this.field !== `${this.type}`;
}
showTip(name) {
errorTip(`配置對(duì)象錯(cuò)誤:屬性 ${name} 必須為 ${this.type} 類(lèi)型!`);
}
}