【Vue.js】<video>タグによる動画再生をコンポーネント化

最近 Vue.jsを始めたのですが、一度データ駆動な実装を味わうと従来のイベント駆動な実装は面倒に感じますね。

さて、Vue.jsの他の特徴としてhtml要素とそれに対する振る舞いをカプセル化して再利用可能にするコンポーネントという機能があります。
ここでは例として、htmlのvideoタグによる動画表示と、それの再生・停止をコントロールする要素をコンポーネント化してみます。

今回作成するコンポーネントの要素はこんな感じ。

f:id:kuranabe:20180919203134p:plain

なお、通常のJavaScriptでの実装は再生・停止ボタンのclickイベント時にgetelementbyid等でvideo要素を取得して再生・停止を操作する、まさにイベント駆動な訳ですが、データ駆動の実装では状態に対する振る舞い(再生・停止処理)を宣言的に紐付け、現在の状態をデータ(変数)で管理しています。データの状態を変化させることで振る舞いも変化するという事です。

早速コンポーネントを作成します。

// コンポーネントの定義
let videoComponent = {
    template:
        // パス表示・再生画面・再生/停止ボタンのテンプレート
        `<div>
            <h3>{{videopath}}</h3>
            <video v-bind:src="videopath" v-video="play"></video>
            <div><button @click="play=!play">{{btnstate}}</button></div>
        </div>`
    ,
    data: function(){
        return {
                play: false  // 再生・停止状態
        }    
    },
    directives: {
        video(el, binding){
            binding.value ? el.play() : el.pause();
        }
    },
    computed:{
        btnstate(){
            return this.play ? "■" : "▸";
        }
    },
    props: {
        videopath: String // 呼出元から渡されるビデオのパス
    },
}

// コンポーネントをローカルに登録
new Vue({
    el: "#app",
    components: {
        'video-component' : videoComponent,
    }
})

コンポーネント化するDOMは一塊である必要があるため、親要素としてdivで囲いtemplateに指定します。
動画の制御に用いているplay変数はボタン押下時にtrue/faseが変化し、これに紐付いたvideoのカスタムディレクティブでplay()とpause()の制御をしています。まさにデータ駆動ですね。
また、コンポーネントへの引数としてvideopathをpropsに指定します。これにより、再生する動画のパスを呼出元からコンポーネント側へ渡しています。
こうして作成したコンポーネントは、<video-component>タグで簡単に利用できます。

    <div id ="app">
        <video-component videopath="蜂.mp4"></video-component>
        <video-component videopath="花火.mp4"></video-component>
    </div>

https://github.com/nai-kon/private_static_storage/raw/master/ezgif-5-61aa40d3d5.gif

コンポーネント万歳!