【音乐App】—— Vue-music 项目学习笔记:播放器内置组件开发(一)
前言:以下內(nèi)容均為學(xué)習(xí)慕課網(wǎng)高級實戰(zhàn)課程的實踐爬坑筆記。
項目github地址:https://github.com/66Web/ljq_vue_music,歡迎Star。
| 播放暫停 | 前進(jìn)后退 |
| 一、播放器Vuex數(shù)據(jù)設(shè)計 |
- 需求: 播放器可以通過歌手詳情列表、歌單詳情列表、排行榜、搜索結(jié)果多種組件打開,因此播放器數(shù)據(jù)一定是全局的
- state.js目錄下:定義數(shù)據(jù) import {playMode} from '@/common/js/config'const state = {singer: {},playing: false, //播放狀態(tài)fullScreen: false, //播放器展開方式:全屏或收起playlist: [], //播放列表(隨機模式下與順序列表不同)sequenceList: [], //順序播放列表mode: playMode.sequence, //播放模式: 順序、循環(huán)、隨機currentIndex: -1 //當(dāng)前播放歌曲的index(當(dāng)前播放歌曲為playlist[index]) }
- common->js目錄下:創(chuàng)建config.js配置項目相關(guān) //播放器播放模式: 順序、循環(huán)、隨機 export const playMode = {sequence: 0, loop: 1,random: 2 }
- getter.js目錄下:數(shù)據(jù)映射(類似于計算屬性) export const playing = state => state.playing
export const fullScreen = state => state.fullScreen
export const playlist = state => state.playlist
export const sequenceList = state => state.sequenceList
export const mode = state => state.mode
export const currentIndex = state => state.currentIndexexport const currentSong = (state) => {return state.playlist[state.currentIndex] || {}
}
組件中可以通過mapgetters拿到這些數(shù)據(jù)
- mutaion-type.js目錄下:定義事件類型字符串常量 export const SET_PLAYING_STATE = 'SET_PLAYING_STATE'
export const SET_FULL_SCREEN = 'SET_FULL_SCREEN'
export const SET_PLAYLIST = 'SET_PLAYLIST'
export const SET_SEQUENCE_LIST = 'SET_SEQUENCE_LIST'
export const SET_PLAY_MODE = 'SET_PLAY_MODE'
export const SET_CURRENT_INDEX = 'SET_CURRENT_INDEX'
mutation中都是動作,前綴加SET、UPDATE等
- mutaion.js目錄下:操作state const mutations = {[types.SET_SINGER](state, singer){state.singer = singer},[types.SET_PLAYING_STATE](state, flag){state.playing = flag},[types.SET_FULL_SCREEN](state, flag){state.fullScreen = flag},[types.SET_PLAYLIST](state, list){state.playlist = list},[types.SET_SEQUENCE_LIST](state, list){state.sequenceList = list},[types.SET_PLAY_MODE](state, mode){state.mode = mode},[types.SET_CURRENT_INDEX](state, index){state.currentIndex = index} }
| 二、播放器Vuex的相關(guān)應(yīng)用 |
- components->player目錄下:創(chuàng)建player.vue
- ?App.vue中應(yīng)用player組件:因為它不是任何一個路由相關(guān)組件,而是應(yīng)用相關(guān)播放器,切換路由不會影響播放器的播放 <player></player>
- player.vue中獲取數(shù)據(jù):控制播放器的顯示隱藏 import {mapGetters} from 'vuex'computed: {...mapGetters(['fullScreen','playlist'])
}
通過v-show判斷播放列表有內(nèi)容時,顯示播放器,依據(jù)fullScreen控制顯示不同的播放器
<div class="player" v-show="playlist.length"><div class="normal-player" v-show="fullScreen">播放器</div><div class="mini-player" v-show="!fullScreen"></div> </div> - song-list.vue中添加點擊播放事件:基礎(chǔ)組件不寫業(yè)務(wù)邏輯,只派發(fā)事件并傳遞相關(guān)數(shù)據(jù) @click="selectItem(song, index) selectItem(item, index){this.$emit('select', item, index)
}
子組件行為,只依賴本身相關(guān),不依賴外部調(diào)用組件的需求,傳出的數(shù)據(jù)可以不都使用
- music-list.vue中監(jiān)聽select事件 <song-list :songs="songs" @select="selectItem"></song-list>
| 三、播放器基礎(chǔ)樣式及歌曲數(shù)據(jù)的應(yīng)用 |
- ?通過mapGetter獲取到currentSong數(shù)據(jù)填入到DOM中:點擊切換播放器展開收起,需要修改fullScreen import {mapGetters, mapMutations} from 'vuex'methods: {back() {//錯誤做法: this.fullScreen = false//正確做法: 通過mapMutations寫入 this.setFullScreen(false)},open() {this.setFullScreen(true)},...mapMutations({setFullScreen: 'SET_FULL_SCREEN'}) }
| 四、播放器展開收起動畫 |
- 需求:normal-player背景圖片漸隱漸現(xiàn),展開時頭部標(biāo)題從頂部下落,底部按鈕從底部回彈,收起時相反
- 實現(xiàn):動畫使用<transition>,回彈效果使用貝塞爾曲線
- 需求:展開時,mini-player的專輯圖片從原始位置飛入CD圖片位置,同時有一個放大縮小效果, 對應(yīng)頂部和底部的回彈;收起時,normal-player的CD圖片從原始位置直接落入mini-player的專輯圖片位置
- 實現(xiàn):Vue提供了javascript事件鉤子,在相關(guān)的鉤子中定義CSS3動畫即可
| 五、播放器歌曲播放功能實現(xiàn)--H5 audio |
- 添加H5 <audio>實現(xiàn)歌曲的播放 <audio :src="currentSong.url" ref="audio"></audio>
- 在watch中監(jiān)聽currentSong的變化,播放歌曲 watch: {currentSong() {this.$nextTick(() => { //確保DOM已存在this.$refs.audio.play()})} }
- 給按鈕添加點擊事件,控制播放暫停 <i class="icon-play" @click="togglePlaying"></i>
- 圖標(biāo)樣式隨播放暫停改變:動態(tài)綁定class屬性playIcon,替換掉原原來的icon-play <i :class="playIcon" @click="togglePlaying"></i> playIcon() {return this.playing ? 'icon-pause' : 'icon-play' }
- CD 旋轉(zhuǎn)動畫效果
| 六、播放器歌曲前進(jìn)后退功能實現(xiàn) |
- 給按鈕添加點擊事件 <i class="icon-prev" @click="prev"></i> <i class="icon-next" @click="next"></i>
-
通過mapGetters獲得currentIndex當(dāng)前歌曲index
- 通過mapMutations定義setCurrentIndex方法修改mutation setCurrentIndex: 'SET_CURRENT_INDEX'
- 定義prev()和next()修改mutation: 限制index邊界 next() {let index = this.currentIndex + 1if(index === this.playlist.length){index = 0}this.setCurrentIndex(index) }, prev() {let index = this.currentIndex - 1if(index === -1){index = this.playlist.length - 1}this.setCurrentIndex(index) }
- 坑:前進(jìn)或后退后會自動開始播放,但播放按鈕的樣式?jīng)]有改變
- 解決:添加判斷,如果當(dāng)前是暫停狀態(tài), 切換為播放 if(!this.playing){this.togglePlaying() }
- 坑:切換太快會出現(xiàn)報錯:Uncaught (in promise) DOMException: The play() request was interrupted by a new load request
- 原因:切換太快audio 數(shù)據(jù)還沒有加載好
- 解決:audio W3C文檔中記錄,audio有兩個事件:
在data中維護(hù)一個標(biāo)志位數(shù)據(jù)songReady,通過ready方法控制只有歌曲數(shù)據(jù)請求好后,才可以播放
data() {return {songReady: false} } ready() {this.songReady = true }在prev()、next()和togglePlaying中添加判斷,當(dāng)歌曲數(shù)據(jù)還沒有請求好的時候,不播放
if(!this.songReady){return }其中prev()和next()中歌曲發(fā)生改變了之后,重置songReady為false,便于下一次ready()
this.songReady = false- 坑:當(dāng)沒有網(wǎng)絡(luò),或切換歌曲的url有問題時,songReady就一直為false,所有播放的邏輯就執(zhí)行不了了
- 解決: error()中也使songReady為true,這樣既可以保證播放功能的正常使用,也可以保證快速點擊時不報錯
- 優(yōu)化: 給按鈕添加disable的樣式 <div class="icon i-left" :class="disableCls"> <div class="icon i-center" :class="disableCls"> <div class="icon i-right" :class="disableCls"> disableCls() {return this.songReady ? '' : 'disable' } &.disablecolor: $color-theme-d
| 七、播放器時間獲取 |
- data中維護(hù)currentTime當(dāng)前播放時間:currentTime: 0 (audio的可讀寫屬性)
- audio中監(jiān)聽時間更新事件: @timeupdate="updateTime"
- methods中定義updateTime()獲取當(dāng)前時間的時間戳,并封裝format函數(shù)格式化: //獲取播放時間 updateTime(e) {this.currentTime = e.target.currentTime //時間戳 }, format(interval){interval = interval | 0 //向下取整const minute = interval / 60 | 0const second = this._pad(interval % 60)return `${minute}:${second}` }
- 坑:秒一開始顯示個位只有一位數(shù)字,體驗不好
- 解決:定義_pad()用0補位 _pad(num, n = 2){ //用0補位,補2位字符串長度let len = num.toString().lengthwhile(len < n){num = '0' + numlen++}return num }
- 格式化后的數(shù)據(jù)填入DOM,顯示當(dāng)前播放時間和總時間: <span class="time time-l">{{format(currentTime)}}</span> <span class="time time-r">{{format(currentSong.duration)}}</span>
| 八、播放器progress-bar進(jìn)度條組件實現(xiàn) |
- base->progress-bar目錄下:創(chuàng)建progress-bar.vue
? ? ?? 需求:進(jìn)度條和小球隨著播放時間的變化而變化
- 實現(xiàn):
? ? ? ?需求:拖動進(jìn)度條控制歌曲播放進(jìn)度
- 實現(xiàn):
? ? ?? 需求:點擊進(jìn)度條任意位置,改變歌曲播放進(jìn)度
- 實現(xiàn):添加點擊事件,通過react.left計算得到偏移量,設(shè)置進(jìn)度條偏移,并派發(fā)事件改變歌曲播放時間 <div class="progress-bar" ref="progressBar" @click="progressClick"> progressClick(e) {const rect = this.$refs.progressBar.getBoundingClientRect()const offsetWidth = e.pageX - rect.leftthis._offset(offsetWidth)this._triggerPercent() }
| 九、播放器progress-circle圓形進(jìn)度條實現(xiàn) -- SVG |
- base->progress-circle目錄下:創(chuàng)建progress-circle.vue
- player.vue中使圓形進(jìn)度條包裹mini-player的播放按鈕,并傳入半徑和百分比: <progress-circle :radius="radius" :percent="percent"><!-- radius: 32 --><i :class="miniIcon" @click.stop.prevent="togglePlaying" class="icon-mini"></i> </progress-circle>
- progress-circle.vue中維護(hù)數(shù)據(jù)dashArray,并使用computed計算出當(dāng)前進(jìn)度對應(yīng)的偏移量: data() {return {dashArray: Math.PI * 100 //圓周長 描邊總長 } }, computed: {dashOffset() { return (1 - this.percent) * this.dashArray //描邊偏移量 } }
?注:項目來自慕課網(wǎng)
轉(zhuǎn)載于:https://www.cnblogs.com/ljq66/p/10167951.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的【音乐App】—— Vue-music 项目学习笔记:播放器内置组件开发(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第五章:条件、循环以及其他语句(上)
- 下一篇: ORACEL游标的使用实例