Web前端—(原生JS)歌词滚动效果

news2024/12/1 8:49:18

歌词滚动效果实现

  • 歌词滚动效果
    • HTML部分
    • CSS部分
    • JS部分
      • 解析歌词字符串,得到歌词的对象数组
      • 计算在当前情况下,播放器播放到第几秒的情况
      • 创建歌词元素
      • 设置ul元素的偏移量
      • 最后对时间变化的事件进行监听
      • 完整JS代码

歌词滚动效果

  • 实现效果如图所示:

在这里插入图片描述

首先准备一个歌曲MP3文件,以及歌词,将歌词放入一个data.js文件中。
在这里插入图片描述

  • 歌词内容如下所示:
    在这里插入图片描述

HTML部分

  • 歌词采用ul-li的结构进行展示
  • 导入相应的css和js文件
  • 代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/index.css">
</head>
<body>
    <audio src="./assets/薛之谦 - 狐狸.mp3" controls></audio>
    <div class="container">
        <ul>           
        </ul>
    </div>

    <script src="./js/data.js"></script>
    <script src="./js/index.js"></script>
</body>
</html>

CSS部分

  • 由于CSS属性变化本身没有动画效果,因此这里采用transition属性实现其动画效果
  • transition属性只针对于数值嘞的属性有效
  • 代码如下:(大家可以根据自己的喜好设置样式)
*{
    margin: 0;
    padding: 0;
}
body{
    background: #000;
    color: #666;
    text-align: center;
}
audio{
    width: 450px;
    margin: 30px 0;
}
.container{
    height: 420px;
    overflow: hidden;
}
.container ul{
    transition: 0.3s;
    
}
.container li{
    height: 30px;
    line-height: 30px;
    transition: 0.3s;
    list-style: none;
    
}
.container li.active{
    color: #fff;
    transform: scale(1.2);
}

JS部分

解析歌词字符串,得到歌词的对象数组

// 解析时间字符串
function parseTime(timeStr){
    let parts = timeStr.split(':');
    return +parts[0]*60 + +parts[1];
}
/**
 * 解析歌词字符串,得到歌词的对象数组
 * 每个歌词对象 
 * {time: 开始时间, words:歌词内容}
 */
function parseLrc(){
    var result = [];
    var lines = lrc.split('\n'); // 字符串数组
    for(let i = 0; i < lines.length; i++){
        // 遍历lines
        let str = lines[i]; //每一句歌词字符串
        let parts = str.split(']'); //分割
        let timeStr = parts[0].substring(1); //截取时间
        var obj ={
            time:parseTime(timeStr),
            words: parts[1]
        }
        result.push(obj);
    }
    return result;
}

var lrcData = parseLrc();

计算在当前情况下,播放器播放到第几秒的情况

/**
 * 计算在当前情况下,播放器播放到第几秒的情况
 * lrcData数组中应该高亮显示的歌词下标
 */
function findIndex(){
    // 获取播放器的当前时间
    let curTime = doms.audio.currentTime;
    for(let i = 0; i<lrcData.length; i++){
        if(curTime<lrcData[i].time){
            return i-1;
        }
    }
    // 如果没有任何一句歌词显示,则返回-1
    // 如果找遍了都没有找到,说明播放到最后一句
    return lrcData.length-1;
}

创建歌词元素

// 创建歌词元素
function createLrcElement(){
    for(let i=0; i<lrcData.length; i++){
        let li = document.createElement('li');
        li.textContent = lrcData[i].words;
        doms.ul.appendChild(li);
    }
}

设置ul元素的偏移量

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


// 设置ul元素的偏移量
function setOffset(){
    let 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样式
    let li1 = doms.ul.querySelector('.active');
    if(li1){
        li1.classList.remove('active');
    }

    let li2 = doms.ul.children[index];
    if(li2){
        li2.classList.add('active');
    }

}

最后对时间变化的事件进行监听

doms.audio.addEventListener('timeupdate', setOffset);

如果大家没有MP3文件或者歌词的话,我会上传我的资源,可以自行下载。

完整JS代码

var doms = {
    audio:document.querySelector('audio'),
    ul:document.querySelector('.container ul'),
    container:document.querySelector('.container')
}

// 解析时间字符串
function parseTime(timeStr){
    let parts = timeStr.split(':');
    return +parts[0]*60 + +parts[1];
}

/**
 * 解析歌词字符串,得到歌词的对象数组
 * 每个歌词对象 
 * {time: 开始时间, words:歌词内容}
 */
function parseLrc(){
    var result = [];
    var lines = lrc.split('\n'); // 字符串数组
    for(let i = 0; i < lines.length; i++){
        // 遍历lines
        let str = lines[i]; //每一句歌词字符串
        let parts = str.split(']'); //分割
        let timeStr = parts[0].substring(1); //截取时间
        var obj ={
            time:parseTime(timeStr),
            words: parts[1]
        }
        result.push(obj);
    }
    return result;
}

var lrcData = parseLrc();


/**
 * 计算在当前情况下,播放器播放到第几秒的情况
 * lrcData数组中应该高亮显示的歌词下标
 */
function findIndex(){
    // 获取播放器的当前时间
    let curTime = doms.audio.currentTime;
    for(let i = 0; i<lrcData.length; i++){
        if(curTime<lrcData[i].time){
            return i-1;
        }
    }
    // 如果没有任何一句歌词显示,则返回-1
    // 如果找遍了都没有找到,说明播放到最后一句
    return lrcData.length-1;
}

// 创建歌词元素
function createLrcElement(){
    for(let i=0; i<lrcData.length; i++){
        let li = document.createElement('li');
        li.textContent = lrcData[i].words;
        doms.ul.appendChild(li);
    }
}
createLrcElement()

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


// 设置ul元素的偏移量
function setOffset(){
    let 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样式
    let li1 = doms.ul.querySelector('.active');
    if(li1){
        li1.classList.remove('active');
    }

    let li2 = doms.ul.children[index];
    if(li2){
        li2.classList.add('active');
    }

}

// 事件监听
doms.audio.addEventListener('timeupdate', setOffset);```

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

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

相关文章

PyCharm中配置PyQt5并添加外部工具

Qt Designer、PyUIC和PyRcc是Qt框架下的三个重要工具&#xff0c;总的来说&#xff0c;这三个工具各司其职&#xff0c;相辅相成&#xff0c;能显著提升Qt开发的速度与效率。 Qt Designer&#xff1a;是一个用于创建图形用户界面的工具&#xff0c;可轻松构建复杂的用户界面。…

Photoshoot 2(Java)

Photoshoot 2 题目描述 在一个似曾相识的场景中&#xff0c;Farmer John 正在将他的 N 头奶牛&#xff08;1≤N≤10^5&#xff09;排成一排&#xff08;为了方便将它们按 1⋯1⋯N 编号&#xff09;&#xff0c;以便拍照。 最初&#xff0c;奶牛从左到右按照 a1,a2,⋯,aN 的顺…

Facebook轮播广告是什么?投放过程中有哪些需要注意的吗?

轮播广告是Facebook广告形式中的一种&#xff0c;可以把3—5个广告合并到一个可滚动的广告单元中。轮播广告会出现在新鲜事即News Feed中&#xff0c;是独立站卖家常用的一种广告形式 为什么选择轮播广告&#xff1f; 转化率更高&#xff1a;相较于单图广告&#xff0c;轮播广…

2024 ccfcsp认证打卡 2022 06 01 归一化处理

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 输入数字的个数int[] a new int[1010]; // 创建一个数组来存储输入的数字double sum 0; // 用于计算所有输入数字的总…

Linux基础命令篇:操作系统服务管理(systemctl service)

Linux基础命令篇&#xff1a;操作系统服务管理&#xff08;systemctl & service&#xff09; service和systemctl是两个用于管理Linux系统服务的命令。它们分别属于SysV init和systemd系统&#xff0c;这两个系统是Linux中用于初始化和管理服务的不同框架。在许多现代Linux…

四、Yocto创建静态IP和VLAN(基于raspiberrypi 4B)

Yocto创建VLAN配置 在车载域控中很多时候需要创建VLAN&#xff0c;本小节记录如何为yocto构建出来的image自动化创建静态IP以及VLAN。 关于各种VLAN的配置参考&#xff1a;VLAN 1. ubuntu系统中使用netplan创建VLAN 正常情况下我们在ubuntu系统中可以通过netplan来自动化创建…

《数据结构学习笔记---第七篇》---栈和队列的OJ练习

1. 括号匹配问题。OJ链接 step1:思路分析 &#xff1a; 1.括号匹配&#xff0c;我们首先考虑用栈实现&#xff0c;我们通过符号栈帧的思想知道&#xff0c;求前中后缀表达式的时候用的就是栈帧&#xff0c;操作数栈和符号栈。 2.根据常见的情况 考虑怎么使用栈&#xff0c;首先…

【Qt】:信号与槽(二)

信号与槽 一.带参数的信号和槽二.信号与槽的多对多连接三.信号与槽的断开四.lamda表达式定义槽函数 一.带参数的信号和槽 Qt的信号和槽也⽀持带有参数,同时也可以⽀持重载.此处我们要求,信号函数的参数列表要和对应连接的槽函数参数列表⼀致.&#xff08;一致指的是类型一致&a…

微信小程序【从入门到精通】——服务器的数据交互

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

LoRA 及其衍生技术总览:An Overview of the LoRA Family

编者按&#xff1a; 对于大语言模型的微调训练来说&#xff0c;传统的全参数微调方法需要处理数百万甚至数十亿级别的参数&#xff0c;计算量和显存占用都非常大。而 LoRA 这种技术方案&#xff0c;通过引入少量的可训练矩阵来调整预训练模型的行为&#xff0c;极大降低了训练所…

EasyRecovery2024中文版数据恢复软件功能全面介绍

EasyRecovery2024是世界著名数据恢复公司 Ontrack 的技术杰作&#xff0c;它是一个威力非常强大的硬盘数据恢复工具。能够帮你恢复丢失的数据以及重建文件系统。 EasyRecovery不会向你的原始驱动器写入任何东东&#xff0c;它主要是在内存中重建文件分区表使数据能够安全地传输…

基于DCT(离散余弦变换)的图像水印算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

【LeetCode: 面试题 16.05. 阶乘尾数 + 阶乘】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Android 12.0 mtp模式下连接pc后显示的文件夹禁止删除copy重命名功能实现

1.前言 在12.0的系统rom定制化开发中,usb连接pc端的时候有好几种模式,在做otg连接pc端的时候,改成mtp模式的时候,在pc端可以看到产品设备 的显示的文件夹的内容,对于产品设备里面的文件在pc端禁止做删除重命名拷贝等操作功能的实现 2.mtp模式下连接pc后显示的文件夹禁止删…

深入探讨多线程编程:从0-1为您解释多线程(下)

文章目录 6. 死锁6.1 死锁原因 6.2 避免死锁的方法加锁顺序一致性。超时机制。死锁检测和解除机制。 6. 死锁 6.1 死锁 原因 系统资源的竞争&#xff1a;&#xff08;产生环路&#xff09;当系统中供多个进程共享的资源数量不足以满足进程的需要时&#xff0c;会引起进程对2…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于纳什谈判的电氢能源系统多时间尺度协同运行优化》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

harbor api v2.0

harbor api v2.0 v2.0 v2.0 “harbor api v2.0”与原来区别较大&#xff0c;此处harbor也做了https。另外&#xff0c;通过接口拿到的数据也是只能默认1页10个&#xff0c;所以脚本根据实际情况一页页的抓取数据 脚本主要用于统计repo、image&#xff0c;以及所有镜像的tag数&…

ubuntu18.04 pycharm

一、下载pycharm &#xff08;1&#xff09;进入官网下载Download PyCharm: The Python IDE for data science and web development by JetBrains 选择专业版&#xff08;professional&#xff09;直接点击下载&#xff08;download&#xff09;&#xff0c;我下载的是2023.3…

PCA+DBO+DBSCN聚类,蜣螂优化算法DBO优化DBSCN聚类,适合学习,也适合发paper!

PCADBODBSCN聚类&#xff0c;蜣螂优化算法DBO优化DBSCN聚类&#xff0c;适合学习&#xff0c;也适合发paper&#xff01; 一、蜣螂优化算法 摘要&#xff1a;受蜣螂滚球、跳舞、觅食、偷窃和繁殖等行为的启发&#xff0c;提出了一种新的基于种群的优化算法(Dung Beetle Optim…

Rust使用原始字符串字面量实现Regex双引号嵌套双引号正则匹配

rust使用Regex实现正则匹配的时候&#xff0c;如果想实现匹配双引号&#xff0c;就需要使用原始字符串字面量&#xff0c;不然无法使用双引号嵌套的。r#"..."# 就表示原始字符串字面量。 比如使用双引号匹配&#xff1a; use regex::Regex;fn main() {println!(&qu…