利用原生HTML + CSS + JS实现歌词滚动

        对于很多音乐APP,都有这么一个功能,就是根据歌曲的进度来控制对应的歌词滚动,如下图所示:

大概这样的效果,我此次是使用原生的HTML+CSS+JS来实现的,以下是具体的实现过程。

1. 数据获取与处理

        对于数据来源,这里由于只有前端展示,所以我们直接使用死数据,杰伦的《我不配》,数据如下(音频可以找我要):

歌词数据如下:

var lrc = `[00:00.06]︿☆我不配☆︿
[00:00.75]
[00:01.11]演唱:周杰伦
[00:02.62]
[00:03.35]︿☆歌词制作:ikun
[00:06.13]→QQ:2682548155←
[00:09.30]www.90lrc.cn ★【歌词网】
[00:11.09]
[00:18.40]这街上太拥挤 太多人有秘密
[00:22.66]玻璃上有雾气 在被隐藏起过去
[00:27.10]你脸上的情绪 在还原那场雨
[00:31.61]这巷弄太过弯曲 走不回故事里
[00:36.00]
[00:36.10]这日子不再绿 又斑驳了几句
[00:40.49]剩下搬空回忆的我在大房子里
[00:44.92]电影院的座椅 隔遥远的距离
[00:49.24]感情没有对手戏 你跟自己下棋
[00:53.70]
[00:53.80]还来不及 仔仔细细写下你的关于
[01:02.65]描述我如何爱你 你却微笑的离我而去
[01:10.90]
[01:11.50]这感觉 已经不对 我努力在挽回
[01:15.90]一些些 应该体贴的感觉 我没给
[01:20.32]你嘟嘴 许的愿望很卑微 在妥协
[01:24.50]是我忽略 你不过要人陪
[01:29.10]
[01:29.19]这感觉 已经不对 我最后才了解
[01:33.57]一页页 不忍翻阅的情节 你好累
[01:38.03]你默背 为我掉过几次泪 多憔悴
[01:42.30]而我心碎 你受罪你的美 我不配
[01:49.58]
[02:04.98]这街上太拥挤 太多人有秘密
[02:09.37]玻璃上有雾气 在被隐藏起过去
[02:13.80]你脸上的情绪 在还原那场雨
[02:18.25]这巷弄太过弯曲 走不回故事里
[02:22.61]
[02:22.82]这日子不再绿 又斑驳了几句
[02:27.26]剩下搬空回忆的我在大房子里
[02:31.58]电影院的座椅 隔遥远的距离
[02:35.99]感情没有对手戏 你跟自己下棋
[02:40.24]
[02:40.26]还来不及 仔仔细细写下你的关于
[02:49.04]描述我如何爱你 你却微笑的离我而去
[02:57.42]
[02:58.20]这感觉 已经不对 我努力在挽回
[03:02.56]一些些 应该体贴的感觉 我没给
[03:06.98]你嘟嘴 许的愿望很卑微 在妥协
[03:11.10]是我忽略 你不过要人陪
[03:15.50]
[03:15.78]这感觉 已经不对 我最后才了解
[03:20.20]一页页 不忍翻阅的情节 你好累
[03:24.66]你默背 为我掉过几次泪 多憔悴
[03:28.98]而我心碎 你受罪你的美 我不配
[03:36.12]
[03:47.30]这感觉 已经不对 我努力在挽回
[03:51.38]一些些 应该体贴的感觉 我没给
[03:55.79]你嘟嘴 许的愿望很卑微 在妥协
[04:00.00]是我忽略 你不过要人陪
[04:04.54]
[04:04.64]这感觉 已经不对 我最后才了解
[04:09.03]一页页 不忍翻阅的情节 你好累
[04:13.64]你默背 为我掉过几次泪 多憔悴
[04:17.95]而我心碎 你受罪你的美 我不配
[04:25.70]`

问题来了,这个数据是一条又丑又长的字符串,我们需要把他解析成对象才好处理啊,因此,第一件事应该是写解析函数:

const parseTime = (arr) => { //将时间解析成秒slet times = arr.split(":");let seconds = parseFloat(times[0])*60+parseFloat(times[1]);return seconds;
}
export const parseLrc = () => { //解析字符串let result = [];let lines = lrc.split("\n");for (let i = 0; i < lines.length; i++) {let line = lines[i];let arrs = line.split("]");let obj = {time: parseTime(arrs[0].substring(1)),text: arrs[1]}result.push(obj);}return result;
}

2. 页面设计

之后我们把HTML页面和CSS样式大概写好,这里比较简单,直接写上:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>歌曲播放器</title><link rel="stylesheet" href="css/index.css" />
</head>
<body><audio src="asserts/music.mp3" controls></audio><div class="container"><ul class="data-list"></ul></div>
</body>
<script type="module" src="js/index.js"></script>
</html>

CSS样式如下:

*{margin: 0;padding: 0;
}
body{background-color: #000;color: #666;text-align: center;
}
audio {width: 450px;margin: 30px 0;
}
.container{height: 500px;overflow: hidden;
}
.container::-webkit-scrollbar{display: none;
}
.container ul{list-style: none;transition: 0.6s;/*transform: translateY(-20px);*/
}
.container ul li{height: 30px;line-height: 30px;font-size: 18px;transition: 0.6s;
}
.container ul li.active{color: #fff;transform: scale(1.2);
}

3. 歌词滚动效果实现

        对于歌词滚动效果,我们具体分析一下,无非就是歌词整体向上移,时间点对应的词高亮一下,对于高亮效果,直接使用.active的CSS属性来实现,具体就是将字体放大,颜色变为亮白色:

.container ul li.active{color: #fff;transform: scale(1.2);
}

        对于移动,我们可以根据audio的进度来移动这个music-list容器,可以使用margin-top或者transform:translateY()来移动,而具体的移动高度可以参考下图

        自此,我们可以实现这个效果了:

// 移动...
let currentIndex = 0;
const move = () => { currentIndex = getMusicIndex(); //当前高亮的歌词下标let containerHeight = domData.container.clientHeight; //Container高度let liHeight = domData.ul.children[0].clientHeight; // 每个li标签的高度let movePx = liHeight * currentIndex + liHeight/2 - containerHeight/2; //需要移动的let maxMove = domData.ul.clientHeight - containerHeight/2;// 范围判断if(movePx < 0){movePx = 0;}if(movePx > maxMove){movePx = maxMove;}// 取消前面的高亮let activeLi = domData.ul.querySelector('.active');if(activeLi){activeLi.classList.remove("active");}// 实现高亮let currentLi = domData.ul.children[currentIndex];if(currentLi){currentLi.classList.add('active');}// 移动domData.ul.style.transform = `translateY(-${movePx}px)`;
}
domData.audio.addEventListener('timeupdate',move);

4. index.js

import {parseLrc} from "./data.js";let domData = {ul: document.querySelector('.container ul'),audio: document.querySelector('audio'),container: document.querySelector('.container'),
} //dom数据
let musicObj = parseLrc(); // 音乐数据const getAudioTime = () => {let result = domData.audio.currentTime;return result;
}const addMusic = () => {let documentFragment = document.createDocumentFragment();for(let i = 0; i < musicObj.length; i++){let li = document.createElement('li');li.textContent = musicObj[i].text;documentFragment.appendChild(li);}domData.ul.appendChild(documentFragment);
}// 根据时间来获取当前需要显示的条数
const getMusicIndex = () => {let time = getAudioTime();for(let i = 0;i < musicObj.length; i++) {let musicTime = musicObj[i].time;if(time < musicTime){return i-1;}}return musicObj.length - 1;
}
// 移动...
let currentIndex = 0;
const move = () => {currentIndex = getMusicIndex(); //当前高亮的歌词下标let containerHeight = domData.container.clientHeight; //Container高度let liHeight = domData.ul.children[0].clientHeight; // 每个li标签的高度let movePx = liHeight * currentIndex + liHeight/2 - containerHeight/2; //需要移动的let maxMove = domData.ul.clientHeight - containerHeight/2;// 范围判断if(movePx < 0){movePx = 0;}if(movePx > maxMove){movePx = maxMove;}// 取消前面的高亮let activeLi = domData.ul.querySelector('.active');if(activeLi){activeLi.classList.remove("active");}// 实现高亮let currentLi = domData.ul.children[currentIndex];if(currentLi){currentLi.classList.add('active');}// 移动domData.ul.style.transform = `translateY(-${movePx}px)`;
}
domData.audio.addEventListener('timeupdate',move);const init = () => {//获取页面歌词
//     插入歌词addMusic();console.log(musicObj)
//     根据时间来移动
}
init(); //入口函数

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

40、基于深度学习的线性预测设计(matlab)

1、原理及流程 深度学习的线性预测是一种利用深度神经网络模型进行线性回归预测的方法。其设计原理主要基于神经网络的层次化特性&#xff0c;利用多层感知器&#xff08;MLP&#xff09;等模型进行特征学习和非线性变换&#xff0c;从而提高线性预测的准确性。 设计流程如下…

Siemens-NXUG二次开发-创建平面(无界非关联)、固定基准面[Python UF][20240614]

Siemens-NXUG二次开发-创建平面&#xff08;无界非关联&#xff09;、固定基准面[Python UF][20240614] 1.python uf函数1.1 NXOpen.UF.Modeling.CreatePlane1.2 NXOpen.UF.ModlFeatures.CreateFixedDplane 2.示例代码2.1 pyuf_plane.py 3.运行结果3.1 内部模式3.1.1 NXOpen.UF…

应变玻璃合金是航天产业重要弹性材料 研究开发意义重大

应变玻璃合金是航天产业重要弹性材料 研究开发意义重大 应变玻璃&#xff0c;是一种形状记忆合金&#xff0c;为纳米级材料&#xff0c;其短程有序晶格应变区域呈冻结状态&#xff0c;具有典型的玻璃化转变特征&#xff0c;可以对外界刺激产生应变反应&#xff0c;也称为应变玻…

数据库 | 数据库设计的步骤

1.需求分析 调查机构情况与熟悉业务活动&#xff0c;明确用户的需求&#xff0c;确定系统的边界&#xff0c;生成数据字典和用户需求规格说明书 2.概念结构设计 将从需求分析中得到的用户需求抽象为概念模型&#xff0c;设计E-R模型 3.逻辑结构设计 将E-R图转换为和DBMS相…

【网络编程开发】17.“自动云同步“项目实践

17."自动云同步"项目实践 文章目录 17."自动云同步"项目实践项目简介功能需求需求分析实现步骤 1.实现TCP通信server.c 服务端tcp.hclient.c 客户端 函数封装tcp.ctcp.hserver.cclient.c编译运行 2.实现文件传输sever.cclient.ctcp.ctcp.hMakeifle编译运行…

MES管理系统中的质量管理活动是什么

在制造业的广阔天地中&#xff0c;质量管理如同航船的指南针&#xff0c;指引着产品品质的航行方向。而随着科技的日新月异&#xff0c;MES管理系统在质量管理领域扮演着越来越重要的角色。MES管理系统不仅连接了企业的管理层与车间生产现场&#xff0c;更在质量管理的各个环节…

RS485常见问题及解决方法

RS485常见问题及解决方法 RS485总线是工业上最常用的通信方式之一&#xff0c;在实际布线或使用过程中通常会出现一系列问题&#xff0c;今天总结一些平时RS485通讯可能会出现的通讯问题及其解决方法以供大家参考。 一、什么是RS485&#xff1f; RS485&#xff0c;全称为TIA…

QT(超详细从0开始)

目录 1.2 Qt的优点 2.安装Qt 3.创建项目 4.解读Qt自动生成的代码 ​编辑 5.Qt Designer 6.Qt对象数 7.Qt乱码问题 8.Qt坐标系的认识 9.信号和槽 9.1 connect 9.2 自定义槽函数 9.3 自定义信号 9.4 断开信号链接&#xff08;disconnect&#xff09; 9.5.lambda表…

家庭智能助手:Kompas AI引领家居智能化新纪元

一、引言 在数字化浪潮的推动下&#xff0c;现代家庭生活正迅速向智能化转型。从简单的自动化设备到复杂的智能家居系统&#xff0c;智能技术正悄无声息地改变我们的日常生活。Kompas AI作为一款前沿的家庭智能助手&#xff0c;不仅预示着家庭生活的未来趋势&#xff0c;更以其…

使用 Verdaccio 建立私有npm库

网上有很多方法,但很多没标注nginx的版本所以踩了一些坑,下方这个文档是完善后的,对linux不是很熟练,所以不懂linux不会搭建的跟着做就可以了 搭建方法 首先需要一台云服务器 以139.196.226.123为例登录云服务器 下载node cd /usr/local/lib下载node 解压 下载 wget https://…

Python内置函数pow()详解

在Python编程中&#xff0c;pow()函数是一个非常有用的内置函数&#xff0c;它用于计算一个数的指数幂。这个函数在数学计算、科学研究、加密算法等领域中都非常实用。 函数功能 pow()函数的主要功能是计算给定数值的指数幂。当提供第三个参数时&#xff0c;它还可以返回指数…

帕金森运动小贴士,壁纸里的健康密码

&#x1f31f; 在这个快节奏的时代&#xff0c;我们越来越关注身体的健康。今天&#xff0c;我想和大家分享一份特别的小贴士&#xff0c;它藏在一张精致的小红书壁纸里&#xff0c;是关于帕金森病的运动建议。帕金森病是一种常见的神经系统疾病&#xff0c;适当的运动对于缓解…

窄依赖和宽依赖的相关函数

在Apache Spark和Apache Flink这类分布式计算系统中&#xff0c;窄依赖和宽依赖描述的是数据在不同计算阶段&#xff08;或称作“阶段”、“stage”&#xff09;之间的流动方式。以下是一些典型的窄依赖和宽依赖函数示例&#xff1a; ### 窄依赖相关函数&#xff08;Narrow De…

小米充电宝怎么样?西圣、小米、罗马仕充电宝测评谁是卷王!

充电宝说实话在我们日常生活中还是非常常见的一个出门必备的充电设备&#xff0c;除了出门必须带数据线之外&#xff0c;充电宝也是不例外的&#xff0c;对于手机不耐电的朋友来说在外面有一个充电宝简直就是蓄电“救星”&#xff0c;什么都可以不带但是充电宝是必带的一款装备…

Ubuntu 在线或离线安装docker

查看自己的ubuntu版本 在终端中执行以下命令&#xff1a; lsb_release -a 终端中的复制粘贴&#xff1a; ctrl shift c ctrl shifr v 在线安装docker&#xff08;不需要外网&#xff09;: 命令行安装&#xff1a;Ubuntu Docker -- 从入门到实践 看完…

python不同版本介绍及更新说明

Python是一种广泛使用的高级编程语言&#xff0c;自1991年由Guido van Rossum首次发布以来&#xff0c;经历了多个版本的迭代和更新。每个新版本通常包含改进、错误修复和新特性。以下是Python主要版本及其更新说明的概述&#xff1a; Python 1.x 系列 Python 1.0&#xff1a…

6月17日(周一),AH 股行情总结

AI手机及苹果概念股全日走强&#xff0c;领益智造、山东精密等多股涨停&#xff0c;立讯精、歌尔股份涨逾6% 。新车型秦L销售预期提振股价&#xff0c;比亚迪涨超1% &#xff1b;航运、煤炭、地产板块下跌。 文章正文 周一&#xff0c;A股低开高走&#xff0c;上证指数收跌0.…

2024年了! 为什么还在用串口服务器?

在数字化飞速发展的2024年&#xff0c;串口服务器这一看似古老的技术仍然在工业自动化、远程监控和数据通信等领域发挥着重要作用。本文将从串口服务器的定义、功能、优势和使用场景四个方面来探讨&#xff0c;为什么串口服务器在今天仍然被广泛使用。 1. 什么是串口服务器 串口…

拉依达的嵌入式学习和秋招经验

拉依达的嵌入式学习和秋招经验 你好&#xff0c;我是拉依达。目前我已经结束了自己的学生生涯&#xff0c;开启了人生的下一个阶段。 从研二准备秋招开始&#xff0c;我就逐渐将自己的学习笔记陆续整理并到CSDN上发布。起初只是作为自己学习的备份记录&#xff0c;后续得到了越…

gpustat

使用gpustat命令查看GPU的资源使用情况&#xff0c;例如&#xff1a; 但是需要先安装&#xff1a; pip install gpustat