這章是播放器的重頭戲-------音樂播放器的設(shè)置,學(xué)到了很多
基礎(chǔ)組件,像songList這種被多次復(fù)用的組件,不寫邏輯代碼,它只派發(fā)事件?。?!
寫在前面:父組件向子組件傳遞的prop是可以實時變化的,子組件接受到的prop也是實時變化的,props是不會被子組件修改的。動畫結(jié)束之后記得清除!:class="{'clsname': xxx}" class綁定的不一定是一個對象,還可以是一個計算屬性返回值
1:
對播放器的樣式進行分析,先暫時跳過把
2:
vuex相關(guān),跳過
3:
播放器組件dom和基本樣式,跳過
4:
vue的動畫效果! create-keyframe-animation
在vue中使用動畫,在外層增加一個transition標(biāo)簽,一般定義兩個屬性,一個是enter-active(leave-active)和enter(leave-to),他們都是在元素消失或者出現(xiàn)這一過程的狀態(tài)。transition是過渡,定義對什么屬性進行過渡效果,時間為幾秒。(還可以自定義緩動曲線,即bezier曲線,控制在0.4s時間內(nèi)動畫的速度快慢)

這張圖即代表在normalplayer進入的時候,在0.4s時間內(nèi)漸顯,他的top向下移動100px,bottom向上移動100px
當(dāng)然,我們還不滿足這么簡單的動畫

像上圖,我們要實現(xiàn)miniPlayer的小圓圈飛向大的圓圈的動畫(沒有視頻。。大家發(fā)揮自己的想象力,233333)
相信大家第一個想到的都是用css里面的關(guān)鍵幀動畫@key-frame吧。具體的css我就不說了,大家可以自己去百度一下。如果用純css來寫 我們可能這樣寫

但是這里就涉及到一個問題了,你怎么知道大圓圈中心的位置是多少?在不同手機上,不同圓圈的中心點不同的呀。所以我們需要用js來生成css代碼,這里就要用到幾個關(guān)鍵知識:
1.create-keyframe-animation動畫庫
create-keyframe-animation就是一個通過js創(chuàng)建css動畫的庫,當(dāng)動畫的坐標(biāo)需要計算時,可以用到這個庫
2.vue中transition的js鉤子
注意:enter和leave中回調(diào)函數(shù)是必須的!!否則他們會被同步調(diào)用!
(兩項都建議大家去看官方文檔,挺簡單的)

我們?yōu)槊總€鉤子觸發(fā)一個函數(shù)
enter周期的時候注冊,記得結(jié)束時調(diào)用回調(diào)函數(shù),這個動畫的意思是一開始wrapper在左下角小圓圈的位置,60%的時候回到中間,100%的時候變回原形,中間有個彈性變小的過程

afterEnter的時候要清空動畫,并且取消注冊動畫

leave的動畫稍微簡單,不需要用到create-keyframe-animation

5:
歌曲播放功能實現(xiàn)
具體思路如下:
1.為audio標(biāo)簽制定src來源
2.調(diào)用audio.play方法播放
涉及到的問題和解決方法如下:
1.為src用v-bind綁定歌曲來源

2.什么時候開始播放?最好的時機是currentSong改變的時候,因為當(dāng)你點擊songList或者其它按鈕的時候,都是改變當(dāng)前的currentSong,所以我們就要watch currentSong。

但是這里會有一個bug當(dāng)你調(diào)用play的時候,dom還沒有ready渲染好,所以會報錯,我們需要在nextTick再去操作dom
實現(xiàn)上面幾步后,現(xiàn)在已經(jīng)可以播放歌曲了。那我們又遇到一個難題,怎么讓它停下來?
之前在vuex中設(shè)置過playing狀態(tài),當(dāng)它為true時,我們調(diào)用this.$refs.audio.play(),否則調(diào)用this.$refs.audio.pause()。但是緊接著又來了,你怎么設(shè)置playing狀態(tài)?當(dāng)然是通過暫停播放按鈕啦,給它添加一個click事件,調(diào)用mutation方法,同時要改變它的class,對了normalPlayer和miniPlayer是一樣的!



好啦,現(xiàn)在我們大功告成,已經(jīng)可以實現(xiàn)基本的播放暫停操作了。然后我們還要讓這個cd旋轉(zhuǎn)起來! 用到css的animation動畫,跟之前的keyframe是一樣滴,當(dāng)playing的時候給它加上play類名,反之則加上pause類名



6:
接下來我們來實現(xiàn)歌曲的前進后退功能。
實現(xiàn)該功能,本質(zhì)上是改變currentSong,在vuex中currentSong是通過playlist和currentIndex得到的(getter也能充當(dāng)計算屬性)

那么,我們給前進和后退按鈕各自綁定一個click事件,改變currentSong,注意控制邊界條件,如果是上一首歌是暫停,點擊下一首歌默認(rèn)播放。(if(!this.songReady) return}這句話是干嘛的等會會講)


好啦,現(xiàn)在是真ojbk了。你的播放器現(xiàn)在有大概的雛形了,但是身為一個有節(jié)操的程序員,程序中發(fā)現(xiàn)了錯誤就要及時修改喲,比如當(dāng)你快速切換下一首歌的時候,dom有時候還沒渲染好會出現(xiàn)這樣一個錯誤

我們當(dāng)然要fix這個錯誤,audio標(biāo)簽在準(zhǔn)備好播放的時候,會觸發(fā)一個canplay事件,出錯的時候會觸發(fā)error事件(具體事件請看文章最下方),我們對這兩個事件進行操作。
添加一個songReady標(biāo)志位,默認(rèn)為false,在ready的時候置為true

再結(jié)合上面的next函數(shù)看看,你是不是就理解了呢?
7:
接下來讓我們來試試修改當(dāng)前時間吧!
audio標(biāo)簽播放的時候會實時觸發(fā)一個很重要的事件timeupdate,一般控制歌曲播放都是靠它。e.target是audio標(biāo)簽。而e.target.currentTime是指當(dāng)前播放的時間,它是可讀寫的,你可以直接修改它。
這個事件觸發(fā)的時候我們給它設(shè)置一個函數(shù),在這個函數(shù)中我們獲取兩個東西:currentTime和totalTime。因為這兩個都是時間戳,所以還需要定義一個format函數(shù)格式化我們得到的時間戳



8:
進度條組件百分比播放實現(xiàn)
思考一下,這樣的功能要怎么實現(xiàn)呢? 我們可以通過currentTime / totalTime 獲得一個百分比,然后讓進度條的寬度隨著這個百分比的增大而增大
我們來寫一個progressBar組件,它接受一個percent props,progress的長度就是經(jīng)過了多少時間,progressBtn就是那個小圓頭,BallWidth就是小圓球的寬度。這里要注意這篇文章開頭寫的一句話:props是實時改變的。 我們來watch props的變化


9:
進度條拖拽功能實現(xiàn)
因為audio.duration是一個可改的變量。你在子組件拖動進度條的時候,不止要改變progress的寬度,同時還會有一個新的percent,再touchend的時候你傳到父組件去修改audio.duration。點擊進度條的時候如果用的是click事件,獲得偏移量要通過e.offsetX。如果是touchstart事件就通過e.touches[0].pageX獲取


這里還會有一個小bug,如果你不加this.touch.isTouch這個標(biāo)志位的話,你在滑動的時候,percent也在改變,這時候還會調(diào)用watch里面的方法設(shè)置offsetWidth。所以我們增加一個標(biāo)志位來判斷進度條長度的變化是不是由拖拽產(chǎn)生的。
10:
圓形進度條的實現(xiàn)(svg不會。。跳過。。)
11:
設(shè)置歌曲播放模式,需要注意的點是nextMode = (this.mode+1) %3。注意這個的打亂算法,改變歌曲順序的時候注意this.currentSong是靠this.currentIndex和this.playlist獲得的,如果只是打亂playlist的話,currentSong就會變化,所以要同時改變playlist和currentIndex



12:
完成剩下的進度條功能,拉到最后的時候拉到下一首歌,需要觸發(fā)ended事件,如果是loop模式,在播放完之后要讓audio.currentTime = 0

回到musicList組件,完成隨機播放全部的按鈕,點擊這個之后,應(yīng)該改變vuex的mode,playing,觸發(fā)的mutation有點多,就把它封裝到action里面去


但是還會有一個bug,當(dāng)你是隨機播放的時候,點擊music-list里面的歌,它彈出來的跟你的預(yù)料的不同
為什么呢? 是因為我們點擊music-list的時候,會調(diào)用我們之前寫的action,然后我踩了一個坑。。。
這個坑以后一定要注意了?。。?/h1>
在shuffle函數(shù)里面,我設(shè)置了newArr = arr 這是一個淺拷貝,是地址引用,這樣你對newArr做修改 arr也會改變,所以在調(diào)用之前的action的時候,傳入的shuffle過的數(shù)組!?。?!同時,你點擊隨機播放全部的時候,player組件里面對mode的watch也會生效,就會再次mutation一個playlist
shuffle
之前寫的action


audio標(biāo)簽事件如下:
