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

 更新时间:2024年02月03日 11:33:29   作者:不会写代码  
本文分享如何在Vue 3中实现一个简单的歌词滚动效果,我将从歌词数据的处理开始,一步步介绍布局的搭建和事件的实现,感兴趣的朋友跟随小编一起看看吧

🎉前言

在这篇博客中,我将分享如何在 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: number
  content: 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 = i
      transformStyle.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: number
  content: 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 = i
      transformStyle.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(可以开始播放事件)。通过监听播放位置变化,实时更新歌词的高亮位置,并在点击歌词时跳转到对应的播放位置。

功能扩展

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

到此这篇关于Vue3中实现歌词滚动显示效果的文章就介绍到这了,更多相关Vue3歌词滚动显示内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vscode vue 文件模板的配置方法

    vscode vue 文件模板的配置方法

    这篇文章主要介绍了vscode vue 文件模板的配置方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • vue使用中的内存泄漏【推荐】

    vue使用中的内存泄漏【推荐】

    内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。这篇文章主要介绍了vue使用中的内存泄漏,需要的朋友可以参考下
    2018-07-07
  • vue项目中图片选择路径位置static或assets的区别及说明

    vue项目中图片选择路径位置static或assets的区别及说明

    这篇文章主要介绍了vue项目中图片选择路径位置static或assets的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • vue3中Vant的使用及说明

    vue3中Vant的使用及说明

    这篇文章主要介绍了vue3中Vant的使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • vue点击单张图片放大实现步骤(纯js)

    vue点击单张图片放大实现步骤(纯js)

    这篇文章主要给大家介绍了关于vue点击单张图片放大实现的相关资料,在vue项目中实现点击图片放大功能相信对大家来说都不陌生,文中给出了详细的js示例代码,需要的朋友可以参考下
    2023-07-07
  • el-element中el-table表格嵌套el-select实现动态选择对应值功能

    el-element中el-table表格嵌套el-select实现动态选择对应值功能

    这篇文章主要给大家介绍了关于el-element中el-table表格嵌套el-select实现动态选择对应值功能的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-01-01
  • vue-i18n实现中英文切换的方法

    vue-i18n实现中英文切换的方法

    这篇文章主要介绍了vue-i18n实现中英文切换的方法,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • 如何利用vue3实现一个俄罗斯方块

    如何利用vue3实现一个俄罗斯方块

    俄罗斯方块这个游戏相信大家都玩过,下面这篇文章主要给大家介绍了关于如何利用vue3实现一个俄罗斯方块的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • Vue Token过期问题的2种解决方案小结

    Vue Token过期问题的2种解决方案小结

    在使用token进行登录的过程中,如果token过期了,需要重新输入用户名和密码登录,这种体验肯定是不好的,下面这篇文章主要给大家介绍了关于Vue Token过期问题的2种解决方案,需要的朋友可以参考下
    2023-02-02
  • vue项目使用node连接数据库的方法(前后端分离)

    vue项目使用node连接数据库的方法(前后端分离)

    这篇文章主要介绍了vue项目使用node连接数据库(前后端分离),本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12

最新评论