<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何利用 Web Animation API 制作一個(gè)切換英語單詞的交互動(dòng)畫

          共 11299字,需瀏覽 23分鐘

           ·

          2021-04-13 00:27

          效果預(yù)覽

          按下右側(cè)的“點(diǎn)擊預(yù)覽”按鈕可以在當(dāng)前頁面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。

          https://codepen.io/comehope/pen/byabeG

          源代碼下載

          https://github.com/comehope/front-end-daily-challenges

          代碼解讀

          本作品用于展示若干包含字母組合 OO 的單詞,每點(diǎn)擊一下,OO 就眨眨眼,同時(shí)更換一個(gè)單詞。

          整體開發(fā)過程分成 4 步,第 1 步用 CSS 實(shí)現(xiàn)頁面的靜態(tài)布局,后面 3 步用 JS 實(shí)現(xiàn)動(dòng)畫和業(yè)務(wù)邏輯。第 2 步實(shí)現(xiàn)單詞中間字母 OO 的眨眼效果,第 3 步實(shí)現(xiàn)隨機(jī)取單詞的邏輯,第 4 步實(shí)現(xiàn)字符的切換動(dòng)畫。

          眨眼動(dòng)畫和字符切換動(dòng)畫都是用 Web Animation API 實(shí)現(xiàn)的。雖然用 JS 寫動(dòng)畫比用 CSS 要麻煩一些,但 API 提供了一些事件 handler,在字符切換動(dòng)畫中就是利用事件機(jī)制來精確控制動(dòng)畫和在動(dòng)畫過程中加入業(yè)務(wù)邏輯的。

          下面開始編碼。

          一、靜態(tài)布局:dom,css

          dom 結(jié)構(gòu)很簡單,一個(gè)名為 .word 的

          元素中包含了 4 個(gè)  子元素,每個(gè)子元素容納一個(gè)字符:

          <p class="word">
              <span>b</span>
              <span>o</span>
              <span>o</span>
              <span>k</span>
          </p>

          令頁面中的元素居中,設(shè)置頁面背景色為青藍(lán)色:

          body {
              margin0;
              height100vh;
              display: flex;
              align-items: center;
              justify-content: center;
              background-color: steelblue;
          }

          設(shè)置單詞的樣式,麻布色,大字號(hào),大寫:

          .word {
              font-size100px;
              color: linen;
              font-family: monospace;
              font-weight: bold;
              display: flex;
              text-transform: uppercase;
              cursor: pointer;
              user-select: none;
          }

          讓單詞兩端的 2 個(gè)字符變?yōu)榉凵?/p>

          .word span:first-child,
          .word span:last-child {
              color: pink;
          }

          用徑向漸變給單詞中間的 OO 加上眼珠:

          .word span:not(:first-child):not(:last-child) {
              background-imageradial-gradient(
                  circle at center,
                  linen 0.05em,
                  transparent 0.05em
              );
          }

          至此,靜態(tài)布局完成。

          二、眨眼動(dòng)畫

          為 .word 元素創(chuàng)建一個(gè)單擊事件函數(shù),每當(dāng)點(diǎn)擊發(fā)生時(shí),就先讓中間的 OO 眨眼,然后獲得下一個(gè)要顯示的單詞,再把當(dāng)前的單詞換成新的單詞:

          document.querySelector('.word').onclick = function({
              //第1步:眨眼動(dòng)畫
              //第2步:獲得下一個(gè)單詞
              //第3步:字符切換動(dòng)畫
          }

          先來實(shí)現(xiàn)第1步-眨眼動(dòng)畫。在此之前了解一下 Web Animation API 的語法,下面是一個(gè)簡單的示例:

          let keyframes = [
              {transform'scaleY(1)'},
              {transform'scaleY(0.1)'},
          ]
          let options = {
              duration200,
              iterations2,
          }
          element.animate(keyframes, options)

          animate() 方法接收 2 個(gè)參數(shù),第 1 個(gè)參數(shù)是一個(gè)數(shù)組,用于定義關(guān)鍵幀;第 2 個(gè)參數(shù)是一個(gè)對(duì)象,用于定義動(dòng)畫屬性,它們分別對(duì)應(yīng)著 CSS 中的 @keyframes 語句和 animation 屬性。上面的 JS 代碼等價(jià)于以下 CSS 代碼:

          @keyframes anim {
              from {
                  transformscaleY(1);
              }

              to {
                  transformscaleY(0);
              }
          }

          .element {
              animation-name: anim;
              animation-duration200ms;
              animation-iteration-count2;
          }

          好了,我們來正式寫眨眼動(dòng)畫:

          function blinkEyes({
              let eyes = document.querySelectorAll('.word span:not(:first-child):not(:last-child)')
              let keyframes = [
                  {transform'scaleY(1)'offset0},
                  {transform'scaleY(0.1)'offset0.25},
                  {transform'scaleY(1)'offset0.5},
                  {transform'scaleY(1)'offset1},
              ]
              let options = {
                  duration200,
                  iterations2,
              }
              eyes.forEach(eye => eye.animate(keyframes, options))
          }

          上面代碼中的 offset 是 @keyframes 中為每一幀指定的百分比值。這段動(dòng)畫的意思是每次動(dòng)畫眨眼 2 次,每次眨眼用時(shí) 200ms,這 200ms 的前 50% 時(shí)間(即前 100ms)做眨眼動(dòng)作,后 50% 時(shí)間等待,這樣設(shè)計(jì)的目的是在 2 次眨眼之間插入 100ms 的間隔。

          然后,在點(diǎn)擊事件里調(diào)用上面的方法:

          document.querySelector('.word').onclick = function({
              //第1步:眨眼動(dòng)畫
              blinkEyes()

              //第2步:獲得下一個(gè)單詞
              //第3步:字符切換動(dòng)畫
          }

          至此,當(dāng)用鼠標(biāo)點(diǎn)擊文字時(shí),OO 就會(huì)眨動(dòng)。

          三、獲得下一個(gè)單詞

          接下來寫一點(diǎn)業(yè)務(wù)邏輯,用于隨機(jī)取出一個(gè)單詞。

          引入 lodash 庫:

          <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

          定義一個(gè)名為 Word 的類:

          function Word({
              const WORDS = ['book''boot''cook''cool''door''food''fool''foot''good''look''loop''moon''noon''pool''poor''room''roof','root''soon''tool''wood''zoom',]
              let current = 'book'
              this.getNext = () => {return current = _(WORDS).without(current).sample()}
          }

          Word 類有一個(gè)名為 getNext() 的方法,用于從預(yù)設(shè)的數(shù)組中隨機(jī)取出一個(gè)單詞,可以用下面的代碼測試一下效果,會(huì)輸出類似 food 這樣的單詞:

          let word = new Word()
          console.log(word.getNext())

          因?yàn)榻酉聛淼膭?dòng)畫只涉及單詞左右兩側(cè)的字母,所以在 getNext() 方法中再把兩端的字符拆出來,返回一個(gè)對(duì)象:

          function Word({
              const WORDS = ['book''boot''cook''cool''door''food''fool''foot''good''look''loop''moon''noon''pool''poor''room''roof','root''soon''tool''wood''zoom',]
              let current = 'book'
              this.getNext = () => {
                  current = _(WORDS).without(current).sample()
                  return {
                      first: current.slice(01),
                      last: current.slice(-1)
                  }
              }
          }

          再測試一下效果,輸出結(jié)果會(huì)變?yōu)轭愃?{first: "f", last: "d"} 的對(duì)象。

          在點(diǎn)擊事件中調(diào)用上面的函數(shù),把結(jié)果存入一個(gè)名為 chars 的變量中:

          let word = new Word()

          document.querySelector('.word').onclick = function({
              //step 1: eyes blink animation
              blinkEyes()

              //第2步:獲得下一個(gè)單詞
              let chars = word.getNext()

              //第3步:字符切換動(dòng)畫
          }

          四、字符切換動(dòng)畫

          該制作字符切換動(dòng)畫了。

          函數(shù)的聲明如下,函數(shù)名為 switchChar,它接收 2 個(gè)參數(shù),第 1 個(gè)參數(shù)表示對(duì)哪個(gè)字符執(zhí)行動(dòng)畫,值為 first 或 last,第 2 個(gè)參數(shù)是將被替換成的新字符:

          function switchChar(which, char) {}

          這樣來調(diào)用:

          switchChar('first''f')

          先實(shí)現(xiàn)更換邏輯,不包含動(dòng)畫效果:

          function switchChar(which, char{
              let letter = {
                  first: {
                      domdocument.querySelector('.word span:first-child'),
                  },
                  last: {
                      domdocument.querySelector('.word span:last-child'),
                  }
              }[which]

              letter.dom.textContent = char
          }

          在點(diǎn)擊事件中調(diào)用 switchChar 函數(shù):

          document.querySelector('.word').onclick = function({
              //step 1: eyes blink animation
              blinkEyes()

              //第2步:獲得下一個(gè)單詞
              let chars = word.getNext()

              //第3步:字符切換動(dòng)畫
              Object.keys(chars).forEach(key => switchChar(key, chars[key]))
          }

          現(xiàn)在運(yùn)行程序的話,在每次點(diǎn)擊之后,單詞兩側(cè)的字符都會(huì)更新。

          接下來寫動(dòng)畫效果,方法和寫眨眼動(dòng)畫類似。這里有兩點(diǎn)要說明,一是因?yàn)橛?first、last 2 個(gè)字符、又有入場、出場 2 個(gè)動(dòng)畫,所以實(shí)際上一共實(shí)現(xiàn)了 4 個(gè)動(dòng)畫效果;二是動(dòng)畫的流程是先讓舊字符出場,再讓新字符入場,而更換字符的操作放置在這 2 個(gè)動(dòng)畫中間,這是用動(dòng)畫 API 的 onfinish 事件實(shí)現(xiàn)的:

          function switchChar(which, char{
              let letter = {
                  first: {
                      domdocument.querySelector('.word span:first-child'),
                      to'-0.5em',
                      from'0.8em',
                  },
                  last: {
                      domdocument.querySelector('.word span:last-child'),
                      to'0.5em',
                      from'-0.8em',
                  }
              }[which]

              let keyframes = {
                  out: [
                      {transform`translateX(0)`filter'opacity(1)'},
                      {transform`translateX(${letter.to})`filter'opacity(0)'},
                  ],
                  in: [
                      {transform`translateX(${letter.from})`filter'opacity(0)'},
                      {transform`translateX(0)`filter'opacity(1)'},
                  ]
              }

              let options = {
                  duration500,
                  fill'forwards',
                  easing'cubic-bezier(0.5, 1.5, 0.5, 1.5)'
              }

              letter.dom
                  .animate(keyframes.out, options)
                  .onfinish = function({
                      letter.dom.animate(keyframes.in, options)
                      letter.dom.textContent = char
                  }
          }

          至此,全部編碼完成。解讀 JS 代碼和解讀 CSS 代碼不一樣,因?yàn)椴皇敲恳恍写a都有視覺效果,很難用語言描述。如果你有不理解的地方,一定是我沒有講清楚,那么請你多看幾遍視頻,仔細(xì)體會(huì)。

          大功告成!

          作者:comehope 原文:https://scrimba.com/p/pEgDAM/cevPbkfB


          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品人妻无码久久久苍井空 | 国产高清免费操逼 | 欧美色乱论视频 | 在线视频 亚洲 | 草热视频在线 |