【slick vol3】
動的youtubeスライダーに、
「YouTube Player API」をどうにか適用させたい

  • 【slick vol3】
    動的youtubeスライダーに、「YouTube Player API」をどうにか適用させたい

    JavaScript

    どうも!こんにちは!

    前々回前回の続きにでslickの動的youtubeスライダーの続編となります。

    前回うまくいかなかった、「YouTube Player API」を動的生成したyoutubeスライダーに適用させるをやっていきたいと思います。

    それではスタートです!

    前回の振り返り

    前回の記事でjsonを読み込み、youtubeスライダーを動的に生成していました。

    $.ajax({
              url: '取得するjsonデータへのパス',
              type: 'get',
              dataType: 'json',
              cache: false,
            })
              .done(function (res) {
                const resDatas = res;
                createHtml(resDatas, $('.your-class'), num); // jsonデータを用いて、動的にiframeを生成させる関数
                slideFire($('.your-class')); // createHtml関数で生成したiframeをスライダーにする関数
              })
              .fail(function () {
                console.log('ajax fail');
              });
    
    //  createHtml関数
    function createHtml(datas, elems, nums) {
          const $createElem = '';
          $.each(datas, function (dataIndex, data) {
            const videoId = data.id.videoId;
            $createElem += '<div class="slider">';
            $createElem += '<div class="slider-video">';
            $createElem +=
              '<iframe width="960" height="540" src="https://www.youtube.com/embed/' +
              videoId +
              '?enablejsapi=1" title="YouTube video player" frameborder="0" class="slider-iframe"></iframe>';
            $createElem += '</div>';
            $createElem += '</div>';
          });
          $(elems).append($createElem);
        }
    
    //  slideFire関数
    function slideFire(elems) {
    // スライドする前にpostMessageを送って、youtube動画を一時停止にさせる
      $(elems).on('beforeChange', function (event, slick) { 
        slick = $(slick.$slider)
        playPauseVide(slick, 'pause')
      })
      $(elems).slick({
        dots: true,
       // 以下slickのオプション設定のため割愛
      });
    }
    
    // iframeにpostMessageを送る関数
    function postMessageToPlayer(player, command) { 
      if (player === null || command === null) return;
      player.contentWindow.postMessage(JSON.stringify(command), '*');
    }
    
    // 動画の一時停止関数
    function playPauseVide(slick, control) { 
      let currentSlide, player;
      currentSlide = slick.find('.slick-current');
      player = currentSlide.find('iframe').get[0]
      switch (control) { 
        case 'pause';
          postMessageToPlayer(player, {
            event: 'command',
            func: 'pauseVideo',
            args: '',
          })
          break;
      }
    }

    こんな感じです。

    YouTube Player APIが動的に生成するものに対してうまく動かなかったので、postMessageを使用しiframeの操作を行い、動画の一時停止をすることができたという感じです。

    んで今回は「YouTube Player API」を使用してyoutube動画を制御しますので、postMessage部分の処理などはなくして、以下のソースを元にやっていきたいと思います。

    $.ajax({
         url: '取得するjsonデータへのパス',
         type: 'get',
         dataType: 'json',
         cache: false,
        })
         .done(function (res) {
          const resDatas = res;
          createHtml(resDatas, $('.your-class'), num); // jsonデータを用いて、動的にiframeを生成させる関数
          slideFire($('.your-class')); // createHtml関数で生成したiframeをスライダーにする関数
         })
         .fail(function () {
          console.log('ajax fail');
         });
    
    // createHtml関数
    function createHtml(datas, elems, nums) {
       const $createElem = '';
       $.each(datas, function (dataIndex, data) {
        const videoId = data.id.videoId;
        $createElem += '<div class="slider">';
        $createElem += '<div class="slider-video">';
        $createElem +=
         '<iframe id="player' + (dataIndex + 1) + '" width="960" height="540" src="https://www.youtube.com/embed/' +
         videoId +
         '?enablejsapi=1" title="YouTube video player" frameborder="0" class="slider-iframe"></iframe>';
        $createElem += '</div>';
        $createElem += '</div>';
       });
       $(elems).append($createElem);
      }
    
    // slideFire関数
    function slideFire(elems) {
     $(elems).slick({
      dots: true,
      // 以下slickのオプション設定のため割愛
     });
    }

    YouTube Player APIを使う対応方法

    まず、YouTube Player APIのリファレンスにある通りやってたら動かないです。

    調べた結果、YouTube Player APIをajaxで呼び出すこと動くっぽい記事をstack overflowでチラッと見つけたのでそのやり方で実装してみました。

    んでどこでajaxを呼び出すかですが、slideFire関数でスライドが生成される前に行うといけます。
    なので今回の処理はすべて、slideFire関数内に記述していきます。

    あと、YouTube Player APIを使用する場合、操作するiframeタグに固有のidを振る必要があるので、そこも降っておくようにしてください。
    私の記述ですと以下のようにidを降っておくという感じです。

        $createElem +=
         '<iframe id="player' + (dataIndex + 1) + '" width="960" height="540" 

    実装

    // slideFire関数
    function slideFire(elems) {
    
    // 動的挿入したiframeから、id属性と、youtubeIdを配列として生成する
    const $iframes = $(elems).find('iframe');
    const videoIds = [];
    const iframeIds = [];
    $iframes.each(function (i, elem) {
      const iframeId = $(elem).attr('id');
      const iframeSrc = $(elem).attr('src');
      const iframeSrcSplit = iframeSrc.split('embed/');
      const videoId = iframeSrcSplit[1].split['?'][0];
      videoIds.push(videoId);
      iframeIds.push(iframeId);
    })
    
    // YouTube Player APIのコードをajaxにてダウンロード
    function getApi() { 
      $.ajax({
        url: 'https://www.youtube.com/iframe_api',
        dataType: 'script',
        async: false,
        success: function (data) { },
        error: function (xhr, status, thrown) { 
          getApi()
        }
      })
    }
    getApi()
    
    // YouTube Player APIのonYouTubeIframeAPIReadyを呼び出す
    window.onYouTubeIframeAPIReady = function () { 
      loadPlayer(videoIds, iframeIds)
    }
    // 各youtube動画をYouTube Player APIで操作できるように、
    // 動的生成した分new YT.Playerを生成する。
    let players = [];
    function loadPlayer(videoIdArray, iframeIdArray) { 
      for (let i invideoIdArray ) { 
        let VIDEOID = invideoIdArray[i]
        let IFRAMEID = iframeIdArray[i]
        IFRAMEID = new YT.Player(IFRAMEID, {
          videoId: VIDEOID,
          events: {
            // YouTube Player APIで行いたいイベントを記載
            onStateChange: onPlayerStateChange
          }
        })
        players.push(IFRAMEID)
      }
    }
    
    // YouTube Player APIで一時停止、再生をする関数
    function onPlayerStateChange(event) { 
      if (event.data === 1) { 
        $('slide').slick('slickPause')
      }
      if (event.data === 2) { 
        $('slide').slick('slickPlay')
      }
    }
    
    // slickのイベントをslick発火前に定義する
      $(elems).on('beforeChange', function (event, slick, currentSlide) {
        if (players[currentSlide].getPlayerState() === 1) { 
          players[currentSlide].pauseVideo()
        }
      })
      $(elems).on('afterChange', function (event, slick, currentSlide) {
        if (players[currentSlide].getPlayerState() === 2) { 
          players[currentSlide].playVideo()
        }
      })
    
    // slick発火
     $(elems).slick({
      dots: true,
      // 以下slickのオプション設定のため割愛
     });
    }

    解説

    動的生成したスライダーのidとyoutubeIdを取得する

    ここは、動的生成したyoutubeのiframeにある、id属性と、src属性で設定されているyoutubeのvideoIdを取得する処理です。

    id属性とvideoIdはYouTube Player APIを操作するために必要なので、そこだけを取得し配列に格納しています。

    // 動的挿入したiframeから、id属性と、youtubeIdを配列として生成する
    const $iframes = $(elems).find('iframe');
    const videoIds = [];
    const iframeIds = [];
    $iframes.each(function (i, elem) {
      const iframeId = $(elem).attr('id');
      const iframeSrc = $(elem).attr('src');
      const iframeSrcSplit = iframeSrc.split('embed/');
      const videoId = iframeSrcSplit[1].split['?'][0];
      videoIds.push(videoId);
      iframeIds.push(iframeId);
    })

    YouTube Player APIをajaxで読みに行く

    次にajaxですが、YouTube Player APIのソースを取りに行く関数を設定し呼び出します。
    またajaxがerrorだった場合に、この関数をもう一度呼び出すようにしています。

    // YouTube Player APIのコードをajaxにてダウンロード
    function getApi() { 
      $.ajax({
        url: 'https://www.youtube.com/iframe_api',
        dataType: 'script',
        async: false,
        success: function (data) { },
        error: function (xhr, status, thrown) { 
          getApi()
        }
      })
    }
    getApi()

    YouTube Player APIで操作するためのYT.Player生成

    YouTube Player APIで操作するために、動的生成したスライダー分のYT.Playerを生成しています。
    合わせて、動画の再生・一時停止イベントとしてonPlayerStateChange関数を設定しています。

    // 各youtube動画をYouTube Player APIで操作できるように、
    // 動的生成した分new YT.Playerを生成する。
    let players = [];
    function loadPlayer(videoIdArray, iframeIdArray) { 
      for (let i invideoIdArray ) { 
        let VIDEOID = invideoIdArray[i]
        let IFRAMEID = iframeIdArray[i]
        IFRAMEID = new YT.Player(IFRAMEID, {
          videoId: VIDEOID,
          events: {
            // YouTube Player APIで行いたいイベントを記載
            onStateChange: onPlayerStateChange
          }
        })
        players.push(IFRAMEID)
      }
    }

    YouTube Player APIで操作するonPlayerStateChange関数の作成

    onPlayerStateChange関数ですが、YouTube Player APIのリファレンスに載っている再生ステータスを参考に、

    • youtubeが再生中であれば、slickを一時停止
    • youtubeの再生が止まったらslickを再開

    という処理を追加しています。
    ここは要件になかったのですが、どうせ操作できるので追加しておいた感じです。

    // YouTube Player APIで一時停止、再生をする関数
    function onPlayerStateChange(event) { 
      if (event.data === 1) { 
        $('slide').slick('slickPause')
      }
      if (event.data === 2) { 
        $('slide').slick('slickPlay')
      }
    }

    slickのイベントでスライダーの動きを検知して処理を実行

    slick発火前に、beforeChange、afterChangeのイベントを登録します。

    「beforeChange = スライドが変わる前」なので、スライドが変わる前にカレントスライドのyoutube動画が再生中であれば一時停止させます。

    // slickのイベントをslick発火前に定義する
      $(elems).on('beforeChange', function (event, slick, currentSlide) {
        if (players[currentSlide].getPlayerState() === 1) { 
          players[currentSlide].pauseVideo()
        }
      })

    「afterChange = スライドが変わったあと」なので、
    スライドが変わったあとにカレントスライドのyoutube動画が一時停止中であれば、再生をさせます。

      $(elems).on('afterChange', function (event, slick, currentSlide) {
        if (players[currentSlide].getPlayerState() === 2) { 
          players[currentSlide].playVideo()
        }
      })


    長々となってしまいましたが、処理的には以上です。

    YouTube Player APIの https://www.youtube.com/iframe_api この読み込みがすんなり行けばいいのですが、どうもすんなり行かないようで。。
    動的生成したものに対してYouTube Player APIを効かすには、ajaxで読み込みをするしかなさそうです。

    何かご指摘やご意見ありましたら、お問い合せにてご連絡いただけると幸いです!

    それでは次回もよろしく〜

記事をシェアする