JS之歌词滚动案例

news2024/12/24 8:03:59

让我为大家带来一个歌词滚动的案例吧!
详细的介绍都在代码块中
我很希望大家可以自己动手尝试一下,如果需要晴天的mp3音频文件可以私信我
上代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歌词滚动</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        ul {
            list-style: none;
            transition: all 0.5s;
        }
        body {
            background-color: #000;
        }
        audio {
            display: block;
            margin: 50px auto;
            width: 400px;
        }
        .container {
            text-align: center;
            color: #fff;
            height: 400px;
            overflow: hidden;
        }
        ul li {
            height: 40px;
            line-height: 40px;
            transition: all 0.5s;
        }
        /* 对应的歌词设置active */
        .active {
            color: red;
            /* 建议使用transform font-size会造成reflow重新布局 效率变低 */
            /* transform不在渲染主线程上执行 而是在合成线程上执行 就不会造成reflow */
            transform: scale(1.5);
        }
    </style>
</head>
<body>
    <!-- 晴天音频 -->
    <audio src="周杰伦-晴天.mp3" controls></audio>
    <!-- 大容器 -->
    <div class="container">
        <!-- 容器需要装歌词 -->
        <ul>
        </ul>
    </div>
</body>
<script>
    // 歌词
    let lrc = `[00:00.71]晴天 - 周杰伦
[00:28.84]故事的小黄花
[00:32.34]从出生那年就飘着
[00:35.85]童年的荡秋千
[00:39.41]随记忆一直晃到现在
[00:42.77]rui sou sou xi dou xi la
[00:45.50]sou la xi xi xi xi la xi la sou
[00:49.41]吹着前奏望着天空
[00:52.60]我想起花瓣试着掉落
[00:56.10]为你翘课的那一天
[00:58.29]花落的那一天
[01:00.14]教室的那一间
[01:01.79]我怎么看不见
[01:03.61]消失的下雨天
[01:05.38]我好想再淋一遍
[01:09.51]没想到失去的勇气我还留着
[01:15.72]好想再问一遍
[01:17.47]你会等待还是离开
[01:24.46]刮风这天我试过握着你手
[01:30.18]但偏偏雨渐渐大到我看你不见
[01:38.50]还要多久我才能在你身边
[01:44.93]等到放晴的那天也许我会比较好一点
[01:52.41]从前从前有个人爱你很久
[01:58.07]但偏偏风渐渐把距离吹得好远
[02:06.44]好不容易又能再多爱一天
[02:12.81]但故事的最后你好像还是说了拜拜
[02:33.93]为你翘课的那一天
[02:36.39]花落的那一天
[02:38.13]教室的那一间
[02:39.84]我怎么看不见
[02:41.55]消失的下雨天
[02:43.31]我好想再淋一遍
[02:47.39]没想到失去的勇气我还留着
[02:53.39]好想再问一遍
[02:55.49]你会等待还是离开
[03:02.46]刮风这天我试过握着你手
[03:08.12]但偏偏雨渐渐大到我看你不见
[03:16.54]还要多久我才能在你身边
[03:23.10]等到放晴的那天也许我会比较好一点
[03:30.49]从前从前有个人爱你很久
[03:36.26]但偏偏风渐渐把距离吹得好远
[03:44.48]好不容易又能再多爱一天
[03:51.05]但故事的最后你好像还是说了拜拜
[03:57.65]刮风这天我试过握着你手
[04:01.45]但偏偏雨渐渐大到我看你不见
[04:04.98]还要多久我才能够在你身边
[04:08.64]等到放晴那天也许我会比较好一点
[04:12.34]从前从前有个人爱你很久
[04:15.40]但偏偏雨渐渐把距离吹得好远
[04:19.05]好不容易又能再多爱一天
[04:22.42]但故事的最后你好像还是说了吧`
    // 获取audio
    const audio = document.querySelector("audio")
    // 获取容器
    const container = document.querySelector(".container")
    // 获取ul
    const ul = document.querySelector("ul")
    // 第一步渲染页面,我们需要用到split \n换行切割 记住是\n 不是/n
    const lrcArr = lrc.split("\n")
    // console.log(lrcArr);
    // 准备好一个数组接收
    const resultArr = []
    // 因为我们是[00:00:00]这样的形式我们需要继续切割
    for (let i = 0; i < lrcArr.length; i++) {
        const lrcData = lrcArr[i].split("]")
        // 我们取到lrcData[0]与lrcData[1]
        // 获取到 00:00:00这样的字符串
        const times = lrcData[0].slice(1)
        // 声明一个对象 我们把时间与歌词依次存入
        const objLrc = {
            // 时间 不过我们需要处理一下时间 把00:00:00这样的格式转换为秒
            seconds: parseTime(times),
            // 歌词
            lrc: lrcData[1]
        }
        // 添加到resultArr中
        resultArr.push(objLrc)
    }
    // 处理时间转换为秒
    function parseTime(times) {
        // 切割times 用:切割
        let timesArr = times.split(":")
        // console.log(timesArr);
        // 把下标为0的分钟转换为秒 但要注意 当前时间为字符串型 我们需要转换为数字型
        // 介绍一下+ 这个是数值中的隐式转换
        return +timesArr[0] * 60 + +timesArr[1]
    }
    // 渲染页面
    function createLis() {
        for (let i = 0; i < resultArr.length; i++) {
            let li = document.createElement("li")
            li.innerHTML = resultArr[i].lrc
            ul.appendChild(li)
        }
    }
    createLis()
    // 获取到下标
    function getIndex() {
        // 获取音频的当前时间
        let current = audio.currentTime
        for (let i = 0; i < resultArr.length; i++) {
            // 判断 如果当前时间小于seconds 下标为 i - 1
            if (current < resultArr[i].seconds) {
                return i - 1
            }
        }
        // 找遍了没有找到 说明播放到最后一句了
        return resultArr.length - 1
    }
    // 获取到container的高度
    const containerHeight = container.clientHeight
    // 获取到第一个li的高度
    const liHeight = ul.children[0].clientHeight
    // 设置最大最小偏移 解决效果不佳
    const minOffset = 0
    const maxOffset = ul.clientHeight - containerHeight
    // 滚动效果
    function setOffset() {
        // 下标
        let index = getIndex()
        // 滚动到哪了 li的高度乘以下标就是对应的歌词 
        // 然后减去容器的一半 这时对应的li就在接近中间的位置
        // 我们再加上li一半的高度这时对应li就在中间
        let offset = liHeight * index - containerHeight / 2 + liHeight / 2
        // 判断
        // 为什么offset会小于0?
        // 假设现在下标为0 选取到了第0个
        // 那么就是 liHeight*0 - 200+liHeight/2
        if (offset < minOffset) {
            // offset赋为minOffset
            offset = minOffset
        }
        // 为什么会大于maxOffset?
        // 当我们歌到了最后的时候 就会造成这种情况
        if (offset > maxOffset) {
            // offset赋为maxOffset
            offset = maxOffset
        }
        // 现在可以滚动了 滚动ul
        // 建议大家使用transform
        // 好处在哪? 如果使用margin 会造成reflow重新布局 效率会出现问题
        // transform不在渲染主线程上执行 而是在合成线程上执行 就不会造成reflow
        ul.style.transform = `translateY(-${offset}px)`
        // 设置active
        // 首先我们获取到active
        let li = ul.querySelector(".active")
        // 存在就移除
        if (li) {
            li.classList.remove("active");
        }
        // 获取到下标对应的歌词
        li = ul.children[index];
        if (li) {
            li.classList.add("active");
        }
    }
    // 我们需要用到audio中的timeupdate事件
    audio.addEventListener("timeupdate", setOffset)
</script>
</html>

请添加图片描述

感谢大家的阅读,如有不对的地方,可以向我提出,感谢大家!

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

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

相关文章

【开源】基于JAVA语言的图书管理系统

目录 一、 系统介绍二、 功能模块2.1 登录注册模块2.1 图书馆模块2.2 图书类型模块2.3 图书模块2.4 图书借阅模块2.5 公告模块 三、 源码解析3.1 图书馆模块设计3.2 图书类型模块设计3.3 图书模块设计3.4 图书借阅模块设计3.5 公告模块设计 四、 免责说明 一、 系统介绍 图书管…

和硕拿下AI Pin代工大单公司 | 百能云芯

和硕公司近日成功中标AI Pin代工大单&#xff0c;AI Pin被认为是继iPhone之后的下一个划时代产品&#xff0c;吸引了全球科技圈的广泛关注。和硕公司对此表示&#xff0c;他们不会只专注于单一客户&#xff0c;而是期望在下半年有更多新品上市&#xff0c;为公司带来丰硕的业绩…

《Linux高性能服务器编程》笔记06

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第13章 多进程编程13.1 fork 系统调用13…

空调设计软件工程师考虑点

空调设计软件工程师考虑点 看如的下边有输入压力P&#xff0c;单位不同&#xff0c;MPG是相对压力&#xff0c;Kpa是绝对压力。绝对压力比相对压力大一个大气压&#xff0c;即100kpa。 海立压缩机直接给转速值就行。CAN数据格式&#xff0c;Motoral高位在前&#xff0c;Intel高…

函数栈帧的创建与销毁【此一篇,足以让卿彻底扫盲】

一、函数栈帧要解决的问题 刚接触编程语言的的我们&#xff0c;想必都会存在这些问题&#xff1a; Ⅰ 局部变量不初始化&#xff0c;为什么是随机值&#xff1b; Ⅱ 形参与实参的关系&#xff1b; Ⅲ 函数是如何被调用&#xff0c;以及开辟相对应的空间的&#xff1b; Ⅳ…

MySQL数据库的锁机制

目录 一、引言 二、锁的类型及作用 2.1 行级锁 2.2 间隙锁与临键锁 2.3 共享锁与排他锁 2.4 意向锁 2.5 表级锁 2.6 元数据锁 三、锁的管理与优化 3.1 合理设置事务隔离级别 3.2 避免长事务 3.3 索引优化 3.4 明确锁定范围 3.5 避免不必要的全表扫描 四、实战分…

SpringBoot+beetl idea热更新解决方案

SpringBootbeetl idea热更新解决方案 第一在application中开启&#xff1a; beetl:resource-auto-check: true #热加载beetl模板&#xff0c;开发时候用第二在application中开启&#xff1a; devtools: 这个部分专门用于配置Spring Boot DevTools的相关参数。DevTools…

基于springboot+vue的“衣依”服装销售平台系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

【latex】在Overleaf的IEEE会议模板中,快速插入参考文献

【LaTeX】在Overleaf的IEEE会议模板中&#xff0c;快速插入参考文献 写在最前面第一步&#xff1a;在文献检索网站导出引用文献的bib文件第二步&#xff1a;编辑overleaf模版方法二&#xff1a;EduBirdie生成参考文献&#xff08;补充&#xff09;使用LaTeX在Overleaf的IEEE会议…

Spring 声明式事务 @Transactional(详解)【面试重点,小林出品】

关于 Transactional 注解的基本使用&#xff0c;推荐看Spring 声明式事务 Transactional&#xff08;基本使用&#xff09; 概述 本篇博客主要学习 Transactional 注解当中的三个常⻅属性: 1. rollbackFor:异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型 …

Goby 漏洞发布|GoAnywhere MFT InitialAccountSetup.xhtml 绕过漏洞(CVE-2024-0204)

漏洞名称&#xff1a;GoAnywhere MFT InitialAccountSetup.xhtml 绕过漏洞&#xff08;CVE-2024-0204&#xff09; English Name&#xff1a;GoAnywhere MFT InitialAccountSetup.xhtml Bypass Vulnerability (CVE-2024-0204) CVSS core: 9.8 影响资产数&#xff1a; 4468 …

微信小程序如何获取当前日期时间

Hello大家好&#xff01;我是咕噜铁蛋&#xff0c;获取当前日期时间是小程序中经常会用到的一个功能。因此&#xff0c;在本文中&#xff0c;我通过科技手段给大家收集整理了下&#xff0c;今天我将向大家介绍如何在微信小程序中获取当前日期时间的方法&#xff0c;并分享一些实…

【项目搭建三】SpringBoot引入redis

添加依赖 本文使用spring data redis访问和操作redis&#xff0c;pom文件中加入以下依赖&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </depende…

java开发——《并发编程》

目录 一.jmm 二.并发了什么 1.只有一个核&#xff08;单核&#xff09;并发还有没有意义 2.单核&#xff0c;还有什么可见性问题 3.并发和并行 三.volitaile 1.变量的可见性问题 2.原因是什么 3.本次修改的变量直接刷到主内存 4.声明其他内存对于这个地址的缓存无效 …

Linux:gcc的相关知识

目录 gcc的翻译&#xff08;编译&#xff09;过程&#xff1a; 预处理&#xff1a; 条件编译&#xff1a; 编译&#xff1a; 汇编&链接&#xff1a; 什么是链接&#xff1f; 安装静态库&#xff1a; 静态库的使用&#xff1a; 动态静态的对比&#xff1a; 优缺对比…

HCIE之BGP基础练习(二)

BGP 一、BGP报文和状态机1.EBGP 建立邻居仅需一条命令2.IBGP之间建立邻居只需要三条命令3.EBGP之间用回环口建立邻居需要三条命令4.下一跳5.宣告路由6.full-math邻居7.路由反射器 一、BGP报文和状态机 1.EBGP 建立邻居仅需一条命令 [AR1]bgp 100 [AR1-bgp]router-id 1.1.1.1 […

Qt5项目拆解第一集解决:中文乱码| 全局字体|注册表|QSS/CSS

# 一、乱码解决代码片段 QTextCodec是Qt中用于处理文本编码和字符集转换的类。它提供了一系列静态函数来实现不同编码的文本转换,包括编码转换、字符集检测和转换、以及数据流中的文本编码处理。QTextCodec类使得Qt可以在不同的编码和字符集之间进行无缝转换,从而方便地处理…

帮你找到99%的电子书,这46个免费电子书网站,你还不知道吗?

国内网站&#xff1a;32个 1鸠摩搜书 网址&#xff1a;https://www.jiumodiary.com/ 一个强大的搜书神站&#xff0c;无论是什么类型的书籍&#xff0c;只要你知道书名&#xff0c;就可以轻松的搜到你想要书籍。页面简单明了&#xff0c;书籍种类繁多&#xff0c;格式多种多样…

linux修改文件夹下所有文件的权限(常用)

1、 修改文件夹下所有文件的权限 # filename为要修改的文件夹名字。-R应该是表示递归修改filename文件夹下所有文件的权限 sudo chmod -R 777 filename2、linux修改单个文件夹权限 sudo chmod 600 &#xff08;只有所有者有读和写的权限&#xff09; sudo chmod 644 &#…