Vue3中实现歌词滚动显示效果

目录

🎉前言

 🎉整体布局

🎉处理歌词数据

🎉处理事件

🎉完整代码

🎉总结


🎉前言

在这篇博客中,我将分享如何在 Vue 3 中实现一个简单的歌词滚动效果。我将从歌词数据的处理开始,一步步介绍布局的搭建和事件的实现。

 🎉整体布局

1.实现歌词的滚动,首先应该给歌词设置一个特定大小的盒子里,然后可以使用overflow:hidden;来对溢出的歌词进行隐藏。

2.控制当前该高亮的歌词的样式,我是使用transform:scale(1.2)来控制文字变大的。

3.过度动画,给高亮显示的歌词加上过度动画效果,还有整个歌词区域移动的动画效果,以及黑胶唱片的旋转动画。

记住要给video标签的width和height设置为0,虽然默认情况下不显示,但是还是会影响布局的

<template><div class="box"><button @click="audioElement.play()" class="btn">播放</button><!-- 音乐播放器 --><video ref="audioElement" class="video" src="./assets/陈奕迅 - 淘汰.ogg" @timeupdate="timeUpdateHandler"@canplay="canPlayHandler"></video><div class="cd"><img src="./assets/CD.jpg" alt=""><div class="msg"><ul><li>歌词</li></ul></div></div></div>
</template><style scoped lang="scss">
.box {width: 100%;display: flex;align-items: center;background-color: #0E0A0D;flex-direction: column;height: 100vh;.btn {padding: 10px 15px;border: none;border-radius: 10%;margin-top: 20px;background-color: #84CCE6;border-color: #84CCE6;color: #000;cursor: pointer;}.video {width: 0;height: 0;}.cd {width: 400px;img {width: 200px;height: 200px;border-radius: 50%;margin: 100px 0 20px 100px;//添加一个动画效果,旋转animation: rotate 5s linear infinite;@keyframes rotate {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}}}.msg {width: 100%;height: 300px;overflow: hidden;ul {width: 100%;display: flex;flex-direction: column;//居中align-items: center;margin-top: 150px;transition: ease .5s;li {list-style: none;color: white;line-height: 30px;transition: all .5s;cursor: pointer;&.active {color: greenyellow;transform: scale(1.2);}}}}
}
</style>

🎉处理歌词数据

这是歌词数据,我把它放在了一个ts文件中,我们可以在这里对数据进行处理好后导出给组件使用

const dataStr = `
00:00 淘汰 – 陈奕迅 (Eason Chan)
00:08 词:周杰伦
00:17 曲:周杰伦
00:26 编曲:C.Y.Kong
00:35 我说了所有的谎
00:39 你全都相信
00:43 简单的我爱你
00:46 你却老不信
00:51 你书里的剧情
00:55 我不想上演
00:58 因为我喜欢
01:01 喜剧收尾
01:08 我试过完美放弃
01:12 的确很踏实
01:15 醒来了梦散了
01:19 你我都走散了
01:23 情歌的词何必押韵
01:27 就算我是K歌之王
01:31 也不见得把
01:33 爱情唱得完美
01:38 只能说我输了
01:42 也许是你怕了
01:46 我们的回忆没有皱褶
01:51 你却用离开烫下句点
01:54 只能说我认了
01:58 你的不安赢得你信任
02:03 我却得到你安慰的淘汰
02:25 我试过完美放弃
02:29 的确很踏实
02:32 醒来了梦散了
02:36 你我都走散了
02:40 情歌的词何必押韵
02:44 就算我是K歌之王
02:48 也不见得把
02:50 爱情唱得完美
02:55 只能说我输了
02:59 也许是你怕了
03:03 我们的回忆没有皱褶
03:08 你却用离开烫下句点
03:11 只能说我认了
03:15 你的不安赢得你信任
03:21 我却得到你安慰的淘汰
03:44 只能说我输了
03:48 也许是你怕了
03:52 我们的回忆没有皱褶
03:57 你却用离开烫下句点
04:00 只能说我认了
04:04 你的不安赢得你信任
04:09 我却得到你安慰的淘汰`

现在这样看着是一整个字符串,因为不好操作,所以我们要把它变成我们想要的形式。

我想要的是这种感觉,解析成一个数组就方便我们渲染以及处理事件。

 所以我们这里就使用一下分割匹配得到我们想要的效果

// 定义歌词数据类型
type Lyric = {timestamp: numbercontent: string
}//解析歌词数据
const parseLyrics = (dataStr: string): Lyric[] => {const lines = dataStr.split('\n')const lyrics: Lyric[] = []for (const line of lines) {const match = line.match(/(\d{2}):(\d{2}) (.+)/)if (match) {const [, minutes, seconds, content] = match// 将时间戳转换为秒const timestamp = parseInt(minutes) * 60 + parseInt(seconds)lyrics.push({ timestamp, content })}}return lyrics
}
// 使用解析函数获取歌词数组
const lyricsArray: Lyric[] = parseLyrics(dataStr)//导出
export default lyricsArray

这样子,就得到我们想要的结果了

🎉处理事件

首先我们得思考需要处理哪些事件

1.那句歌词得高亮?

2.偏移量为多少?

那么我们先得记得把歌词渲染上去

<li v-for="(item, index) in lyricsArray" :key="item.timestamp">{{ item.content }}</li><script setup lang="ts">
import lyricsArray from './data.ts'
</script>

第一个问题,哪句歌词得高亮

在video的dom元素上我们可以通过 currentTime 来获取它此时的播放时间位置,我们可以监听它的位置变化,然后得到需要显示的歌词的 Index,然后再li标签上对其active类名进行动态显示就行了。

第二个问题,偏移量

既然我们之前有给li标签设置line-height ,那么我们就可以再Index变化的时候一起进行计算出来。

可以再添加一个交互,就是用户点击某句歌词,跳动那个地方去

<template><div class="box"><button @click="audioElement.play()" class="btn">播放</button><!-- 音乐播放器 --><video ref="audioElement" class="video" src="./assets/陈奕迅 - 淘汰.ogg" @timeupdate="timeUpdateHandler"@canplay="canPlayHandler"></video><div class="cd"><img src="./assets/CD.jpg" alt=""><div class="msg"><ul :style="transformStyle"><li v-for="(item, index) in lyricsArray" :key="item.timestamp" :class="activeIndex === index ? 'active' : ''"@click="audioChange(item)">{{ item.content }}</li></ul></div></div></div>
</template><script setup lang="ts">
import lyricsArray from './data.ts'
import { ref, watch } from 'vue';
const audioElement = ref<any>(null); //dom元素
const currentTime = ref<string>('') //播放位置
const activeIndex = ref<number>(0)  //高亮Index
const transformStyle = ref<string>('') //偏移量function timeUpdateHandler() {// 处理播放位置变化事件console.log('播放位置变化事件');//获取播放的位置currentTime.value = audioElement.value.currentTime;
}
//监听播放的位置变化
watch(currentTime, (newVal) => {for (let i = 0; i < lyricsArray.length; i++) {if (lyricsArray[i].timestamp < +newVal) {activeIndex.value = itransformStyle.value = `transform: translateY(${-activeIndex.value * 30}px)`}}
})function canPlayHandler() {//预处理完成,可以开始播放console.log('可以开始播放');
}//处理歌词点击事件
const audioChange = (i: any) => {audioElement.value.currentTime = i.timestamp + 1;
}</script>

这样点击按钮后就能看到完美的效果了

🎉完整代码

处理歌词:

//导出歌词数据
const dataStr = `
00:00 淘汰 – 陈奕迅 (Eason Chan)
00:08 词:周杰伦
00:17 曲:周杰伦
00:26 编曲:C.Y.Kong
00:35 我说了所有的谎
00:39 你全都相信
00:43 简单的我爱你
00:46 你却老不信
00:51 你书里的剧情
00:55 我不想上演
00:58 因为我喜欢
01:01 喜剧收尾
01:08 我试过完美放弃
01:12 的确很踏实
01:15 醒来了梦散了
01:19 你我都走散了
01:23 情歌的词何必押韵
01:27 就算我是K歌之王
01:31 也不见得把
01:33 爱情唱得完美
01:38 只能说我输了
01:42 也许是你怕了
01:46 我们的回忆没有皱褶
01:51 你却用离开烫下句点
01:54 只能说我认了
01:58 你的不安赢得你信任
02:03 我却得到你安慰的淘汰
02:25 我试过完美放弃
02:29 的确很踏实
02:32 醒来了梦散了
02:36 你我都走散了
02:40 情歌的词何必押韵
02:44 就算我是K歌之王
02:48 也不见得把
02:50 爱情唱得完美
02:55 只能说我输了
02:59 也许是你怕了
03:03 我们的回忆没有皱褶
03:08 你却用离开烫下句点
03:11 只能说我认了
03:15 你的不安赢得你信任
03:21 我却得到你安慰的淘汰
03:44 只能说我输了
03:48 也许是你怕了
03:52 我们的回忆没有皱褶
03:57 你却用离开烫下句点
04:00 只能说我认了
04:04 你的不安赢得你信任
04:09 我却得到你安慰的淘汰`// 定义歌词数据类型
type Lyric = {timestamp: numbercontent: string
}//解析歌词数据
const parseLyrics = (dataStr: string): Lyric[] => {const lines = dataStr.split('\n')const lyrics: Lyric[] = []for (const line of lines) {const match = line.match(/(\d{2}):(\d{2}) (.+)/)if (match) {const [, minutes, seconds, content] = match// 将时间戳转换为秒const timestamp = parseInt(minutes) * 60 + parseInt(seconds)lyrics.push({ timestamp, content })}}return lyrics
}
// 使用解析函数获取歌词数组
const lyricsArray: Lyric[] = parseLyrics(dataStr)//导出
export default lyricsArray

组件代码:

<template><div class="box"><button @click="audioElement.play()" class="btn">播放</button><!-- 音乐播放器 --><video ref="audioElement" class="video" src="./assets/陈奕迅 - 淘汰.ogg" @timeupdate="timeUpdateHandler"@canplay="canPlayHandler"></video><div class="cd"><img src="./assets/CD.jpg" alt=""><div class="msg"><ul :style="transformStyle"><li v-for="(item, index) in lyricsArray" :key="item.timestamp" :class="activeIndex === index ? 'active' : ''"@click="audioChange(item)">{{ item.content }}</li></ul></div></div></div>
</template><script setup lang="ts">
import lyricsArray from './data.ts'
import { ref, watch } from 'vue';
const audioElement = ref<any>(null);
const currentTime = ref<string>('')
const activeIndex = ref<number>(0)
const transformStyle = ref<string>('')
console.log(lyricsArray);function timeUpdateHandler() {// 处理播放位置变化事件console.log('播放位置变化事件');//获取播放的位置currentTime.value = audioElement.value.currentTime;
}
//监听播放的位置变化
watch(currentTime, (newVal) => {for (let i = 0; i < lyricsArray.length; i++) {if (lyricsArray[i].timestamp < +newVal) {activeIndex.value = itransformStyle.value = `transform: translateY(${-activeIndex.value * 30}px)`}}
})function canPlayHandler() {// 处理可以开始播放事件console.log('可以开始播放事件');
}
//处理歌词点击事件
const audioChange = (i: any) => {audioElement.value.currentTime = i.timestamp + 1;
}</script><style scoped lang="scss">
.box {width: 100%;display: flex;align-items: center;background-color: #0E0A0D;flex-direction: column;height: 100vh;.btn {padding: 10px 15px;border: none;border-radius: 10%;margin-top: 20px;background-color: #84CCE6;border-color: #84CCE6;color: #000;cursor: pointer;}.video {width: 0;height: 0;}.cd {width: 400px;img {width: 200px;height: 200px;border-radius: 50%;margin: 100px 0 20px 100px;//添加一个动画效果,旋转animation: rotate 5s linear infinite;@keyframes rotate {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}}}.msg {width: 100%;height: 300px;overflow: hidden;ul {width: 100%;display: flex;flex-direction: column;//居中align-items: center;margin-top: 150px;transition: ease .5s;li {list-style: none;color: white;line-height: 30px;transition: all .5s;cursor: pointer;&.active {color: greenyellow;transform: scale(1.3);// font-size: 30px;}}}}
}
</style>

🎉总结

歌词数据处理

首先,我们定义了歌词数据的结构 Lyric,并通过 parseLyrics 函数将歌词字符串解析为该类型的数组。这使得我们能够更方便地处理歌词数据。

组件布局

在布局方面,我们设计了一个简单的页面结构,包括音乐播放器、CD封面和歌词展示区域。通过 ref 获取音频元素的引用,并使用 watch 监听播放位置的变化,实现了歌词的滚动效果。

事件处理

我们处理了两个主要事件:timeupdate(播放位置变化事件)和 canplay(可以开始播放事件)。通过监听播放位置变化,实时更新歌词的高亮位置,并在点击歌词时跳转到对应的播放位置。

功能扩展

我这只是实现了小功能,也不一定是最好的解决方案,大家可以增加更多的功能,让交互变得更加的有趣,便捷。

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

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

相关文章

2024美赛数学建模B题思路分析 - 搜索潜水器

1 赛题 问题B&#xff1a;搜索潜水器 总部位于希腊的小型海上巡航潜艇&#xff08;MCMS&#xff09;公司&#xff0c;制造能够将人类运送到海洋最深处的潜水器。潜水器被移动到该位置&#xff0c;并不受主船的束缚。MCMS现在希望用他们的潜水器带游客在爱奥尼亚海底探险&…

oracle 19c上安装样例数据库

样例schema的分类 HR: Human Resources OE: Order Entry PM: Product Media IX: Information Exchange SH: Sales History BI: Business Intelligence 安装样例数据库 1&#xff1a;HR的安装&#xff0c;通过dbca时候 2&#xff1a;HR的安装&#xff0c;安装完数据库后&#…

基于Go-Kit的Golang整洁架构实践

如何用Golang实现简洁架构&#xff1f;本文介绍了基于Go-Kit实现简洁架构的尝试&#xff0c;通过示例介绍了简洁架构的具体实现。原文: Why is Go-Kit Perfect For Clean Architecture in Golang? 简介 Go是整洁架构(Clean Architecture)的完美选择。整洁架构本身只是一种方法…

MySQL数据库基础第一篇(SQL通用语法与分类)

文章目录 一、SQL通用语法二、SQL分类三、DDL语句四、DML语句1.案例代码2.读出结果 五、DQL语句1.DQL-基本查询2.DQL-条件查询3.DQL-聚合函数4.DQL-分组查询5.DQL-排序查询6.DQL-分页查询7.DQL语句-执行顺序1.案例代码2.读出结果 六、DCL语句1.DCL-管理用户2.DCL-权限控制1.案例…

Kubernetes实战(二十三)-k8s event监控利器kube-eventer对接企微告警

1 背景 监控是保障系统稳定性的重要组成部分&#xff0c;在Kubernetes开源生态中&#xff0c;资源类的监控工具与组件监控比较多。 cAdvisor&#xff1a;kubelet内置的cAdvisor&#xff0c;监控容器资源&#xff0c;如容器cpu、内存&#xff1b;Kube-state-metrics&#xff1…

Maya------创建多边形工具

配合导入图像使用 Tab键可以删除一个点&#xff01; 模型不能超过4边面&#xff01;多切割工具进行连接&#xff01; 15.maya常用命令5.创建多边形工具 反转 双显 挤出_哔哩哔哩_bilibili

kubecolor让kubelet更好看

kubecolor让kubelet更好看 默认情况下kubectl输出都是白色,在列特别多的情况下比较难分清楚具体是哪个对应哪个. 发现kubecolor可以完美解决这样的问题. 1. 安装kubecolor mkdir kubecolor cd kubecolor wget https://github.com/kubecolor/kubecolor/releases/download/v0.…

vue2+html2pdf下载PDF,PDF分页切割

问题&#xff1a; PDF下载下来后&#xff0c;文档内容被暴力分割。 解决方案&#xff1a; HTML <!-- 打印按钮 --> <el-button type"primary" size"small" class"el-icon-download right_btn" click"downloadPDF">PDF&…

three.js CSS3DRenderer、CSS3DObject渲染HTML标签

有空的老铁关注一下我的抖音&#xff1a; 效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red;position: relative;"><…

CSS3的新盒子,选择器等

新增的选择器&#xff1a; 属性选择器&#xff1a; 结构伪类选择选器&#xff1a; nth较为重要&#xff1a;但公式中的字母必须是n 区别&#xff1a; nth-child&#xff1a; 认为父类下的都是儿子&#xff0c;此时就需要有对应的需要&#xff0c;如下&#xff0c;此时即使排1&…

【Bugs】Jmeter报错:NoSuchMethodError: org.apache.jmeter.samplers.

报错情况 Jmeter版本&#xff1a;5.4.3 报错场景&#xff1a;在线程组中添加了jpgc - PerfMon Metrics Collector性能监控组件后出现报错。 Jmeter中无法运行测试&#xff0c;cmd命令行中出现以下报错。 cmd报错详细内容&#xff1a; Uncaught Exception java.lang.NoSuchMe…

废品上门回收小程序搭建全过程

随着人们对环境保护意识的不断增强&#xff0c;废品回收成为了一项重要的社会活动。为了方便废品回收的顾客和回收者之间的联系&#xff0c;废品上门回收小程序成为了一种流行的解决方案。然而&#xff0c;如何选择一款合适的废品上门回收小程序搭建平台呢&#xff1f;下面将为…

数据表的增删改查(上)

这一章的主角是数据表&#xff0c;但因为上篇文章还有一些数据库的知识没解决&#xff0c;因此先写了数据库。 选择数据库 在数据库创建后&#xff0c;不会将当前创建的数据库作为后续操作的默认数据库&#xff0c;如果需要在数据库中创建数据表并插入数据&#xff0c;需要先…

leetcode189.轮转数组|超简单易于理解方法

题目 https://leetcode.cn/problems/rotate-array/description/https://leetcode.cn/problems/rotate-array/description/ 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输…

springboot完成一个线上图片存放地址+实现前后端上传图片+回显

1.路径 注意路径 2.代码&#xff1a;&#xff08;那个imagePath没什么用&#xff0c;懒的删了&#xff09;&#xff0c;注意你的本地文件夹要有图片&#xff0c;才可以在线上地址中打开查看 package com.xxx.common.config;import org.springframework.beans.factory.annotat…

深信服技术认证“SCCA-C”划重点:深信服云计算关键技术

为帮助大家更加系统化地学习云计算知识&#xff0c;高效通过云计算工程师认证&#xff0c;深信服特推出“SCCA-C认证备考秘笈”&#xff0c;共十期内容。“考试重点”内容框架&#xff0c;帮助大家快速get重点知识。 划重点来啦 *点击图片放大展示 深信服云计算认证&#xff08…

mermaid使用指南+notion使用实例-持续更新中

最近一个月了吧&#xff0c;发现Notion插入图片的功能坏了&#xff0c;直接paste会404&#xff0c;本地上传也不行。电脑本地版和手机端都插不了图片&#xff0c;很头疼。解决方法也简单&#xff0c;用图床&#xff0c;放链接。 付费版我用的七牛&#xff0c;结合PicGo&#x…

Linux 路由配置与使用

概念 路由信息用于指导数据包从源地址查找到目的地址传输路径的信息&#xff1b; 路由分类 根据路由信息的来源分为静态路由和动态路由 静态路由 由管理员手动配置的路由表项信息&#xff0c;根据路由形式的不同&#xff0c;静态路由又可细分为&#xff1a; 直连路由&#xf…

LEETCODE 170. 交易逆序对的总数

class Solution { public:int reversePairs(vector<int>& record) {if(record.size()<1)return 0;//归并 递归int left,right;left0;rightrecord.size()-1;int nummergeSort(left,right,record);return num;}int mergeSort(int left,int right, vector<int>…

【Qt】Json在Qt中的使用

Json JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;广泛用于互联网应用程序之间的数据传输。JSON基于JavaScript中的对象语法&#xff0c;但它是独立于语言的&#xff0c;因此在许多编程语言中都有对JSON的解析和生成支持。…