JS / 使用 Youtube Iframe API製作動態歌詞

自從帕妃在THE FIRST TAKE的 愛のしるし出現在youtube的推播上

我又開始聽帕妃的歌了….

想當年是從錄音帶買到CD

在搜尋日文歌詞翻譯的時候

看到一個日語教學網站marumaru

除了提供歌詞翻譯之外,還結合youtube呈現中文/日語動態歌詞

稍微研究了一下背後使用的技術跟方式

自己土炮的結論是

1.透過Youtube Iframe API 建立播放器與取得影片資訊

2.結合 Jquery函示庫 $.doTimeout 設定每100毫秒觸發一個擷取當前影片的播放時間秒數

3.在顯示歌詞的li標籤內自訂一個st屬性,存放歌詞顯示的時間(格式是hh:mm:ss),在Javascript利用自訂函數轉換成秒數值

4.透過判斷式,比較播放時間位於哪兩個歌詞播放區間,取最早時間的歌詞 li 序號

5.知道當前時間應該呈現的歌詞 li 序號,透過Javascript抓取並寫入要呈現的位置,同時修改css效果


重點說明

1.Youtube Iframe API 

範例是用動態的方式引用 js檔,並且嵌入網頁之中

  var tag = document.createElement("script");
  tag.src = "https://www.youtube.com/iframe_api";
  var firstScriptTag = document.getElementsByTagName("script")[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

 

之後是實例化、建立youtube播放器

function onYouTubeIframeAPIReady() {
  var player = new YT.Player("player", {
    height: "390",
    width: "640",
    videoId: "oIoaIlPpIcA",
    events: {
      onReady: onPlayerReady,
      onStateChange: onPlayerStateChange
    }
  });
}

 

videoId就是https://www.youtube.com/watch?v=後面的字串

在events 設定2個觸發事件

第1個 onReady,顧名思義就是播放器載入之後要觸發的函式

第2個 onStateChange,當播放狀態改變時要執行的函試

2.$.doTimeout 

跟原生的setInterval()差不多,不過這個函示庫可以重複利用建立的$.doTimeout 

透過這個函式每1000毫秒執行一次比對

我設定的流程是建立一個範圍在歌詞li 總數內的迴圈

透過當前 跟 當前+1的時間區間 

比對從youtube  API 利用getCurrentTime() 取得的影片播放時間

3.利用自訂函數轉換成秒數值

由於getCurrentTime() 取得的影片播放時間是秒數值

而參考網站在歌詞li st屬性的時間格式是 hh:mm:ss

所以我直接借用網站內的兩個函式來轉換成秒數值(其實是秒數.毫秒數)

4.透過判斷式,取得當前時間的歌詞 li 序號

因為用這種比對方式,在迴圈跑到最後一筆時,會因為沒有再下一筆而產生錯誤

所以又用一個判斷式,當跑到迴圈的最後一筆時,直接設定是li的最後一筆

之後會有3種情況

第1種:當前時間 小於 第1個區間 也就是還沒演唱

r=-1

結束迴圈

第2種:當前時間  介於 某一個時間區間

此時 r = 迴圈目前的位置

結束迴圈(因為只要取第1個符合條件的區間)

第3種:當前時間  大於所有時間區間 也就是結束演唱 沒有歌詞了

r = -2

結束迴圈

      var r;
      for (var i = 0; i < stList; i++) {
        if (i != stList - 1) {
          var st1 = convertLST($(".LyricsYomi").eq(i).attr("st"));     //取得li標籤內的st屬性值,透過convertLST()轉換成秒數.毫秒
          var st2 = convertLST($(".LyricsYomi").eq(i + 1).attr("st"));
        }else{
          var st3 = convertLST($(".LyricsYomi").eq(stList - 1).attr("st"));
        }
        
        //console.log("st1", st1);
        //console.log("st2", st2);
        if (mtime < st1) {
          r = -1;
          console.log(r);
          break;
        } else if (mtime >= st1 && mtime < st2) {
          r = i;
          console.log(r);
          break;
        }else if(mtime >= st3){
          r = -2;
          console.log(r);
          break;
        }
      }

 

5.透過Javascript寫入要呈現的位置,同時修改css效果

透過前面的判斷式

會得到3種 r 值

因此就可以設計不同條件要呈現的內容

if (r > -1) {
        var sn1 = $(".LyricsYomi").eq(r).html();
        var sn2 = $(".Translate_zh").eq(r).html();
        var sn =
          "<li lang='ja' class='li3'>" +
          sn1 +
          "</li><li class='li4'>" +
          sn2 +
          "</li>";
        $("#ly").html(sn);
        $(".LyricsYomi").css("color", "blue");
        $(".LyricsYomi").eq(r).css("color", "red");
        $(".Translate_zh").css("color", "gray");
        $(".Translate_zh").eq(r).css("color", "red");
      }else if (r == -1){
        //$("#ly").html("<li class='li4'>雪花 - 中島美嘉</li>");
      }
      else if(r == -2){
        $("#ly").html("<li class='li4'>雪花 - 中島美嘉</li>");
      }

 

Css與Html設定

為了讓整體頁面都能居中,因此,最外一層的div main 如下設定

display: flex; 
justify-content: center;

第二層div block主要是設定背景顏色與寬度跟高度

重點是設定寬度,這樣才能夠限制後面div都能夠居中,不然都會排成一行

而這個寬度則是依據 youtube播放器的寬度

background: #D4FFFF;
width: 640px;
height: 100%;
padding:10px;

第三層有4個div,主要就是頁面上所有的內容,或者程式要寫入內容的區塊

 

完整的程式碼

雖然是用中島美嘉的歌…..不過之後只要改歌詞內容就可以了

See the Pen Listening to the YouTube Embed IFrame time change events–22222 by 莊幸諺 (@trico109748007) on CodePen.

 

參考資料

Youtube Iframe API 常用功能

Listening to the YouTube Embed Iframe time change events without polling player.getCurrentTime()

D09_Video元件的YouTube影片操作

Youtube 互動

YouTube Player API Reference for iframe Embeds

jQuery TypeError: text is not a function

[jQuery] 筆記 (五) – 選擇器 (selector)

jQuery 设置内容和属性

JS getAttribute()方法:讀取元素的屬性值

認識 parseInt、parseFloat 與 Number 轉換成數字的三種方法

JavaScript toFixed() 方法

【學習筆記】JavaScript – Regex 正則表達式

String.prototype.substr()

js-把秒转化为 *天*小时*分钟*秒

JS將字串轉換成數字