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`;

相关推荐
難釋懷10 分钟前
Nginx获取客户端真实IP
服务器·前端·nginx
甲维斯30 分钟前
GLM5.2超过Opus4.8Think,全球第二了!
前端·人工智能·ai编程
by————组态33 分钟前
Ricon组态系统 - 新一代Web可视化组态平台
前端·后端·物联网·架构·组态·组态软件
JieE21233 分钟前
手把手带你用纯 CSS 实现一个 3D 旋转魔方,这些前端基础你能打几分?
前端·css·html
lichenyang4531 小时前
鸿蒙 Web 容器(二):H5 和 ArkTS 说话前,先定一份「协议」
前端
JYeontu1 小时前
开箱流水加载动画
前端·javascript·css
RANxy1 小时前
AntV 入门系列:G6 图可视化实战
前端
尽欢i1 小时前
Vue3 customRef 封神教程:防抖、本地存储、自动埋点一套搞定,模板干干净净
前端·javascript·vue.js
VOLUN1 小时前
TypeScript封装通用RESTful BaseAPI,后台接口代码精简80%
前端·javascript