UNPKG

easy-typer-js

Version:

Very powerful JS typewriter plugin, compatible with native JS and MVVM class framework (Vue, React...), whatever you want to output.

227 lines (200 loc) 5.56 kB
// 配置对象typer接口 interface Typer { output: string; type: string; isEnd: boolean; speed: number; backSpeed: number; sleep: number; singleBack: boolean; sentencePause: boolean; } // 打字机模式类型接口 interface TyperAction { rollback: Function; normal: Function; custom: Function; // 这里是为了可以如数组一般取值:arr[key]形式 [key: string]: any; } class EasyTyper { obj: Typer; // 配置对象 input: Array<string>; // 输入源 timer: number; // 定时器 typeAction: TyperAction; // 打字机模式类型 fn: Function; // 完成输入源输出后执行的回调函数 hooks: Function; // 完成每一帧的输出后的钩子函数 constructor(obj: Typer, input: Array<string> | string, fn: Function, hooks: Function) { 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) } // 下一次触发方式 async nextTick(){ // 等待 await 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:number) { return new Promise(resolve => setTimeout(resolve, ms)) } // 结束 close() { return this.obj.isEnd = true } } /**** 以下方法和实例都是为了编译成js后验证字段正确性 **/ // 策略接口 interface TyperStrategy { [key: string]: any } // 错误提示语 const errorTip = (message: string) =>{ throw new Error(message); } // 校验参数完整性 const checkKeyIsNull = (obj: any) => { const props: Typer = { 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: any) => { Object.keys(obj).forEach(key => { const proxy = EasyTyperStrategy[key](obj); if(proxy.check()) { proxy.showTip(key); } }) } // 策略分发 const EasyTyperStrategy: TyperStrategy = (() => ({ output: (obj: any) => { return new CheckField(`string`, obj.output); }, type: (obj: any) => { return new CheckField(`string`, obj.type); }, isEnd: (obj: any) => { return new CheckField(`boolean`, obj.isEnd); }, speed: (obj: any) => { return new CheckField(`number`, obj.speed); }, backSpeed: (obj: any) => { return new CheckField(`number`, obj.backSpeed); }, sleep: (obj: any) => { return new CheckField(`number`, obj.sleep); }, singleBack: (obj: any) => { return new CheckField(`boolean`, obj.singleBack); }, sentencePause:(obj: any) => { return new CheckField(`boolean`, obj.sentencePause); }, }))() // 字段校验类 class CheckField { type: string; field: any; constructor(type: string, field: any) { this.type = type; this.field = field; } check() { return typeof this.field !== `${this.type}`; } showTip(name: string) { errorTip(`配置对象错误:属性 ${name} 必须为 ${this.type} 类型!`); } } export default EasyTyper;