web前端渡一大师课 03 歌词滚动效果

效果图: 歌词会随播放滚动

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="shortcut icon" href="./assets/favicon.ico" type="image/x-icon" />
  <link rel="stylesheet" href="./css/index.css" />
</head>
<style>
  * {
    margin: 0;
    padding: 0;
  }

  body {
    background: #000;
    color: #666;
    text-align: center;
  }

  audio {
    width: 450px;
    margin: 30px 0;
  }

  .container {
    height: 420px;
    overflow: hidden;
    /* border: 2px solid #fff; */
  }

  .container ul {
    /* border: 2px solid #fff; */
    transition: 0.6s;
    list-style: none;
  }

  .container li {
    height: 30px;
    /* border: 1px solid #fff; */
    line-height: 30px;
    transition: 0.2s;
  }

  .container li.active {
    color: #fff;
    /* font-size: ; */
    transform: scale(1.2);
  }
</style>

<body>
  <audio controls src="./assets/music.mp3"></audio>
  <div class="container">
    <ul class="lrc-list"></ul>
  </div>
   //引入的歌词文件
  <script src="./js/data.js"></script>
  <!-- <script src="./js/index.js"></script> -->
</body>

<script>
  /**
    * 解析歌词字符串
    * 得到一个歌词对象的数组
    * 每个歌词对象: 
    * {time:开始时间,words:歌词内容}
  */

  // [00:01.06]难念的经
  function parseLrc() {
    console.log(lrc);
    // 1. 分割字符串,得到一个数组
    var lines = lrc.split('\n');
    //存放歌词对象的数组
    var result = [];
    //循环遍历数组,拿到每一行的字符串
    for (var i = 0; i < lines.length; i++) {
      var str = lines[i];
      //分割字符串
      var parts = str.split(']');
      //截取时间
      var timeStr = parts[0].substring(1);
      //添加一个对象
      var obj = {
        time: parseTime(timeStr),
        words: parts[1] || '',
      };
      result.push(obj);
      //得到{time: 229.99, words: '天阔阔雪漫漫共谁同航'}
    }
    return result;
  }
  /**
   * 将时间字符串转换为秒数
   * @param {string} timeStr 时间字符串,格式为"mm:ss"
   * @returns {number} 返回转换后的秒数
   */
  function parseTime(timeStr) {
    var parts = timeStr.split(':');
    return +parts[0] * 60 + +parts[1];
  }

  var lrcData = parseLrc();

  //获取需要的dom 
  var doms = {
    audio: document.querySelector('audio'),
    ul: document.querySelector('.container ul'),
    container: document.querySelector('.container'),

  };

  /**
   * 计算出,在当前播放器播放到第几秒的情况下
   * lrcData数组中,应该高亮显示的歌词下标
   * 如果没有任何一句歌词需要显示,则得到-1
   */
  function findIndex() {
    //获取当前播放器的时间
    var curTime = doms.audio.currentTime;
    //遍历lrcData数组
    for (var i = 0; i < lrcData.length; i++) {
      console.log('i', lrcData[i]);
      //如果当前时间小于lrcData[i].time
      //说明当前时间还没有到达这一句歌词的开始时间
      //此时,应该高亮显示的歌词下标就是i-1
      if (curTime < lrcData[i].time) {
        return i - 1;
      }
    }
    //找遍了都没找到(说明播放到最后一句)
    return lrcData.length - 1;
  }


  //界面 创建歌词元素 li  
  function createLrcElements() {
    //文档片段
    var frag = document.createDocumentFragment();
    for (var i = 0; i < lrcData.length; i++) {
      var li = document.createElement('li');
      li.innerText = lrcData[i].words;
      //添加到ul中, 改动了 dom 树 
      // doms.ul.appendChild(li);
      frag.appendChild(li);
    }
    doms.ul.appendChild(frag);
  }
  createLrcElements();

  //尺寸信息 

  //容器高度
  var containerHeight = doms.container.clientHeight;
  // 每个li的高度
  var liHeight = doms.ul.children[0].clientHeight;
  //最大偏移量
  var maxOffset = doms.ul.clientHeight - containerHeight;


  //设置 ul 元素的偏移量 
  function setOffset() {
    console.log('调用了 setOffset');
    //获取当前需要高亮显示的歌词下标
    var index = findIndex();
    //偏移量计算 
    var offset = liHeight * index + liHeight / 2 -
      //容器的一半  
      containerHeight / 2;
    console.log('偏移量', offset);
    //边界判断 最小值
    if (offset < 0) {
      offset = 0;
    }
    if (offset > maxOffset) {
      offset = maxOffset;
    }
    doms.ul.style.transform = `translateY(-${offset}px)`;

    //去掉之前的active样式
    var li = doms.ul.querySelector('.active');
    if (li) {
      li.classList.remove('active');
    }
    //重新获取需要高亮显示的li 
    li = doms.ul.children[index];
    if (li) {
      li.classList.add('active');
    }
  }

  //时间改变的事件 
  doms.audio.addEventListener('timeupdate', setOffset
    //每次播放器时间更新,都需要重新计算偏移量
  );

  //初始偏移量
  setOffset();
</script>

</html>

歌词

var lrc = `[00:01.06]难念的经

00:03.95\]演唱:周华健 \[00:06.78

00:30.96\]笑你我枉花光心计 \[00:34.15\]爱竞逐镜花那美丽 \[00:36.75\]怕幸运会转眼远逝 \[00:39.32\]为贪嗔喜恶怒着迷 \[00:41.99\]责你我太贪功恋势 \[00:44.48\]怪大地众生太美丽 \[00:47.00\]悔旧日太执信约誓 \[00:49.66\]为悲欢哀怨妒着迷 \[00:52.56\]啊 舍不得璀灿俗世 \[00:57.66\]啊 躲不开痴恋的欣慰 \[01:02.86\]啊 找不到色相代替 \[01:08.09\]啊 参一生参不透这条难题 \[01:13.15\]吞风吻雨葬落日未曾彷徨 \[01:15.73\]欺山赶海践雪径也未绝望 \[01:18.23\]拈花把酒偏折煞世人情狂 \[01:20.90\]凭这两眼与百臂或千手不能防 \[01:23.76\]天阔阔雪漫漫共谁同航 \[01:26.09\]这沙滚滚水皱皱笑着浪荡 \[01:28.68\]贪欢一刻偏教那女儿情长埋葬 \[01:32.38

01:34.09\]吞风吻雨葬落日未曾彷徨 \[01:36.50\]欺山赶海践雪径也未绝望 \[01:39.07\]拈花把酒偏折煞世人情狂 \[01:41.69\]凭这两眼与百臂或千手不能防 \[01:44.68\]天阔阔雪漫漫共谁同航 \[01:46.93\]这沙滚滚水皱皱笑着浪荡 \[01:49.54\]贪欢一刻偏教那女儿情长埋葬 \[01:53.41

02:15.45\]笑你我枉花光心计 \[02:18.53\]爱竞逐镜花那美丽 \[02:21.14\]怕幸运会转眼远逝 \[02:23.76\]为贪嗔喜恶怒着迷 \[02:26.43\]责你我太贪功恋势 \[02:28.98\]怪大地众生太美丽 \[02:31.60\]悔旧日太执信约誓 \[02:34.26\]为悲欢哀怨妒着迷 \[02:36.90\]啊 舍不得璀灿俗世 \[02:42.04\]啊 躲不开痴恋的欣慰 \[02:47.34\]啊 找不到色相代替 \[02:52.52\]啊 参一生参不透这条难题 \[02:57.47\]吞风吻雨葬落日未曾彷徨 \[03:00.05\]欺山赶海践雪径也未绝望 \[03:02.64\]拈花把酒偏折煞世人情狂 \[03:05.27\]凭这两眼与百臂或千手不能防 \[03:08.22\]天阔阔雪漫漫共谁同航 \[03:10.49\]这沙滚滚水皱皱笑着浪荡 \[03:13.06\]贪欢一刻偏教那女儿情长埋葬 \[03:18.45\]吞风吻雨葬落日未曾彷徨 \[03:20.90\]欺山赶海践雪径也未绝望 \[03:23.54\]拈花把酒偏折煞世人情狂 \[03:26.21\]凭这两眼与百臂或千手不能防 \[03:29.07\]天阔阔雪漫漫共谁同航 \[03:31.32\]这沙滚滚水皱皱笑着浪荡 \[03:33.92\]贪欢一刻偏教那女儿情长埋葬 \[03:39.32\]吞风吻雨葬落日未曾彷徨 \[03:41.84\]欺山赶海践雪径也未绝望 \[03:44.38\]拈花把酒偏折煞世人情狂 \[03:47.04\]凭这两眼与百臂或千手不能防 \[03:49.99\]天阔阔雪漫漫共谁同航 \[03:52.20\]这沙滚滚水皱皱笑着浪荡 \[03:54.89\]贪欢一刻偏教那女儿情长埋葬 \[04:00.28\]吞风吻雨葬落日未曾彷徨 \[04:02.68\]欺山赶海践雪径也未绝望 \[04:05.25\]拈花把酒偏折煞世人情狂 \[04:07.90\]凭这两眼与百臂或千手不能防 \[04:10.85\]天阔阔雪漫漫共谁同航 \[04:13.08\]这沙滚滚水皱皱笑着浪荡 \[04:15.75\]贪欢一刻偏教那女儿情长埋葬 \[04:19.48\]\`;

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax