Slick×YoutubeAPIを実装する

slickスライダー×Youtube埋め込みでハマったので備忘録として残します。

# ハマったポイント
動画部分でslickのスワイプが効かない。

# 解決策
色々と探しましたが、空divを被せる方法しかなさそうだったので、タップイベントを取得する以下方法を参考にカスタムしました。
[slick.jsとYoutube APIを使って動画スライダーを実装する](https://ce7am7.netlify.app/2018/07/10/how-to-use-youtube-on-slick/)

# 最終完成形
PCでは並べて表示、スマホではslickを有効にして動画をひとつずつスライドさせる。
埋め込み動画はインライン再生とし、コントローラーを表示・操作できるようにする。
オリジナル再生ボタン・サムネイルを設定する。

[Slick×YoutubeAPIを実装する](https://camphor.xsrv.jp/demo/)

よく見ているチャンネルのお気に入り動画を埋め込みました。

### サンプルコード
```html:HTML
<div class="l-movie">
  <ul class="list" id="js-slick">
    <li class="list__item">
      <div class="list__ytwrap">
        <div class="list__yt" videoid="3qcNqfxIRdY"></div>
        <div class="list__thumb"><img src="https://placehold.jp/160/000000/555555/600x340.png?text=01" alt=""></div>
      </div>
    </li>
    <li class="list__item">
      <div class="list__ytwrap">
        <div class="list__yt" videoid="FKbQaUeN7FM"></div>
        <div class="list__thumb"><img src="https://placehold.jp/160/000000/555555/600x340.png?text=02" alt=""></div>
      </div>
    </li>
    <li class="list__item">
      <div class="list__ytwrap">
        <div class="list__yt" videoid="GTYJ3ECLNrw"></div>
        <div class="list__thumb"><img src="https://placehold.jp/160/000000/555555/600x340.png?text=03" alt=""></div>
      </div>
    </li>
    <li class="list__item">
      <div class="list__ytwrap">
        <div class="list__yt" videoid="g6xDB-22WzE"></div>
        <div class="list__thumb"><img src="https://placehold.jp/160/000000/555555/600x340.png?text=04" alt=""></div>
      </div>
    </li>
  </ul>
</div>
```
```sass:SASS
.l-movie{
  width: 100%;
  position: relative;
  margin-bottom: 50px;
  .list{
    width: 90vw;
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    @media screen and (max-width: 750px) {
      width: 100%;
      display: inline-block;
    }
    &__item{
      width: 48%;
      margin-bottom: 40px;
      margin-right: 4%;
      position: relative;
      &:nth-of-type(2n){
        margin-right: 0;
      }
      &:last-of-type{
        margin-right: 0;
      }
      @media screen and (max-width: 750px) {
        width: 100%;
        margin-bottom: 0;
        margin-right: 0;
      }
    }
    &__ytwrap{
      position: relative;
      width: 100%;
      padding-bottom: 56.5%;
      overflow: hidden;
      -webkit-overflow-scrolling: touch;
    }
    &__yt{
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
      @media screen and (max-width: 750px) {
        opacity: 0;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
    }
    &__thumb{
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
      cursor: pointer;
      width: 100%;
      height: 100%;
      > img{
        width: 100%;
        height: auto;
      }
      &::before, &::after{
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        transition: all .2s easeInSine;
        z-index: 2;
      }
      &::after{
        content: "";
        width: 72px;
        height: 72px;
        border: 2px solid #fff;
        border-radius: 50%;
      }
      &::before{
        content: "";
        width: 0;
        height: 0;
        border-style: solid;
        border-width: 9px 0 9px 14px;
        border-color: transparent transparent transparent #fff;
        z-index: 3;
      }
      @media screen and (min-width: 750px) {
        &:hover{
          &::after{
            background-color: #fff;
          }
          &::before{
            border-color: transparent transparent transparent #000;
          }
        }
      }
    }
    &__mask{
      @media screen and (max-width: 750px) {
        width: 100%;
        height: 70%;
        position: absolute;
        top: 0;
        left: 0;
        z-index: 4;
      }
    }
  }
}
```
```js:JS
/*  YouTube  */
var ytPlayers = [];
var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onYouTubeIframeAPIReady() {
  if ($(window).width() < 750) {
    $('.list__ytwrap').append('<div class="list__mask"></div>');
  }
  $('.list__yt').each(function(i,elem){
    embedYoutube(i,elem);
  });
}
function embedYoutube(i,elem){
  var videoId = $(elem).attr('videoid');
  var playerId = videoId + '_' + i;
  $(elem).attr('id', playerId);
  ytPlayers[playerId] = ytInit(playerId, videoId);
  hammerInit(playerId);
}
function ytInit(playerId, videoId){
  var ytPlayer = new YT.Player(
    playerId,
    {
      videoId: videoId,
      playerVars: {
        enablejsapi: 1,
        rel:0,
        controls: 1,
        playsinline: 1,
        fs: 1,
        wmode: 'transparent',
        origin: location.protocol + '//' + location.hostname + "/"
      },
      events: {
        onReady: onPlayerReady,
        onError: onPlayerError
      }
    }
  );
  return ytPlayer;
}
function hammerInit(playerId){
  if ($(window).width() < 750) {
    var hammerObj = new Hammer($('#' + playerId + ' ~ .list__mask' ).get(0));
  }else{
    var hammerObj = new Hammer($('#' + playerId + ' ~ .list__thumb').get(0));
  }
  hammerObj.on('tap click', function(event){
    console.log(ytPlayers[playerId].getPlayerState())
    ytPlayers[playerId].getPlayerState() === 1 ? ytPlayers[playerId].pauseVideo() : ytPlayers[playerId].playVideo();
  });
  if ($(window).width() < 750) {
    $('.list__mask').on('tap',function() {
      var thumb = $(this).prev('.list__thumb');
      thumb.prev('.list__yt').css('opacity','1');
      thumb.hide();
    });
  }else{
    $('.list__thumb').on('click',function() {
      $(this).hide();
    });
  }
}
function onPlayerReady(event) {
  // event.target.playVideo();
}
function onPlayerError(event) {
  console.log(e)
}

/*  slick  */
$(function () {
  var win = $(window);
  var slider = $("#js-slick");

  win.on("load resize", function () {
    if (win.width() < 768) {
      slider.not(".slick-initialized").slick({
        accessibility: true,
        draggable: true,
        arrows: true,
        swipe: true,
        dots: true,
        autoplay: false,
        infinite: true,
        cssEase: 'ease',
        slidesToShow: 1,
        slidesToScroll: 1
      });
    } else if (slider.hasClass("slick-initialized")) {
      slider.slick("unslick");
    }
  });

  slider.on('afterChange', function(event, slick, currentSlide){
    Object.keys(ytPlayers).forEach(function(key) {
      var ytPlayer = this[key];
      console.log(key, ytPlayer);
      ytPlayer.pauseVideo();
    }, ytPlayers);
  });
});
```

#解説
元のソースだとコントローラーが触れなくなるので、被せるdivのサイズを調整し、コントローラー分は触れるように調整。

```sass:CSS
&__mask{
  @media screen and (max-width: 750px) {
    width: 100%;
    height: 70%;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 4;
  }
}
```

PCではボタンにhover効果をつけたかった＆slick不要なので、空divはスマホのときのみ生成されるように変更。

```js:JS
if ($(window).width() < 750) {
  $('.list__ytwrap').append('<div class="list__mask"></div>');
}
```

PCではサムネイルをクリック・スマホではmaskをタップでサムネイル非表示＆再生開始するように変更。
スワイプ中に埋め込み動画がサムネイルより先に表示されてしまう不具合があったため、初期状態では埋め込み動画はopacity:0で非表示にし、タップでopacity:1になるよう調整も追加。

```js:JS
if ($(window).width() < 750) {
  $('.list__mask').on('tap',function() {
    var thumb = $(this).prev('.list__thumb');
    thumb.prev('.list__yt').css('opacity','1');
    thumb.hide();
  });
}else{
  $('.list__thumb').on('click',function() {
    $(this).hide();
  });
}
```
以上。