Nuxt.jsとBuefyでYoutubeプレイヤーを作るメモ


先日のハッカソンでこういうのを作ってみました。自分用メモです。

> https://djuko.cf

> <img src="https://i.gyazo.com/b9623e7385a722ffeb1cb0da730ff247.png" width="600px"> 

Youtubeを再生させつつ、そのリストを右側に表示させます。

最終的にはこんな感じのリスト自動選択まで

> <img src="https://i.gyazo.com/dc4135b4a2ec3fe65a14d724e5b1d06a.gif" width="600px">

ちなみにYoutubeプレイヤーはこちらをそのまま利用

> 参考: [Nuxt.js 2でYoutubeプレイヤー埋め込み](https://qiita.com/n0bisuke/items/d8ae0a9ca23a691a439e)

## BuefyのTable(b-table)を利用

今回create-nuxt-appでアプリ作成する際にCSSフレームワークで[Buerfy](https://buefy.org/documentation/table#selection)を選択したのでこの枠組みのまま作ってみます。

> https://buefy.org/documentation/table#selection
> <img src="https://i.gyazo.com/0af9de15e349748b1ff5e9117d02a361.png" width="400px"> 


`b-table`というコンポーネントが用意されていて、これが結構便利だったので利用してみました。

### シンプルな利用

シンプルな利用だとこんな感じ

```Player.vue

<template>
//省略

<b-table
  :data="data"
  :columns="columns"
  ></b-table>
          
//省略
</template>

<script>
//省略
  data(){
    return{
      //省略
      
      data: <再生リスト>,
      columns: [
        {
            field: 'rank',
            label: 'DAM RANK/DAY',
            numeric: true,
        },
        {
            field: 'title',
            label: 'TITLE',
        },
      ],
    }
  }
//省略
</script>
```

> <img src="https://i.gyazo.com/367e0b22943039abb1dd60fb29b332af.png" width="400px"> 

キレイに表示されますね。

ただ、これだけだとただの表なので現在何が再生されているかの選択がされていないため、プレイヤー的には物足りないです。

### セレクションを有効にする

`selected.sync`というプロパティを有効にしてみます。

> <img src="https://i.gyazo.com/781cbc88c3481c41403d88502a506f18.png" width="400px">

どこが再生されているか分かるようになりました。

`this.selected`に再生中の音楽データを入れ込むようにしてあげると、b-tabel側で選択状態になります。ここのロジック部分はYoutube Player API側の説明が長くなりそうなのでカット。

```Player.vue

<template>
        
//省略
<b-table
  :data="data"
  :columns="columns"
  :selected.sync="selected"
  ></b-table>

//省略

</template>

<script>
//省略
  data(){
    return{
      //省略
      
      data: <再生リスト>,
      columns: [
        {
            field: 'rank',
            label: 'DAM RANK/DAY',
            numeric: true,
        },
        {
            field: 'title',
            label: 'TITLE',
        },
      ],
      selected: {}, //再生中の音楽データ
    }
  }
//省略
</script>
```

### テーブルのスクロールに対応する(stickyHeaders)

このままだとこんな感じで再生リストが長くなった際にページがものすごく長くなってしまって操作が大変になります。

> これを
> <img src="https://i.gyazo.com/bf2bbd4ddbd0ebb82d9ea03a1ccb575c.gif" width="400px"> 

↓

> こうします
> <img src="https://i.gyazo.com/8c3a8150a0f6effbe44bbc2b1af189de.gif" width="400px"> 

`sticky-header`のプロパティを有効にします。
`<template>`側では`:sticky-header="stickyHeaders"`を追加、`<script>`側ではdataに`stickyHeaders: true`を追加します。

```Player.vue

<template>
        
//省略
<b-table
  :data="data"
  :columns="columns"
  :selected.sync="selected"
  :sticky-header="stickyHeaders"
  ></b-table>

//省略

</template>

<script>
//省略
  data(){
    return{
      //省略
      
      data: <再生リスト>,
      columns: [
        {
            field: 'rank',
            label: 'DAM RANK/DAY',
            numeric: true,
        },
        {
            field: 'title',
            label: 'TITLE',
        },
      ],
      selected: {}, //再生中の音楽データ
      stickyHeaders: true //スクロール対応
    }
  }
//省略
</script>
```

## 自動的にスクロールさせる

ここからはb-tableの話というよりはVue.jsやJavaScriptの話

現状だと、スクロール対応は出来てるのですが、出来ればページを開いたときに自動的にその要素を表示させたいですよね。

> こんな感じ
> <img src="https://i.gyazo.com/8dd4c9217b4f6f93bcdddad3b58b8201.gif" width="400px">

ここが結構情報がなくて大変でした。

* 1. セレクトされている要素の特定
* 2. 特定した要素にスクロールさせる

結果この二つのステップで実装できました。

### 1. セレクトされている要素の特定

Vue.jsだと`this.$el`で仮想DOMの要素アクセスができるっぽい?です。
(認識間違ってたらコメントください。)

> 参考: [DOM要素に対応するvue.jsコンポーネントを見つけるには](https://www.it-swarm.dev/ja/javascript/%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8Bvuejs%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%B8%E3%81%AEdom%E8%A6%81%E7%B4%A0/1050571125/)

b-tableで選択中のクラスは`.b-table`と`.is-selected`のクラスで検索できるのでこんな感じで検索できます。

```js
this.$el.querySelector('.b-table .is-selected');
```

### 2. 特定した要素にスクロールさせる

`scrollIntoView()`を使って実装できました。これは通常のJSのメソッドですが、あまり使ったことなかったので存在を初めて知りました。

> 参考: [あまり知られてなさそうなメソッド element.scrollIntoView()](https://qiita.com/amamamaou/items/728d571d508347b2bc82)

### まとめてみる

こんな感じです。今回僕の実装だと、再生後にセレクションをしているので、そのまま書くと絶妙なタイミングで`is-selectedクラス`が付与されずうまくスクロールされなかったので`setTimeout()`で若干タイミングをずらしています。

```js
  //スクロール
  setTimeout(()=>{
    const nowVideoElm = this.$el.querySelector('.b-table .is-selected');
    nowVideoElm.scrollIntoView({
      behavior: 'smooth',
      block: 'center',
    });
  },100);
```

`behavior: 'smooth'`に設定するとアニメーションがいい感じですね。

## まとめ

`b-table`、`scrollIntoView()`、`this.$el`などを組み合わせて使ってみましたがけっこう便利だしユースケースあるかもしれないですね。

割とこのアニメーション好きです。

> https://djuko.cf
> <img src="https://i.gyazo.com/dc4135b4a2ec3fe65a14d724e5b1d06a.gif" width="600px">