亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue3中實(shí)現(xiàn)歌詞滾動(dòng)顯示效果

 更新時(shí)間:2024年02月03日 11:33:29   作者:不會(huì)寫代碼  
本文分享如何在Vue 3中實(shí)現(xiàn)一個(gè)簡單的歌詞滾動(dòng)效果,我將從歌詞數(shù)據(jù)的處理開始,一步步介紹布局的搭建和事件的實(shí)現(xiàn),感興趣的朋友跟隨小編一起看看吧

??前言

在這篇博客中,我將分享如何在 Vue 3 中實(shí)現(xiàn)一個(gè)簡單的歌詞滾動(dòng)效果。我將從歌詞數(shù)據(jù)的處理開始,一步步介紹布局的搭建和事件的實(shí)現(xiàn)。

 ??整體布局

1.實(shí)現(xiàn)歌詞的滾動(dòng),首先應(yīng)該給歌詞設(shè)置一個(gè)特定大小的盒子里,然后可以使用overflow:hidden;來對(duì)溢出的歌詞進(jìn)行隱藏。

2.控制當(dāng)前該高亮的歌詞的樣式,我是使用transform:scale(1.2)來控制文字變大的。

3.過度動(dòng)畫,給高亮顯示的歌詞加上過度動(dòng)畫效果,還有整個(gè)歌詞區(qū)域移動(dòng)的動(dòng)畫效果,以及黑膠唱片的旋轉(zhuǎn)動(dòng)畫。

記住要給video標(biāo)簽的width和height設(shè)置為0,雖然默認(rèn)情況下不顯示,但是還是會(huì)影響布局的

<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;
      //添加一個(gè)動(dòng)畫效果,旋轉(zhuǎn)
      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>

??處理歌詞數(shù)據(jù)

這是歌詞數(shù)據(jù),我把它放在了一個(gè)ts文件中,我們可以在這里對(duì)數(shù)據(jù)進(jìn)行處理好后導(dǎo)出給組件使用

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 因?yàn)槲蚁矚g
01:01 喜劇收尾
01:08 我試過完美放棄
01:12 的確很踏實(shí)
01:15 醒來了夢(mèng)散了
01:19 你我都走散了
01:23 情歌的詞何必押韻
01:27 就算我是K歌之王
01:31 也不見得把
01:33 愛情唱得完美
01:38 只能說我輸了
01:42 也許是你怕了
01:46 我們的回憶沒有皺褶
01:51 你卻用離開燙下句點(diǎn)
01:54 只能說我認(rèn)了
01:58 你的不安贏得你信任
02:03 我卻得到你安慰的淘汰
02:25 我試過完美放棄
02:29 的確很踏實(shí)
02:32 醒來了夢(mèng)散了
02:36 你我都走散了
02:40 情歌的詞何必押韻
02:44 就算我是K歌之王
02:48 也不見得把
02:50 愛情唱得完美
02:55 只能說我輸了
02:59 也許是你怕了
03:03 我們的回憶沒有皺褶
03:08 你卻用離開燙下句點(diǎn)
03:11 只能說我認(rèn)了
03:15 你的不安贏得你信任
03:21 我卻得到你安慰的淘汰
03:44 只能說我輸了
03:48 也許是你怕了
03:52 我們的回憶沒有皺褶
03:57 你卻用離開燙下句點(diǎn)
04:00 只能說我認(rèn)了
04:04 你的不安贏得你信任
04:09 我卻得到你安慰的淘汰`

現(xiàn)在這樣看著是一整個(gè)字符串,因?yàn)椴缓貌僮鳎晕覀円阉兂晌覀兿胍男问健?/p>

我想要的是這種感覺,解析成一個(gè)數(shù)組就方便我們渲染以及處理事件。

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

// 定義歌詞數(shù)據(jù)類型
type Lyric = {
  timestamp: number
  content: string
}
//解析歌詞數(shù)據(jù)
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
      // 將時(shí)間戳轉(zhuǎn)換為秒
      const timestamp = parseInt(minutes) * 60 + parseInt(seconds)
      lyrics.push({ timestamp, content })
    }
  }
  return lyrics
}
// 使用解析函數(shù)獲取歌詞數(shù)組
const lyricsArray: Lyric[] = parseLyrics(dataStr)
//導(dǎo)出
export default lyricsArray

這樣子,就得到我們想要的結(jié)果了

??處理事件

首先我們得思考需要處理哪些事件

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>

第一個(gè)問題,哪句歌詞得高亮

在video的dom元素上我們可以通過 currentTime 來獲取它此時(shí)的播放時(shí)間位置,我們可以監(jiān)聽它的位置變化,然后得到需要顯示的歌詞的 Index,然后再li標(biāo)簽上對(duì)其active類名進(jìn)行動(dòng)態(tài)顯示就行了。

第二個(gè)問題,偏移量

既然我們之前有給li標(biāo)簽設(shè)置line-height ,那么我們就可以再Index變化的時(shí)候一起進(jìn)行計(jì)算出來。

可以再添加一個(gè)交互,就是用戶點(diǎn)擊某句歌詞,跳動(dòng)那個(gè)地方去

<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;
}
//監(jiān)聽播放的位置變化
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() {
  //預(yù)處理完成,可以開始播放
  console.log('可以開始播放');
}
//處理歌詞點(diǎn)擊事件
const audioChange = (i: any) => {
  audioElement.value.currentTime = i.timestamp + 1;
}
</script>

這樣點(diǎn)擊按鈕后就能看到完美的效果了

??完整代碼

處理歌詞:

//導(dǎo)出歌詞數(shù)據(jù)
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 因?yàn)槲蚁矚g
01:01 喜劇收尾
01:08 我試過完美放棄
01:12 的確很踏實(shí)
01:15 醒來了夢(mèng)散了
01:19 你我都走散了
01:23 情歌的詞何必押韻
01:27 就算我是K歌之王
01:31 也不見得把
01:33 愛情唱得完美
01:38 只能說我輸了
01:42 也許是你怕了
01:46 我們的回憶沒有皺褶
01:51 你卻用離開燙下句點(diǎn)
01:54 只能說我認(rèn)了
01:58 你的不安贏得你信任
02:03 我卻得到你安慰的淘汰
02:25 我試過完美放棄
02:29 的確很踏實(shí)
02:32 醒來了夢(mèng)散了
02:36 你我都走散了
02:40 情歌的詞何必押韻
02:44 就算我是K歌之王
02:48 也不見得把
02:50 愛情唱得完美
02:55 只能說我輸了
02:59 也許是你怕了
03:03 我們的回憶沒有皺褶
03:08 你卻用離開燙下句點(diǎn)
03:11 只能說我認(rèn)了
03:15 你的不安贏得你信任
03:21 我卻得到你安慰的淘汰
03:44 只能說我輸了
03:48 也許是你怕了
03:52 我們的回憶沒有皺褶
03:57 你卻用離開燙下句點(diǎn)
04:00 只能說我認(rèn)了
04:04 你的不安贏得你信任
04:09 我卻得到你安慰的淘汰`
// 定義歌詞數(shù)據(jù)類型
type Lyric = {
  timestamp: number
  content: string
}
//解析歌詞數(shù)據(jù)
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
      // 將時(shí)間戳轉(zhuǎn)換為秒
      const timestamp = parseInt(minutes) * 60 + parseInt(seconds)
      lyrics.push({ timestamp, content })
    }
  }
  return lyrics
}
// 使用解析函數(shù)獲取歌詞數(shù)組
const lyricsArray: Lyric[] = parseLyrics(dataStr)
//導(dǎo)出
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;
}
//監(jiān)聽播放的位置變化
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('可以開始播放事件');
}
//處理歌詞點(diǎn)擊事件
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;
      //添加一個(gè)動(dòng)畫效果,旋轉(zhuǎn)
      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>

??總結(jié)

歌詞數(shù)據(jù)處理

首先,我們定義了歌詞數(shù)據(jù)的結(jié)構(gòu) Lyric,并通過 parseLyrics 函數(shù)將歌詞字符串解析為該類型的數(shù)組。這使得我們能夠更方便地處理歌詞數(shù)據(jù)。

組件布局

在布局方面,我們?cè)O(shè)計(jì)了一個(gè)簡單的頁面結(jié)構(gòu),包括音樂播放器、CD封面和歌詞展示區(qū)域。通過 ref 獲取音頻元素的引用,并使用 watch 監(jiān)聽播放位置的變化,實(shí)現(xiàn)了歌詞的滾動(dòng)效果。

事件處理

我們處理了兩個(gè)主要事件:timeupdate(播放位置變化事件)和 canplay(可以開始播放事件)。通過監(jiān)聽播放位置變化,實(shí)時(shí)更新歌詞的高亮位置,并在點(diǎn)擊歌詞時(shí)跳轉(zhuǎn)到對(duì)應(yīng)的播放位置。

功能擴(kuò)展

我這只是實(shí)現(xiàn)了小功能,也不一定是最好的解決方案,大家可以增加更多的功能,讓交互變得更加的有趣,便捷。

到此這篇關(guān)于Vue3中實(shí)現(xiàn)歌詞滾動(dòng)顯示效果的文章就介紹到這了,更多相關(guān)Vue3歌詞滾動(dòng)顯示內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入了解Vue Pinia持久化存儲(chǔ)二次封裝

    深入了解Vue Pinia持久化存儲(chǔ)二次封裝

    Pinia 是2019年由vue.js官方成員重新設(shè)計(jì)的新一代狀態(tài)管理庫,類似Vuex,下面我們就來學(xué)習(xí)一下如何通過Pinia實(shí)現(xiàn)持久化存儲(chǔ)的相關(guān)知識(shí),感興趣的小伙伴可以了解下
    2023-12-12
  • vue使用Echarts設(shè)置數(shù)據(jù)無效問題記錄及解決方法

    vue使用Echarts設(shè)置數(shù)據(jù)無效問題記錄及解決方法

    這篇文章主要介紹了vue使用Echarts設(shè)置數(shù)據(jù)無效問題記錄,本文通過場(chǎng)景分析給大家分享解決方法,需要的朋友可以參考下
    2022-08-08
  • vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式

    vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式

    這篇文章主要介紹了vue用復(fù)選框?qū)崿F(xiàn)組件且支持單選和多選操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • vue樣式穿透 ::v-deep的具體使用

    vue樣式穿透 ::v-deep的具體使用

    這篇文章主要介紹了vue樣式穿透 ::v-deep的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • vue3表單參數(shù)校驗(yàn)及正則表達(dá)式舉例詳解

    vue3表單參數(shù)校驗(yàn)及正則表達(dá)式舉例詳解

    最近項(xiàng)目中有一個(gè)校驗(yàn)身份證號(hào)手機(jī)號(hào)的業(yè)務(wù),索性給大家總結(jié)下,這篇文章主要給大家介紹了關(guān)于vue3表單參數(shù)校驗(yàn)及正則表達(dá)式的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • Element中Upload組件上傳功能實(shí)現(xiàn)(圖片和文件的默認(rèn)上傳及自定義上傳)

    Element中Upload組件上傳功能實(shí)現(xiàn)(圖片和文件的默認(rèn)上傳及自定義上傳)

    這篇文章主要介紹了Element中Upload組件上傳功能實(shí)現(xiàn)包括圖片和文件的默認(rèn)上傳及自定義上傳,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • vue實(shí)現(xiàn)廣告欄上下滾動(dòng)效果

    vue實(shí)現(xiàn)廣告欄上下滾動(dòng)效果

    這篇文章主要介紹了vue實(shí)現(xiàn)廣告欄上下滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • vue實(shí)現(xiàn)自定義樹形組件的示例代碼

    vue實(shí)現(xiàn)自定義樹形組件的示例代碼

    這篇文章主要介紹了vue實(shí)現(xiàn)自定義樹形組件的示例代碼,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • vue項(xiàng)目發(fā)布有緩存正式環(huán)境不更新的解決方案

    vue項(xiàng)目發(fā)布有緩存正式環(huán)境不更新的解決方案

    vue項(xiàng)目每次發(fā)布新版本后,測(cè)試人員都要強(qiáng)制刷新才能更新瀏覽器代碼來驗(yàn)證bug,下面這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目發(fā)布有緩存正式環(huán)境不更新的解決方案,需要的朋友可以參考下
    2024-03-03
  • vue3基于elementplus 簡單實(shí)現(xiàn)表格二次封裝過程

    vue3基于elementplus 簡單實(shí)現(xiàn)表格二次封裝過程

    公司渲染表格數(shù)據(jù)時(shí)需要將空數(shù)據(jù)顯示‘-’,并且對(duì)于每一列數(shù)據(jù)的顯示也有一定的要求,基于這個(gè)需求對(duì)element-plus簡單進(jìn)行了二次封裝,這篇文章主要介紹了vue3基于elementplus 簡單實(shí)現(xiàn)表格二次封裝過程,需要的朋友可以參考下
    2024-05-05

最新評(píng)論