最近では見かけることが少なくなったタイプライターアニメーション。

私も、まだ現役でjQueryを使用していた時に実装経験があり、割と面倒だった覚えがあります。

ですが、以前に比べasyncawaitが出てきたことにより、実装しやすくなっている気がします。

ということで、早速Vue2でタイプライターアニメーションを作成してみましょう。

# この記事で解決すること

  • 当サイトのTOPページのローディングアニメーションにあるようなタイプライターが実装できる
    ※タイプライターのみです

 

前提

表題にもある通り、今回はVue2で実装します。

ですが、重要なのはどのようなmethodsを組むかという点にあるので、Vue3への流用もある程度できるのではないかと思います。

また、コンポーネント化したいのでSFC構文で作成します。

  • Vueを実装できる環境
  • Vue Templateをコンパイル・トランスパイルできる環境

フォルダ構成

  • folder
    • src
      • js
        • app.js
        • components
          • typelighter.vue
    • index.html

手順

  1. Vue Templateの初期設定
  2. methodsの処理
    • 乱数の取得
    • 文字出力アニメーションの設定
    • アニメーションループの作成
  3. 実装
    • mountedに実装
    • componentの読み込み
    • templateの挿入
    • 実働確認

    Vue Templateの初期設定

    今回はSFC構文ですので、htmlとcssもtemplate内に入れ込みます。

    typelighter.vue

    <template>
      <div class="typelighter-text" v-text="returnText + randText"></div>
    </template>
    
    <style>
      .typelighter-text {
        font-family: sans-serif;
        font-weight: bold;
        letter-spacing: .3rem;
      }
    </style>
    
    <script>
      export default {
        props: {
          text: {
            type: String,
            default: 'Hello World',
            require: true
          },
        },
        data() {
          return {
            returnText: '',
            randText: ''
          }
        },
        methods: {
        },
      }
    </script>
    props
    text htmlに挿入するカスタムブロックでテキストを設定したいので、
    propsにはtextを設定しておきます。
    default: ‘Hello World’(必須ではない)
    require: true
    data
    returnText props:textの文字を1文字づつ入れるために設定
    randText 文字が表示されるまでの間にランダムで表示される文字を入れるために設定

    # point

    returnTextrandTextに分けている理由は「実際に表示したい文字」と「その文字が表示されるまでの間にランダムで表示される文字」を分けておきたかったからです。

    例えば「あいう」という文字をタイプライター形式で表示する場合
    「$」→「あ$」→「あい$」→「あいう」(「$」はランダムな文字と仮定する)
    にするためです。

    methodsの処理

    乱数の取得

    ランダムな文字を表示したい場合、表示前の段階でランダムな文字を生成する必要があります。

    typelighter.vue: methods

    getRandom() {
    
      // 表示されるランダムな文字の設定(47文字:何文字でもOK)
      let randStr = "abcdefghijklmnopqrstuvwxyz0123456789<>$&!'()%[]"
    
      // ランダムな文字を配列に変換
      let randAry = [...randStr]
    
      // 0~46(47-1)の中でランダムな数字を計算(x番目と仮定する)
      let randNum = Math.floor(Math.random() * (randAry.length - 1))
    
     // randAryのx番目の文字をreturn
      return randAry[randNum]
    },

    文字出力アニメーションの設定

    ランダムな文字を規定回数出力し終わったらprops:textの文字をreturnするというmethodになります。
    1文字づつこのmethodを繰り返す必要がありますので、最終でこのmethodをループさせることになります。

    typelighter.vue: methods

    // textには、ランダムな文字を規定回数出力し終わった時点で返る文字が入ります(次項で設定)
    returnStr(text) {
    
      // ループカウントをリセット
      let cnt = 0
    
      // 規定回数の設定
      let cntMax = 3
    
      // Promise
      return new Promise((resolve) => {
    
        // 25msでループ(一番違和感がなかった)
        setInterval(() => {
          
          // ループカウントが規定回数に達しているかの判定
          if (cnt == cntMax) {
    
         // 規定回数に達していた場合は text を返す
            resolve(text)
    
          } else {
    
            // 規定回数に達していない場合は ランダムな文字を出力してループカウントを++
            this.randText = this.getRandom()
            cnt ++
          }
        }, 25)
      })
    },

    props:textを1文字づつ処理しますので、1文字終わったら次の文字にいかないといけません。

    そのためには、秒数で管理するか、asyncawaitを使用するかの実質2択になってきます。(watchでも頑張ったらできそうですが…)

    11, 20行目

    次項で使用するasyncawaitのために、処理が終わったらtextPromise Objectとして返す設定にしています。

    8, 28行目

    この行でアニメーションの速度などを制御しています。

    25msを3回ループするので、1文字75msで終わることになります。

    アニメーションループの作成

    では、先程作成したアニメーションをループします。

    typelighter.vue: methods

    async printText() {
    
      // props:text の文字数分だけループさせる
      // text='test'の場合、['t', 'e', 's', 't'] がループで回る
      for await (let text of [...this.text]) {
    
        // returnStrが終わったらreturnTextにtextが入る
        this.returnText += await this.returnStr(text)
      }
    
      // すべての文字を出力し終わったらランダムな文字は必要ないのでバイバイ
      this.randText = ''
    },

    asyncawaitが出てきてから、アニメーションとかでよく使う、「○○が終わるまで〇〇の処理は待ってね」っていうのが本当に楽になりましたね。

    ということで、methodsの処理は完了となります。

    実装

    mountedに実装

    mountedにthis.printText()を書いて.Vueファイルの処理は完了です。

    typelighter.vue: mounted

    mounted() {
      this.printText()
    },

    componentの読み込み

    htmlに挿入するには、jsファイル内でcomponentを使用することを書かなくてはいけません。

    Vue2ではこんな感じです。

    app.js

    import typelighter from './components/typelighter.vue'
    
    new Vue({
      el: '#typelighter',
      components: { typelighter },
    })

    templateの挿入

    問題なくcomponentが読み込めていれば、あとはhtmlに挿入するだけです。

    index.html

    <div id="typelighter">
      <typelighter text="Hello World" />
    </div>

    実働確認

    実際に動かすとこんな感じです。

    See the Pen
    Untitled
    by [ brandnew] (@brandnew-work)
    on CodePen.

    最後に

    いかがでしたでしょうか。

    asyncawaitを使えば、たった3methodsでタイプライターが実装できることがわかりましたね!

    当サイトのTOPのローダーのようにするには、上記にプラスしてcssアニメーションとasyncawaitで実装できますので、みなさんも試してみてください。

    それよりも「Highlighting Code Block」に「Vue Template」のシンタックスなくて2記事目にしてコードハイライターのプラグイン変えてやろうかと思いました。。。

    それでは皆様よいVueライフを!