实现歌词滚动效果

文章目录

    • 需求
    • 源码

需求

有一段音频和一个字符串格式的歌词,现欲将二者结合做到歌词随音乐播放歌词滚动的效果,如下图所示

在这里插入图片描述

源码

  • 目录结构
    在这里插入图片描述

  • index.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>
      <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>
    </html>
    
  • css

    • index.css
      * {
        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);
      }
      
  • js

    • data.js
      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]`;
      
    • index.js
      /**
       * 解析歌词字符串
       * 得到一个歌词对象的数组
       * 每个歌词对象:
       * {time:开始时间, words: 歌词内容}
       */
      function parseLrc() {
        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);
        }
        return result;
      }
      
      /**
       * 将一个时间字符串解析为数字(秒)
       * @param {String} timeStr 时间字符串
       * @returns
       */
      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;
        for (var i = 0; i < lrcData.length; i++) {
          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.textContent = lrcData[i].words;
          frag.appendChild(li); // 改动了 dom 树
        }
        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() {
        var index = findIndex();
        var offset = liHeight * index + liHeight / 2 - containerHeight / 2;
        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 = doms.ul.children[index];
        if (li) {
          li.classList.add('active');
        }
      }
      
      doms.audio.addEventListener('timeupdate', setOffset);
      

注:源码取自《渡一教育大师课——袁老师》的讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/329095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows PyCharm远程同步Linux服务器上的项目文件,以及远程连接Linux服务器上的python环境

&#xff08;1&#xff09;上传项目文件到Linux服务器和前置说明 &#xff08;1-1&#xff09;本地项目文件地址&#xff1a;D:\Python_Work\XXX &#xff08;1-2&#xff09;阿里云服务器项目文件地址&#xff1a;/home/XXX &#xff08;1-3&#xff09;Pycharm必须是专业版…

杨中科 .NETCORE EFCORE第七部分 一对一,多对多

一对一 一对一关系配置 1、builder.HasOne(o >o.Delivery).WithOne(d>d.Order).HasForeignKey(d>dOrderId); 2、测试插入和获取数据 示例 新建 Order 新建 Delivery DeliveryConfig OrderConfig 执行 迁移命令 查看数据库 测试数据插入 运行查看数据 多对多…

Qt中ComboBox的简单使用

1.相关说明 combobox中item的文字、data、图片设置 2.界面绘制 3.相关主要代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete …

怎么修改或移除WordPress后台仪表盘概览底部的版权信息和主题信息?

前面跟大家分享『WordPress怎么把后台左上角的logo和评论图标移除&#xff1f;』和『WordPress后台底部版权信息“感谢使用 WordPress 进行创作”和版本号怎么修改或删除&#xff1f;』&#xff0c;其实在WordPress后台仪表盘的“概览”底部还有一个WordPress版权信息和所使用的…

【Python笔记】pip intall -e命令:让你的工程直接使用开源包的源码,可断点调试,修改源码!

最近学习MetaGPT&#xff0c;用到了 pip install -e . 安装命令&#xff0c;这个安装命令是从源代码安装包。 从源代码安装包有几个好处&#xff1a; 包内的代码是可见的&#xff0c;是白盒&#xff0c;不是黑盒&#xff0c;可以直接在项目中看源码断点调试可以直接断到源码里…

看看通义天问AI的水平怎么样?

写在前面 我问了通义千问一个问题&#xff1a;gooey项目改名后的新名字是什么&#xff1f; 这个问题很模糊&#xff0c;它把gooey理解为一个python库&#xff0c;这很正常&#xff0c;毕竟该项目知名度大。 随着对话的进展&#xff0c;我补充了相关信息&#xff0c;将上下文领…

QT软件在线安装与维护

一.安装 安装QT开发环境分离线安装和在线安装两种方式&#xff0c;具体步骤如下&#xff1a; QT官网注册账号----下载安装包-----安装-----选择要安装的版本与开发包----版本维护 注意&#xff1a;Qt5.14.2是最后提供二进制安装包的版本&#xff0c;后面的版本都需要在线安装…

力扣hot100 爬楼梯 线性DP

Problem: 70. 爬楼梯 复杂度 时间复杂度: O ( n ) O(n) O(n) Code class Solution {public int climbStairs(int n){int[] f new int[n 1];f[0] 1;f[1] 1;for (int i 2; i < n; i)f[i] f[i - 1] f[i - 2];return f[n];} }

保证Kafka消息有序性

一、Kafka特性 写入同一个partion分区中的数据是一定有顺序的kafka中一个消费者消费一个partion的数据&#xff0c;消费者取出数据时&#xff0c;也是有顺序的 二、保证消息Kafka消息有序性 在生产者端&#xff0c;应保证消息被写入同一分区。可以在构造消息时指定消息的key…

【技术科普】四大主流芯片架构及其主要优势!

在“算力时代”&#xff0c;数字经济蓬勃发展下数据量呈现爆发式增长&#xff0c;数据价值凸显&#xff0c;从数据的产生到数据的传输、再到计算、处理&#xff0c;都离不开计算芯片&#xff0c;影响着计算芯片的芯片架构到底是什么&#xff1f;有什么作用&#xff1f;今天我们…

Springboot使用参数解析器HandlerMethodArgumentResolver,解析请求头里的数据

HandlerMethodArgumentResolver 是 Spring MVC 中的一个接口&#xff0c;它允许你自定义方法参数的解析过程。当处理请求时&#xff0c;Spring MVC 需要将请求中的信息映射到控制器方法的参数上&#xff0c;而 HandlerMethodArgumentResolver 允许你在这个过程中进行自定义操作…

群晖搭建LDAP服务器实现一个账号登录DSM、Gitea、jellyfin

文章目录 前言安装LDAP Server新建群组新增用户 DSM加入LDAPDSM使用LDAP登录 Gitea配置登录取消其登录权限 Jellyfin配置登录 总结 前言 LDAP&#xff08;轻量级目录访问协议&#xff09;是一种用于访问和管理分布式目录服务的协议&#xff0c;它具有以下好处&#xff1a; 集…

【USTC】verilog 习题练习1-5

1 编写 Verilog 代码&#xff0c;使电路输出信号1 输入格式 无输入 输出格式 输出1&#xff0c;位宽为1 module top_module(output out );// Write your code hereassign out 1; endmodule 2编写 Verilog 代码&#xff0c;使电路输出信号0 输入格式 无输入 输出格式 …

虚拟ip可以解决所有的安全问题吗

虚拟IP&#xff08;Virtual IP&#xff09;是一种网络技术&#xff0c;可以把多台物理服务器或设备组合成一个逻辑集群&#xff0c;并且使用同一个IP地址对外提供服务。虚拟IP具有负载均衡、故障切换和高可用性等优势&#xff0c;同时还可以作为一种安全措施来增加系统的抗攻击…

S32K3系列 --- 硬件I2C Mcal配置

前言 网上看到很多I2C的教程,基本都是模拟I2C,现在S32K3的芯片支持硬件I2C,我想着就配一个硬件的出来吧,这边记录一下,供大家学习。 一、I2C原理 这里主要教大家如何去配置,去使用。 原理的话可以参考这篇文章: 一文搞懂I2C通信总线_i2c通信的详细讲解-CSDN博客 I2…

详解React与Vue的性能对比

React 和 Vue 是当前最流行的前端开发框架之一。它们都具有高度的灵活性和可扩展性&#xff0c;但在某些方面有所不同。在本篇文章中&#xff0c;我将详细介绍 React 和 Vue 这两个技术&#xff0c;并比较它们的优点和缺点。 目录 1. React&#xff1a; 1.1 优点&#xff1a; …

理解二叉树的深度与高度

二叉树深度&#xff1a;任意一个节点到根节点的距离。 二叉树高度&#xff1a;任意一个节点到叶子节点的距离。

如何避免知识付费小程序平台的陷阱?搭建平台的最佳实践

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

数据结构奇妙旅程之二叉树初阶

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

【OpenCV学习笔记16】- 图像阈值

这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子&#xff0c;也会给出自己根据官方的例子完成的更改代码&#xff0c;同样彩蛋的实现也会结合多个知识点一起实现一些小功能&#xff0c;来帮助我们对学会的知识点进行结合应用。 如果有喜欢我笔…