1. 程式人生 > 實用技巧 >邏輯清晰的 html 歌詞同步滾動實現思路

邏輯清晰的 html 歌詞同步滾動實現思路

效果展示

跟隨歌曲時間同步滾動歌詞,並實現高亮效果

自動播放

改變進度

總體思路

  1. 獲取歌詞
  2. 解析歌詞
  3. 列印歌詞
  4. 同步歌詞

1. 獲取歌詞

用 ajax 和網易雲的 api 獲取的歌詞資源.(涉及到跨域問題,這裡不贅述,主要講同步功能的思路)

2. 解析歌詞

步驟:
1. 新建陣列 lrcArray
2. 提取歌詞 lrcGet
3. 用換行符把字串 lrcGet分 割為陣列 lrc
4. 遍歷 lrc

其中,遍歷 lrc 後的處理步驟

  • 過濾
  • 提取和轉化時間
  • 提取歌詞
  • 新增進陣列 lrcArray

控制檯返回的 lrcGet

lrc

js

      var lrcArray = [];//新建陣列,用於存放歌詞
      var lrcGet = data.lrc.lyric;//提取歌詞
      // console.log(lrcGet);
      var lrc = lrcGet.split('\n');
      // console.log(lrc);


      $.each(lrc, function(i, item) {
        //過濾空白文字
        if (item.split(']')[1] == "" || item == "" || item.indexOf('作曲') !== -1 || item.indexOf('作詞') !== -1) {
          return true;
      }
        //轉化時間
        var timeStr = item.substring(item.indexOf("[") + 1, item.indexOf("]"));
        var min = parseInt(timeStr.split(':')[0]) * 60;
        var sec = parseFloat(timeStr.split(':')[1]);
        var time = parseFloat((min + sec).toFixed(2));
        //新增進陣列
        lrcArray.push({
          t: time,
          c: item.substring(item.indexOf(']') + 1)
        });
      });

3. 列印歌詞

控制檯返回處理後的陣列lrcArray如下:

html程式碼

      <div class="lyrics">
        <ul class="lyricsList"></ul>
      </div>

js

      //顯示歌詞
      //列印全部在頁面
      var html = "";
      $.each(lrcArray, function(i, v) {
        html += '<li>' + v.c + '</li>';
      });
      $('.lyricsList').append(html);

4. 同步歌詞

      $('#audio')[0].ontimeupdate = function() {
        $.each(lrcArray, function(i, v) {
          if ($('#audio')[0].currentTime >= lrcArray[i].t) {
            $('.lyricsList').css('margin-top', '');//避免進度變動時數值產生混亂
            $('.lyricsList li').eq(i).addClass('highlight');
            $('.lyricsList li').eq(i).siblings().removeClass('highlight');
            if (i > 2) {
              $('.lyricsList').css('margin-top', (-i + 2) * 30 + 'px');
            }
          }
        });
      };

思路: audio 時間進度每更新一次,就同步一次該陣列,把audio 的當前時間和陣列每一項的時間作比較.當檢測到前者大於或等於後者,就高亮和滾動對應的歌詞.

關於高亮的思路

以程式碼的角度:

以index(這裡對應 i)為橋樑,只要符合條件,對應的 li 就會先被高亮後被消除高亮,一直到最後一個符合條件的 lrcArray[i].t裡的 i對應的 li被高亮,此時只會高亮而不會消除,等到 i+1 符合條件,i 的 先高亮後消除,i+1只高亮,以此迭代下去.

從樣式上看:

由於程式碼執行飛速,以致肉眼不會看到先高亮後消除的過程,只會看到最後一個被高亮.

關於滾動的思路

從第一行歌詞開始滾動,定格在第三行

html程式碼

      <div class="lyrics">
        <ul class="lyricsList"></ul>
      </div>

css 樣式

    .lyrics {
      height: 32%;
      padding-top: 8px;
      overflow: hidden;
    }
    .lyrics .lyricsList {
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    .lyrics .lyricsList li {
      height: 30px;
      line-height: 15px;
      font-size: 12px;
    }

js 程式碼

      var index = 0;
      $('#audio')[0].ontimeupdate = function() {
        if (this.currentTime >= parseFloat(lrcArray[index].t)) {
          $('.lyricsList li').eq(index).addClass('highlight');
          $('.lyricsList li').eq(index).siblings().removeClass('highlight');
          if (index > 2) {
            //新增 css 樣式以實現滾動效果
            $('.lyricsList').css('margin-top', -(index - 2) * 30 + 'px');
          }
          index++;
        }
      };

js 滾動思路分析圖

從 i>2 起設定margin-top負值.
當滾動到 i=3對應的 li 節點後,margin-top 的值每次增加一個負的 li 的行高,即-30px,以實現推動效果。在高亮效果滾動到下一個 li的同時,ul 容器往上推動一個li行高,兩者作用下的效果為高亮位置保持不變。

注:

  • 在條件程式碼塊的第一行用.css('margin-top', '')消除樣式,避免每一次margin-top 值發生改變引起數值錯亂,避免 bug.

  • 以下在觸發進度條事件時,會發生同步錯誤.
    原因:
    this.currentTime突然變小,而 index 無法隨之改變,因此條件無法匹配.
    this.currentTime突然變大, index也無法突變,只能遞增,會產生逐行高亮又消失,直到匹配到臨界值的情況.

涉及的 jq 方法總結

  • dom 方法
    • $(dom).eq(i) 返回第i 個;
    • $(dom).siblings() dom的兄弟節點,即除了自己,藉助該方法可實現兄弟節點之前的排他性樣式,如歌詞高亮.
    • $(dom).empty() 清空所有子節點
    • $(dom).css('屬性', ''); 消除某個屬性
  • each遍歷中跳出迴圈的方法:
    • return false;——跳出所有迴圈;相當於 javascript 中的 break 效果。
    • return true;——跳出當前迴圈,進入下一個迴圈;相當於 javascript 中的 continue 效果
  • 過濾空白文字和包含關鍵詞文字
  if (item.split(']')[1] == "" || item == "" || item.indexOf('作曲') !== -1 || item.indexOf('作詞') !== -1) {
    return true;
  • 字串
    • str.split('\n')
    • str.substring(str.indexOf("[") + 1, str.indexOf("]")) 提取[]中間的字串
    • str.indexOf(str,str)
  • 數字
    • str.parseInt(),str.parseFloat()
    • num.toFixed(num)
  • 陣列
    • arr.push(item)

完整程式碼

      <div class="lyrics">
        <ul class="lyricsList"></ul>
      </div>
    .lyrics {
      height: 32%;
      padding-top: 8px;
      overflow: hidden;
    }
    .lyrics .lyricsList {
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    .lyrics .lyricsList li {
      height: 30px;
      line-height: 15px;
      font-size: 12px;
    }
    //歌詞高亮
    .highlight {
      color: #3effee;
      text-shadow: 0 0 10px #ded1fc;
    }
function parseLrc(data) {
  // console.log(data);
  var lrcArray = []; //新建陣列,用於存放歌詞
  var lrcGet = data.lrc.lyric; //提取歌詞
  console.log(lrcGet);
  var lrc = lrcGet.split('\n');
  console.log(lrc);


  $.each(lrc, function(i, item) {
    //過濾空白文字
    if (item.split(']')[1] == "" || item == "" || item.indexOf('作曲') !== -1 || item.indexOf('作詞') !== -1) {
      return true;
    }
    //轉化時間
    var timeStr = item.substring(item.indexOf("[") + 1, item.indexOf("]"));
    var min = parseInt(timeStr.split(':')[0]) * 60;
    var sec = parseFloat(timeStr.split(':')[1]);
    var time = parseFloat((min + sec).toFixed(2));
    //新增進陣列
    lrcArray.push({
      t: time,
      c: item.substring(item.indexOf(']') + 1)
    });
  });

  console.log(lrcArray);

  //顯示歌詞
  var html = "";
  $.each(lrcArray, function(i, v) {
    html += '<li>' + v.c + '</li>';
  });
  $('.lyricsList').append(html);

  //同步高亮歌詞
  $('#audio')[0].ontimeupdate = function() {
    $.each(lrcArray, function(i, v) {
      if ($('#audio')[0].currentTime >= lrcArray[i].t) {
        $('.lyricsList').css('margin-top', '');
        $('.lyricsList li').eq(i).addClass('highlight');
        $('.lyricsList li').eq(i).siblings().removeClass('highlight');
        if (i > 2) {
          $('.lyricsList').css('margin-top', (-i + 2) * 30 + 'px');
        }
      }
    });
  };
}

本文作者: 喬一亖
本文連結: https://www.cnblogs.com/joyce33/p/13376752.html
版權宣告: 本文版權歸作者和部落格園共有,轉載請註明出處!如有問題或建議,請多多賜教,非常感謝。