js動(dòng)畫(huà)特效之打字機(jī)效果: easy-typer-js

讓文字像打字機(jī)一樣一個(gè)個(gè)的顯示在頁(yè)面上扔字,應(yīng)該會(huì)給用戶一種比較炫酷的感覺(jué)
easy-typer.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: '', // 輸出內(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)型!`);
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末门粪,一起剝皮案震驚了整個(gè)濱河市喊积,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌玄妈,老刑警劉巖乾吻,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拟蜻,居然都是意外死亡绎签,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)酝锅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诡必,“玉大人,你說(shuō)我怎么就攤上這事屈张∏苋ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵阁谆,是天一觀的道長(zhǎng)碳抄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)场绿,這世上最難降的妖魔是什么剖效? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮焰盗,結(jié)果婚禮上璧尸,老公的妹妹穿的比我還像新娘。我一直安慰自己熬拒,他們只是感情好爷光,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著澎粟,像睡著了一般蛀序。 火紅的嫁衣襯著肌膚如雪欢瞪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天徐裸,我揣著相機(jī)與錄音遣鼓,去河邊找鬼。 笑死重贺,一個(gè)胖子當(dāng)著我的面吹牛骑祟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播气笙,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼次企,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了潜圃?” 一聲冷哼從身側(cè)響起抒巢,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秉犹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體稚晚,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崇堵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了客燕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸳劳。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖也搓,靈堂內(nèi)的尸體忽然破棺而出赏廓,到底是詐尸還是另有隱情,我是刑警寧澤傍妒,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布幔摸,位于F島的核電站,受9級(jí)特大地震影響颤练,放射性物質(zhì)發(fā)生泄漏既忆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一嗦玖、第九天 我趴在偏房一處隱蔽的房頂上張望患雇。 院中可真熱鬧,春花似錦宇挫、人聲如沸苛吱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)翠储。三九已至绘雁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彰亥,已是汗流浹背咧七。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留任斋,地道東北人继阻。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像废酷,于是被迫代替她去往敵國(guó)和親瘟檩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348