让文字像打字机一样一个个的显示在页面上,应该会给用户一种比较炫酷的感觉
![](https://img.haomeiwen.com/i19541867/c9edd3bf7efddc3c.gif)
直接上代码
<!-- 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: '', // 输出内容 使用MVVM框架时可以直接使用
type: 'rollback',
isEnd: false,
speed: 80,
backSpeed: 80,
sleep: 3000,
singleBack: true,
sentencePause: true
}
const textList = [`黎明前的黑暗是最深不见底的黑暗!`, `世界上本没有无用的齿轮,只有齿轮自身能决定它的用途!`, `天不生我彭小呆,万古长青一生狂!`]
const typing = new EasyTyper(obj, textList, ()=>{
// 此回调结束了easyTyper的生命周期
console.log('结束了,我的使命!')
}, (output, instance) => {
// 钩子函数,每一帧的数据获取和实例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
};
// 实例化完后立即执行打字输出
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() {
// 如果句子出书完毕,且是句子暂停模式
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);
}
// 下一次触发方式
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);
}
// 关闭定时器
closeTimer() {
clearInterval(this.timer);
}
// 线程等待
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 结束
close() {
return this.obj.isEnd = true;
}
}
// 错误提示语
const errorTip = (message) => {
throw new Error(message);
};
// 校验参数完整性
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('配置对象错误: 字段数量不正确!');
}
propsKeys.forEach(key => {
if (obj[key] === undefined || obj[key] === null) {
errorTip('配置对象错误:字段值为null或undefined!');
}
});
};
// 检验参数类型
const checkFieldIsError = (obj) => {
Object.keys(obj).forEach(key => {
const proxy = EasyTyperStrategy[key](obj);
if (proxy.check()) {
proxy.showTip(key);
}
});
};
// 策略分发
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);
},
}))();
// 字段校验类
class CheckField {
constructor(type, field) {
this.type = type;
this.field = field;
}
check() {
return typeof this.field !== `${this.type}`;
}
showTip(name) {
errorTip(`配置对象错误:属性 ${name} 必须为 ${this.type} 类型!`);
}
}
网友评论