利用原生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) => { //将时间解析成秒s
    let 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(); //入口函数

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

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

相关文章

Qt中利用QTextBrowser控件设计日志窗口

我们一般使用Qt开发应用程序时&#xff0c;都有将控制台窗口去掉。但是&#xff0c;有时候又需要查看一些调试信息&#xff0c;一般的处理方式是把log写到一个文件中。本文介绍以下日志窗口&#xff0c;可以更方便的查看日志信息。 UI设计 推拽UI控件&#xff0c;修改默认背景…

05眼动识别软件详情2波形优化

对应视频链接点击直达 01项目点击下载&#xff0c;可直接运行&#xff08;含数据库&#xff09; 05眼动识别软件详情2 对应视频链接点击直达期望的数据展示数据波形对比如何实现几种常用滤波介绍维纳滤波巴特沃斯滤波器中值滤波排序滤波 推荐 结语其他以下是废话 原始数据的波…

Objective-C 学习笔记 | KVC(key-value coding)

Objective-C 学习笔记 | KVC&#xff08;key-value coding&#xff09; Objective-C 学习笔记 | KVC&#xff08;key-value coding&#xff09;非对象类型Key 路径 Objective-C 学习笔记 | KVC&#xff08;key-value coding&#xff09; KVC 可以让程序通过名称直接存取属性&a…

CentOS 5(CentOS 6、Redhat 6)服务器配置VNC

一、配置服务器yum源 yum源&#xff08;本地、华为云、阿里云、网易&#xff09; 二、使用yum安装vnc服务 1、检查系统是否安装了vnc 和 vncserver&#xff0c; rpm -qa | grep vnc如果没有安装那就行自行下载安装&#xff08;我这里用yum安装了&#xff0c;vncserver安装需…

让你的网页动起来 - 轻松实现 JavaScript 拖拽功能

效果展示 实现 要实现该效果需要运用 HTML5 的 dragstart 拖放操作事件 通过去开启dragstart监听拖放操作事件就能实现图片的拖动 <div class"empty"><div class"fill" draggable"true"></div> </div>本例子中我们对…

基于深度学习的鸟类检测识别系统【python源码+Pyqt5界面+数据集+训练代码 MX_003期】

简介&#xff1a; 基于深度学习的鸟类检测识别系统在当今世界中具有广泛的应用前景。系统不仅可以帮助生态学家和保护人员监测和保护鸟类种群&#xff0c;还能在农业管理、城市生态监测以及科学研究领域发挥重要作用。通过自动化的图像识别技术&#xff0c;可以实现对鸟类种类、…

汽车传动系统为汽车动力总成重要组成部分 我国市场参与者数量不断增长

汽车传动系统为汽车动力总成重要组成部分 我国市场参与者数量不断增长 汽车系统主要包括动力系统、制动系统、传动系统、转向系统、行驶系统、燃油供给系统、照明系统以及电器系统。汽车传动系统指能够将发动机产生的动力转化为车辆行驶驱动力的动力传递装置。汽车传动系统为汽…

学生用小台灯什么牌子的好?五大强劲护眼台灯牌子分享

在这个数码时代&#xff0c;人们对屏幕的依赖程度越来越高&#xff0c;尤其是孩子们。他们不仅在学校里需要长时间盯着教科书&#xff0c;还会在学习和娱乐中使用各种数码设备。然而&#xff0c;这也使得眼睛健康问题逐渐凸显&#xff0c;尤其是儿童近视的问题。为了保护视力&a…

Inpaint软件下载附加详细安装教程

​Inpaint是一款由Maxim Gapchenko开发的图像处理软件&#xff0c;它可以帮助用户轻松地去除图像中的水印和其他不需要的元素&#xff0c;这个软件的核心技术是基于图像处理算法的&#xff0c;它可以自动识别图片中的像素&#xff0c;并用周围的颜色进行替换&#xff0c;使得图…

吴恩达深度学习笔记:机器学习(ML)策略(1)(ML strategy(1))1.11-1.12

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第一周 机器学习&#xff08;ML&#xff09;策略&#xff08;1&#xff09;&#xff08;ML strategy&#xff08;1&#xff09;&#xff09;1.11 超过人的表现&#xff08;Surp…

Swift Combine — Subject Publishers(PassthroughSubject CurrentValueSubject)

本文主要介绍一下Subject&#xff0c;Subject 本身也是一个 Publisher&#xff0c;其定义如下&#xff1a; public protocol Subject<Output, Failure> : AnyObject, Publisher {func send(_ value: Self.Output)func send(completion: Subscribers.Completion<Self.…

豆浆机水位传感器工作原理

豆浆机水位传感器的工作原理基于光电效应&#xff0c;利用近红外发光二极管和光敏接收器的组合实现液位的精确检测与控制。在豆浆机内部&#xff0c;传感器安装在水箱底部或需要检测液位的位置&#xff0c;起到监测和控制豆浆机水位的重要作用。 传感器包括一个近红外发光二极…

Suno新技能亮相:完美复刻歌手音色,我甚至不敢公开!

之前写过一篇文章 颠覆音乐创作! Suno史诗级更新&#xff0c;随便哼哼就能出一首好听的歌曲&#xff1f; Suno支持上传一段音频或者自己的哼唱进行续创歌曲&#xff0c;这个功能大家有玩出花样嘛&#xff1f; 可能很多人&#xff0c;还不知道这个到底有啥用! 大家先看看这首《满…

如何用Vue3和p5.js绘制交互式3D饼图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于p5.js实现色彩轮和饼状图的动态可视化 应用场景 本代码利用p5.js库&#xff0c;创建了一个交互式的色彩轮和饼状图可视化界面。它适用于需要展示颜色信息或数据分布情况的场景&#xff0c;如设计、数据分…

docker通过容器id查看运行命令;Portainer监控管理docker容器

1、docker通过容器id查看运行命令 参考&#xff1a;https://blog.csdn.net/a772304419/article/details/138732138 docker inspect 运行镜像id“Cmd”: [ “–model”, “/qwen-7b”, “–port”, “10860”, “–max-model-len”, “4096”, “–trust-remote-code”, “–t…

【招联消费金融股份】有限公司2024年5月18日【算法开发岗暑期实习】一面试经验分享

招联消费金融股份有限公司2024年5月18日面试经验分享 面试流程&#xff1a;共30多分钟&#xff0c;先3分钟自我介绍&#xff0c;然后细细介绍简历上面的论文和实习信息。问题1&#xff1a;扩散模型的noise schedule有什么研究。问题2&#xff1a;有哪些常见的数学分布问题3&…

jquery动态效果插件之ScrollMagic

ScrollMagic 是一个强大的 JavaScript 库,可以帮助开发者在页面滚动时触发各种动画效果。它支持复杂的滚动交互,非常适合制作富交互的网页。 这里他使用了ScrollMagic的几种滚动效果: 视差滚动效果:页面上的一些元素在滚动时会产生视差滚动效果,即元素以不同的速度移动,营造出…

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

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

洗地机性价比高的是哪一款?行内人告诉你

在浏览前&#xff0c;希望您轻触屏幕上方的“关注”按钮&#xff0c;让我后续为您带来更多实用且精彩的内容&#xff0c;感谢您的支持&#xff01; 洗地机作为现在的流行清洁工具&#xff0c;它的魅力之处在于&#xff1a;性价比极高&#xff0c;大多数家庭无需花费过多就能把…

语义分割和目标检测的关系

目录 1.语义分割的目标 2.目标检测的目标 3.两种任务的异同之处 从大方向的任务特点上来说 &#xff08;1&#xff09;物体的位置 &#xff08;2&#xff09;物体的分类 从数据格式来说 (1&#xff09;语义分割的数据格式 (2&#xff09;目标检测的数据格式 1.语义分…