Vue3實(shí)現(xiàn)簡(jiǎn)易音樂(lè)播放器組件
前言
用Vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)易的音樂(lè)播放器組件
其效果圖如下所示:
實(shí)現(xiàn)這個(gè)組件需要提前做的準(zhǔn)備:
- 引入ElementUI
- 引入字節(jié)跳動(dòng)圖標(biāo)庫(kù)
- 一張唱見(jiàn)圖片
- 將要播放的音樂(lè)上傳到文件服務(wù)器上,并提供一個(gè)能在線(xiàn)訪(fǎng)問(wèn)的鏈接【這里使用的是阿里云的OSS服務(wù)】
準(zhǔn)備
ElementUI
ElementUI的引入可以參照其官網(wǎng)的引入方式;
字節(jié)跳動(dòng)圖標(biāo)庫(kù)
組件的【上一首】【播放】【下一首】【音量】等圖標(biāo)都是來(lái)源自這個(gè)圖標(biāo)庫(kù),這是其安裝文檔
在main.js中,我是這樣引入的:
//引入字節(jié)跳動(dòng)圖標(biāo)庫(kù) import {install} from '@icon-park/vue-next/es/all'; import '@icon-park/vue-next/styles/index.css'; ...... //這種加載方式進(jìn)行加載的話(huà),代表使用默認(rèn)的前綴進(jìn)行加載:icon //也就是說(shuō)假如要使用一個(gè)主頁(yè)圖標(biāo),使用圖標(biāo)時(shí)標(biāo)簽該這么寫(xiě):? //<icon-home theme="outline" size="24" fill="#FFFFFF" :strokeWidth="2"/> //install(app,'prefix') 用這種方式進(jìn)行加載的話(huà),可以自定義使用圖標(biāo)庫(kù)時(shí)的標(biāo)簽前綴 install(app)
唱見(jiàn)圖片
音樂(lè)源
將要播放的音樂(lè)放到文件服務(wù)器上,我這里是使用阿里云的OSS服務(wù)進(jìn)行音樂(lè)文件的存儲(chǔ),然后在整個(gè)頁(yè)面加載時(shí)【也就是在onMounted生命周期函數(shù)中獲取這些數(shù)據(jù)源】。在后面的代碼中,這一步體現(xiàn)在:
//初始化歌曲源 const initMusicArr = () => { ? ? ? ? requests.get("/Music/QueryAllMusic").then(function (res) { ? ? ? ? ? ? musicState.musicArr = res ? ? ? ? ? ? musicState.musicCount = res.length ? ? ? ? }) ? ? } ? ? onMounted(() => { ? ? ? ? initMusicArr() ? ? ? ? ? ? ...... ? ? })
完整代碼
<template> ? <!--音樂(lè)播放器--> ? <div class="music-container" :class="{'music-active-switch': offsetThreshold}"> ? ? <div class="music-disk"> ? ? ? <!--唱片圖片--> ? ? ? <img class="music-disk-picture" :class="{'music-disk-playing-style': playState}" src="./images/R-C.png" ? ? ? ? ? ?alt=""> ? ? </div> ? ? <!--進(jìn)度條--> ? ? <div class="music-slider"> ? ? ? <el-slider ? ? ? ? ? v-model="playTime" ? ? ? ? ? :format-tooltip="tooltipFormat" ? ? ? ? ? size="small" ? ? ? ? ? :max="sliderLength" ? ? ? ? ? @change="changePlayTime"/> ? ? </div> ? ? <!--按鈕組--> ? ? <div class="button-group"> ? ? ? <!--上一曲 按鈕--> ? ? ? <button class="play-button" @click="lastButtonClick"> ? ? ? ? <icon-go-start theme="outline" size="23" fill="#939393" :strokeWidth="3" strokeLinejoin="miter" ? ? ? ? ? ? ? ? ? ? ? ?strokeLinecap="butt"/> ? ? ? </button> ? ? ? <!--播放 按鈕--> ? ? ? <button class="play-button" @click="playButtonClick"> ? ? ? ? <icon-play-one v-if="!playState" theme="outline" size="23" fill="#939393" :strokeWidth="3" ? ? ? ? ? ? ? ? ? ? ? ?strokeLinejoin="miter" strokeLinecap="butt"/> ? ? ? ? <icon-pause v-if="playState" theme="outline" size="23" fill="#939393" :strokeWidth="3" ? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/> ? ? ? </button> ? ? ? <!--下一曲 按鈕--> ? ? ? <button class="play-button" @click="nextButtonClick"> ? ? ? ? <icon-go-end theme="outline" size="23" fill="#939393" :strokeWidth="3" strokeLinejoin="miter" ? ? ? ? ? ? ? ? ? ? ?strokeLinecap="butt"/> ? ? ? </button> ? ? ? <!--音量按鈕--> ? ? ? <div class="voice-container"> ? ? ? ? <button class="voice-button" @click="voiceButtonClick"> ? ? ? ? ? <icon-volume-notice v-if="!voiceMute" theme="outline" size="23" fill="#939393" :strokeWidth="3" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/> ? ? ? ? ? <icon-volume-mute v-if="voiceMute" theme="outline" size="23" fill="#939393" :strokeWidth="3" ? ? ? ? ? ? ? ? ? ? ? ? ? ? strokeLinejoin="miter" strokeLinecap="butt"/> ? ? ? ? </button> ? ? ? ? <div class="voice-slider"> ? ? ? ? ? <el-slider ? ? ? ? ? ? ? v-model="voicePower" ? ? ? ? ? ? ? :max="1" ? ? ? ? ? ? ? :step="0.1" ? ? ? ? ? ? ? size="small" ? ? ? ? ? ? ? @change="changeVoicePower"/> ? ? ? ? </div> ? ? ? </div> ? ? </div> ? ? <audio ? ? ? ? ref="musicAudio" ? ? ? ? class="audio-component" ? ? ? ? controls ? ? ? ? preload="auto" ? ? ? ? @canplay="changeDuration"> ? ? ? <source ref="musicSource" type="audio/mpeg"/> ? ? </audio> ? </div> </template> <script> import {computed, onMounted, onUnmounted, reactive, ref, watch} from "vue"; //這里是自己封裝的axios請(qǐng)求,可以將這里替換成自己的請(qǐng)求邏輯 import requests from "@/api/ajax"; export default { ? name: "index", ? setup() { ? ? //是否正在播放 ? ? const playState = ref(false); ? ? //現(xiàn)在的播放時(shí)間 ? ? const playTime = ref(0.00); ? ? //歌曲的時(shí)間長(zhǎng)度 ? ? const playDuration = ref(0.00); ? ? //進(jìn)度條長(zhǎng)度 ? ? const sliderLength = ref(100); ? ? //歌曲URL ? ? const musicUrl = ref(""); ? ? //播放器標(biāo)簽 ? ? const musicAudio = ref(null); ? ? //實(shí)現(xiàn)音樂(lè)播放的標(biāo)簽 ? ? const musicSource = ref(null); ? ? //是否靜音 ? ? const voiceMute = ref(false); ? ? //音量大小 ? ? const voicePower = ref(0.5); ? ? const musicState = reactive({ ? ? ? musicArr: [], ? ? ? musicCount: 0 ? ? }) ? ? const musicCursor = ref(0); ? ? //頁(yè)面偏移量 ? ? const pageOffset = ref(0) ? ? //是否達(dá)到閾值,達(dá)到閾值就顯示播放器,反之 ? ? const offsetThreshold = ref(false) ? ? //激活播放器 ? ? const operateMusicPlayer = () => { ? ? ? pageOffset.value = window.scrollY ? ? ? //當(dāng)頁(yè)面滾動(dòng)偏移達(dá)到800,激活用戶(hù)框 ? ? ? if (pageOffset.value > 800) { ? ? ? ? offsetThreshold.value = true ? ? ? } else { ? ? ? ? //反之 ? ? ? ? offsetThreshold.value = false ? ? ? } ? ? } ? ? //播放按鈕點(diǎn)擊回調(diào) ? ? const playButtonClick = () => { ? ? ? if (playState.value) { ? ? ? ? musicAudio.value.pause() ? ? ? } else { ? ? ? ? musicAudio.value.play() ? ? ? } ? ? ? //修改播放時(shí)間【設(shè)置這個(gè),當(dāng)一首歌正常播放結(jié)束之后,再次點(diǎn)擊播放按鈕,進(jìn)度條會(huì)得到重置】 ? ? ? playTime.value = musicAudio.value.currentTime ? ? ? //重新設(shè)置播放狀態(tài) ? ? ? playState.value = !playState.value ? ? } ? ? //上一曲按鈕點(diǎn)擊回調(diào) ? ? const lastButtonClick = () => { ? ? ? musicCursor.value -= 1 ? ? ? changeMusic() ? ? } ? ? //下一曲按鈕點(diǎn)擊回調(diào) ? ? const nextButtonClick = () => { ? ? ? musicCursor.value += 1 ? ? ? changeMusic() ? ? } ? ? //歌曲進(jìn)度條文本提示 ? ? const tooltipFormat = (val) => { ? ? ? let strTime = playTime.value ? ? ? let strMinute = parseInt(strTime / 60 + '') ? ? ? let strSecond = parseInt(strTime % 60 + '') ? ? ? return strMinute + ":" + strSecond ? ? } ? ? //當(dāng)歌曲能播放時(shí)【亦即在canplay鉤子函數(shù)中】,musicAudio.value.duration才不會(huì)是NaN,才能進(jìn)行歌曲長(zhǎng)度的設(shè)置 ? ? const changeDuration = () => { ? ? ? if (playDuration.value != musicAudio.value.duration) { ? ? ? ? //修改進(jìn)度條的最大值 ? ? ? ? sliderLength.value = musicAudio.value.duration ? ? ? ? //修改歌曲播放時(shí)間 ? ? ? ? playDuration.value = musicAudio.value.duration ? ? ? } ? ? } ? ? //el-slider的鉤子函數(shù),拖動(dòng)進(jìn)度條時(shí)快進(jìn)歌曲,改變當(dāng)前播放進(jìn)度 ? ? const changePlayTime = (val) => { ? ? ? musicAudio.value.currentTime = val ? ? } ? ? //音量按鈕點(diǎn)擊回調(diào) ? ? const voiceButtonClick = () => { ? ? ? voiceMute.value = !voiceMute.value ? ? ? if (!voiceMute.value) { ? ? ? ? voicePower.value = 1 ? ? ? ? musicAudio.value.volume = 1 ? ? ? } else { ? ? ? ? voicePower.value = 0 ? ? ? ? musicAudio.value.volume = 0 ? ? ? } ? ? } ? ? //el-slider的鉤子函數(shù),用于調(diào)節(jié)音量 ? ? const changeVoicePower = (val) => { ? ? ? musicAudio.value.volume = val ? ? ? voicePower.value = val ? ? ? if (val > 0) { ? ? ? ? voiceMute.value = false ? ? ? } else { ? ? ? ? voiceMute.value = true ? ? ? } ? ? } ? ? //播放狀態(tài)下,進(jìn)度條里的數(shù)值每秒遞增。而Audio因?yàn)樵诓シ艩顟B(tài)下,currentTime會(huì)自己遞增,所以不用處理 ? ? const updatePlayTimePerSecond = () => { ? ? ? if (playState.value) { ? ? ? ? playTime.value += 1 ? ? ? ? if (playTime.value >= playDuration.value) { ? ? ? ? ? //代表當(dāng)前歌曲已經(jīng)播放完畢,進(jìn)行切歌 ? ? ? ? ? musicCursor.value++ ? ? ? ? ? changeMusic() ? ? ? ? } ? ? ? } ? ? } ? ? //切歌 ? ? const changeMusic = () => { ? ? ? //切歌【這里的music_url是后端返回給前端的json字符串中,用于存儲(chǔ)歌曲在線(xiàn)鏈接的屬性名是:music_url,所以要實(shí)現(xiàn)自己的請(qǐng)求邏輯,將這里的music_url改為自己的即可】 ? ? ? musicSource.value.src = musicState.musicArr[musicCursor.value % musicState.musicCount].music_url ? ? ? // 當(dāng)刷新了url之后,需要執(zhí)行l(wèi)oad方法才能播放這個(gè)音樂(lè) ? ? ? musicAudio.value.load() ? ? ? playTime.value = musicAudio.value.currentTime ? ? ? sliderLength.value = musicAudio.value.duration ? ? ? musicAudio.value.play() ? ? ? playState.value = true ? ? } ? ? //初始化歌曲源【將這里替換成自己的請(qǐng)求邏輯】 ? ? const initMusicArr = () => { ? ? ? requests.get("/Music/QueryAllMusic").then(function (res) { ? ? ? ? musicState.musicArr = res ? ? ? ? musicState.musicCount = res.length ? ? ? }) ? ? } ? ? onMounted(() => { ? ? ? initMusicArr() ? ? ? //播放狀態(tài)下,使播放進(jìn)度自增1,以與Audio內(nèi)置的currentTime相匹配 ? ? ? setInterval(updatePlayTimePerSecond, 1000) ? ? ? //添加滾動(dòng)事件 ? ? ? window.addEventListener("scroll", operateMusicPlayer) ? ? }) ? ? onUnmounted(() => { ? ? ? window.removeEventListener("scroll", operateMusicPlayer) ? ? }) ? ? return { ? ? ? musicAudio, ? ? ? musicSource, ? ? ? playState, ? ? ? playTime, ? ? ? playDuration, ? ? ? sliderLength, ? ? ? musicUrl, ? ? ? voiceMute, ? ? ? voicePower, ? ? ? musicState, ? ? ? musicCursor, ? ? ? pageOffset, ? ? ? offsetThreshold, ? ? ? playButtonClick, ? ? ? lastButtonClick, ? ? ? nextButtonClick, ? ? ? voiceButtonClick, ? ? ? tooltipFormat, ? ? ? changeMusic, ? ? ? changeDuration, ? ? ? changePlayTime, ? ? ? changeVoicePower, ? ? ? updatePlayTimePerSecond, ? ? ? initMusicArr ? ? } ? }, } </script> <style scoped> .music-container { ? position: fixed; ? justify-content: center; ? width: 280px; ? height: 110px; ? background-color: white; ? border-radius: 15px; ? bottom: 15px; ? left: 10px; ? opacity: 0; ? transition: 0.5s; } .music-disk { ? position: absolute; ? width: 90px; ? height: 90px; ? left: 15px; ? top: 10px; ? border-radius: 50%; } .music-disk-picture { ? width: 90px; ? height: 90px; ? border-radius: 50%; ? /*設(shè)置圖片不可點(diǎn)擊*/ ? pointer-events: none; } .music-disk-playing-style { ? animation: music-disk-rotate 5s linear infinite; } @keyframes music-disk-rotate { ? 0% { ? ? transform: rotate(0deg); ? } ? 100% { ? ? transform: rotate(360deg); ? } } .button-group { ? position: absolute; ? width: 330px; ? height: 38px; ? left: 90px; ? bottom: 13px; ? margin-left: 10px; } .button-group > button { ? margin-left: 10px; } .play-button { ? float: left; ? width: 31px; ? height: 31px; ? padding: 4px; ? /*margin: 0px;*/ ? border: 0px; ? border-radius: 50%; ? margin: 7px 0px 0px 0px; } .voice-button { ? float: left; ? width: 31px; ? height: 31px; ? padding: 0px; ? /*margin: 0px;*/ ? border: 0px; ? border-radius: 50%; ? margin: 7px 0px 0px 0px; ? background-color: transparent; } .music-slider { ? position: absolute; ? top: 20px; ? left: 120px; ? width: 50%; } .voice-container { ? float: left; ? margin-left: 12px; ? width: 31px; ? height: 38px; ? overflow: hidden !important; ? transition: 0.5s; } .voice-container:hover { ? width: 160px; } .voice-slider { ? position: relative; ? top: 2px; ? right: -30px; ? width: 90px; ? height: 35px; ? background-color: white; ? border-radius: 10px; ? padding: 0px 15px 0px 15px; ? transition: 0.2s; } .audio-component { ? width: 300px; ? height: 200px; ? top: 100px; ? display: none; } .music-active-switch{ ? opacity: 1; } </style>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue使用element-ui的el-image的問(wèn)題分析
這篇文章主要介紹了vue使用element-ui的el-image的問(wèn)題分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01antd?vue?表格rowSelection選擇框功能的使用方式
這篇文章主要介紹了antd?vue?表格rowSelection選擇框功能的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。2022-12-12vue-calendar-component日歷組件報(bào)錯(cuò)Clock is not defi
這篇文章主要為大家介紹了vue-calendar-component日歷組件報(bào)錯(cuò)Clock is not defined解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11微信小程序?qū)崙?zhàn)基于vue2實(shí)現(xiàn)瀑布流的代碼實(shí)例
瀑布流,又稱(chēng)瀑布流式布局,是比較流行的一種網(wǎng)站頁(yè)面布局,視覺(jué)表現(xiàn)為參差不齊的多欄布局,隨著頁(yè)面滾動(dòng)條向下滾動(dòng),這種布局還會(huì)不斷加載數(shù)據(jù)塊并附加至當(dāng)前尾部,這篇文章主要介紹了微信小程序?qū)崙?zhàn),基于vue2實(shí)現(xiàn)瀑布流,需要的朋友可以參考下2022-12-12vue計(jì)算屬性時(shí)v-for處理數(shù)組時(shí)遇到的一個(gè)bug問(wèn)題
這篇文章主要介紹了在做vue計(jì)算屬性,v-for處理數(shù)組時(shí)遇到的一個(gè)bug 問(wèn)題,需要的朋友可以參考下2018-01-01vue3中通過(guò)ref獲取元素節(jié)點(diǎn)的實(shí)現(xiàn)
這篇文章主要介紹了vue3中通過(guò)ref獲取元素節(jié)點(diǎn)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07