JavaScript字串動畫輪播效果

antzone發表於2017-03-13

分享一段程式碼例項,它利用js實現了字串動畫輪播效果。

程式碼例項如下:

[HTML] 純文字檢視 複製程式碼執行程式碼
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>螞蟻部落</title>
<style>
html, body {
  background: #212121;
  height: 100%;
}
.container {
  height: 100%;
  width: 100%;
  justify-content: center;
  align-items: center;
  display: flex;
}
.text {
  font-weight: 100;
  font-size: 50px;
  color: #FAFAFA;
}
.dud {
  color: #757575;
}
</style>
<script>
class TextScramble {
  constructor(el) {
    this.el = el;
    this.chars = '!<>-_\\/[]{}—=+*^?#________';
    this.update = this.update.bind(this);
  }
  setText(newText) {
    const oldText = this.el.innerText;
    const length = Math.max(oldText.length, newText.length);
    const promise = new Promise((resolve) => this.resolve = resolve);
    this.queue = [];
    for (let index = 0; index < length; index++) {
      const from = oldText[index] || '';
      const to = newText[index] || '';
      const start = Math.floor(Math.random() * 40);
      const end = start + Math.floor(Math.random() * 40);
      this.queue.push({ from, to, start, end });
    }
    cancelAnimationFrame(this.frameRequest);
    this.frame = 0;
    this.update();
    return promise;
  }
  update() {
    let output = '';
    let complete = 0;
    for (let index = 0, n = this.queue.length; index < n; index++) {
      let { from, to, start, end, char } = this.queue[index];
      if (this.frame >= end) {
        complete++;
        output += to;
      } else if (this.frame >= start) {
        if (!char || Math.random() < 0.28) {
          char = this.randomChar();
          this.queue[index].char = char;
        }
        output += `<span class="dud">${char}</span>`;
      } else {
        output += from;
      }
    }
    this.el.innerHTML = output;
    if (complete === this.queue.length) {
      this.resolve();
    } else {
      this.frameRequest = requestAnimationFrame(this.update);
      this.frame++;
    }
  }
  randomChar() {
    return this.chars[Math.floor(Math.random() * this.chars.length)];
  }
}
 
window.onload=function(){
  const phrases = [
    '螞蟻部落',
    'css教程',
    'js教程',
    'softwhy.com',
    '努力奮鬥',
    'json教程'
  ];
 
  const el = document.querySelector('.text');
  const fx = new TextScramble(el);
 
  let counter = 0;
  const next = () => {
    fx.setText(phrases[counter]).then(() => {
      setTimeout(next, 800)
    })
    counter = (counter + 1) % phrases.length;
  }
  next()
}
</script>
</head>
<body>
<div class="container">
  <div class="text"></div>
</div>
</body>
</html>

上面的程式碼實現了我們的要求,下面介紹一下它的實現過程。

程式碼註釋:

(1).class TextScramble {}建立一個類。

(2).constructor(el) {

  this.el = el;

  this.chars = '!<>-_\\/[]{}—=+*^?#________';

  this.update = this.update.bind(this);

},類的建構函式,能夠初始化一些值。

this.update = this.update.bind(this),這個的目的是為了固定函式的this指向,不管其他地方怎麼呼叫update()方法,  update()內部的this都是指向當前的那個例項物件。

(3).setText(newText) {},宣告一個原型上的方法,引數是一個語句。

(4).const oldText = this.el.innerText,獲取當前元素的文字內容。

(5).const length = Math.max(oldText.length, newText.length),獲取當前元素字串和新傳入字串的最大長度。

(6).const promise = new Promise((resolve) => this.resolve = resolve),建立一個promise物件例項,後面會用到,之所以將resove方法賦值給this.resolve,是為了在可以在其他地方用例項物件來呼叫此方法。

(7).this.queue = [],建立一個陣列,後面會用到。

(8).for (let index = 0; index < length; index++) {

  const from = oldText[index] || '';

  const to = newText[index] || '';

  const start = Math.floor(Math.random() * 40);

  const end = start + Math.floor(Math.random() * 40);

  this.queue.push({ from, to, start, end });

}通過for迴圈來為陣列新增元素,下面分步介紹一下:

const from = oldText[index] || '';

const to = newText[index] || '';

原來的文字和新傳入的不一定哪個更長,index有可能會超出,所以如果超出就賦值給空字元。

(9).const start = Math.floor(Math.random() * 40);

const end = start + Math.floor(Math.random() * 40);

40這個數字沒有特別的邏輯,也可以是其他數字,生成一個起始值和結束值。

(10).this.queue.push({ from, to, start, end }),將物件壓入陣列。

(11).cancelAnimationFrame(this.frameRequest),取消上一個動畫的執行。

(12).this.frame = 0,賦值為0,後面會用到。

(13).this.update(),呼叫update()方法。

(14).return promise,返回promise物件。

(15).update() {},此函式是實現動畫變換的核心。

(16).let output = '',宣告一個變數,用儲存輸出新顯示的語句。

(17).let complete = 0,宣告一個變數並賦初值為0,後面會用到。

(18).for (let index = 0, n = this.queue.length; index < n; index++) {  

  let { from, to, start, end, char } = this.queue[index];

  if (this.frame >= end) {

    complete++;

      output += to;

    } else if (this.frame >= start) {

      if (!char || Math.random() < 0.28) {

        char = this.randomChar();

        this.queue[index].char = char;

      }

      output += `<span class="dud">${char}</span>`;

    } else {

      output += from;

    }

}

雖然程式碼看起來比較多,其實邏輯很簡單。

this.frame剛開始是0並且能夠累加,剛開始有很大的概率是小於end,所以就會執行後面兩個if語句分支的程式碼,於是就會出現展現原來字串內容和chars所規定的特殊的字元效果;隨著this.frame累加,最終會大於等於end,那麼output中的字元只有新的陣列字元。

(19).this.el.innerHTML = output,在文件中顯示指定字串。

(20).if (complete === this.queue.length) {

  this.resolve();

} else {

  this.frameRequest = requestAnimationFrame(this.update);

  this.frame++;

},如果if (complete === this.queue.length)成立,那麼說明新字串組合完畢,比如"螞蟻部落"連線完整。

那麼就執行this.resolve()方法,改變promise的狀態。

否則繼續動畫的執行。

相關文章