最近准備把之前做的一個仿網易雲音樂的自制音樂網頁播放器項目做一個總結。
相關功能如下:
-
通過后台頁面上傳歌曲、編輯歌曲功能。
-
前端頁面自動更新播放熱度高的歌曲
-
在線聽歌、查看歌詞。且配有相應的播放動畫。
預覽鏈接:https://leonardo-zyh.github.io/163-music-demo/src/index.html
可通過微信二維碼打開:
該項目主要是使用了jQuery以及MVC模塊化的思想來完成的移動端音樂會播放器,因此在介紹這個應用的制作思路和流程之前,我想重新總結一下對模塊化和MVC的理解。
模塊化
我的認識中模塊化是通過MVC的V,也就是View來划分的,把頁面中看得見的區域進行功能划分,每一個功能不同的區域就是一個分開的模塊,
window.eventHub={ events:{}, emit(eventName,data){ for(let key in this.events){ if (key===eventName){ let fnList=this.events[key] fnList.map((fn)=>{ fn.call(undefined,data) }) } } }, on(eventName,fn){ if (this.events[eventName]===undefined){ this.events[eventName]=[] } this.events[eventName].push(fn) } }
MVC模式
MVC模式(Model-View-Controller)是軟件工程中的一種軟件架構模式,把軟件系統分為三個基本部分:
模型(Model)、視圖(View)和控制器(Controller)
- 模型(Model)
模型層:數據保存,可以簡單理解就是數據層,用於提供數據。在項目中,(簡單理解)一般把數據訪問和操作,比如將對象關系映射這樣的代碼作為Model層,也就是對數據庫的操作這一些列的代碼作為Model層。比如代碼中我們會寫DAO和DTO類型的代碼,那這個DAO和DTO我們可以理解為是屬於Model層的代碼。- 視圖(View)
視圖層:用戶界面,就是UI界面,用於跟用戶進行交互。一般所有的JSP、Html等頁面就是View層。- 控制器(Controller)
控制層:業務邏輯,Controller層的功能就是將Model和View層進行關聯。比如View主要是顯示數據的,但是數據又需要Model去訪問,這樣的話,View會先告訴Controller,然后Controller再告訴Model,Model請求完數據之后,再告訴View。這樣View就可以顯示數據了
render()
函數來更新功能區的HTML代碼,使頁面視圖發生相對應的改變。總的來說,View要做的事情,就是改變網頁的用戶視覺,去把代碼改變的內容以直觀的方式呈現在網頁中,需要切記的是每個MCV模塊中View所代表的區域之間是不能互相交叉的,因此在進行模塊化時要明確好每個模塊的職責。
let view={ el:'#用戶界面', template:` <div>HTML模版</div>
`, init() { this.$el = $(this.el) }, render(){ $(this.el).html(this.template) } }
let model={ data:{}, fetch(){......}, save(){......}, update(){......} }
Controller作為業務邏輯,Controller層的功能就是將Model和View層進行關聯。Controller代表的是控制當前模塊在不同的時刻所進行的操作,比如,Controller對象里一般都會有init方法、bindEvents方法和bindEventHub方法。init方法意思就是在模塊初始化的時候,需要做些什么,因此我們會在init方法里面初始化view、初始化model,進行事件綁定,進行事件發布訂閱中心的事件訂閱等等,這些就是我們在模塊初始化時要做的事情。然后在元素觸發事件時模塊需要做什么,在其他模塊發布事件后模塊需要做什么,都分別反映在了Controller的bindEvents方法和bindEventHub方法中,因此Controller就像一個控制塔,有條不紊地在合適的時候處理着合適的事情,是統籌Model和View的中心。
let controller={ init(view,model){ this.view=view this.model=model this.bindEvents() this.bindEventHub() }, bindEvents(){......}, bindEventHub(){......} }
相信在你看完以上我對模塊化和MVC的項目總結之后,會幫助你更好地梳理接下來我要介紹的音樂播放器的思路。因為這個應用涉及的代碼很多,所以我只能介紹重要的思路,以及會在最后說一下在制作過程中遇到的幾個坑、問題以及解決這個問題的思路的做法。
項目的制作思路
當你有了制作某個項目的想法,你第一件要做的事情是什么,不是直接寫代碼,而是分析這個項目,我們可以通過以下三個圖例來進行分析:
1.用例圖(use cases)
分析當你身為用戶或者應用管理員的時候,使用應用的時候需要什么的頁面,頁面需要怎么樣的功能,這就是用例圖會表達出來的內容。
例如音樂播放器這個應用,身為普通用戶的話,我們可以查看首頁、查看歌單頁和歌曲頁,歌曲頁里面可以聽歌、可以暫停以及可以查看歌詞,我們還可以搜歌,可以搜歌來搜出歌手和歌曲名等等。
通過這些分析,你就會了解到你當前要制作的這個應用,他需要怎么樣的功能以及每個功能應該出現在哪一個頁面當中。
2.線框圖(也叫草圖,stretch)
線框圖要展示的,就是你要制作的應用中,每個頁面功能區的布局,也就是線框圖會告訴你這個應用含有多少個頁面,每個頁面里有着哪些功能區,以及功能區的大體位置也能在上面體現出來。
3.系統架構圖
系統架構圖展示的是在該應用中,前端頁面、后端頁面以及數據庫中使用的是什么工具,例如音樂播放器中,前端頁面使用的是jQuery,后端頁面使用的是LeanCloud提供的API,數據庫使用的是 LeanCloud和七牛,以及這三者之前的交互方式,例如前端頁面和后端頁面的交互方式是通過AJAX來進行的。

關於音樂播放器的樣式問題,這是需要自己去花時間去尋找優秀的設計模板,並進寫模仿、修改和編寫才能得到的內容,因此就不在這里進行闡述了。


關於項目中所遇到的問題
在進行音樂作品播放器的制作過程中,我在一些地方使用了ES6的新語法展開語法
...
,這個語法的表示的是當前對象的所有屬性,如:
let attributes={name:"xzb",age:18} let obj={id:1,...attributes} console.log(obj) // {id:1,name:"xzb",age:18}
這個語法在是【使用中很方便,但是當你在移動端使用它的時候,很容易出現語法錯誤,移動端不支持
后面只能使用Object.assign方法來代替它了。
解決辦法:使用Object.assign()
2.無法對移動端進行調試
第二個坑是由第一個坑衍生出來的,在發現第一個坑之后,我的第一個反應是,十分無奈,為什么這么說呢?哥,你在PC端出錯,我還能通過控制台來看看出錯的地方在哪,你在移動端出錯,我.....
好吧,只能靠萬能的互聯網了,在一番資料的查詢之后,我得到了想要的解決辦法,有以下四個:
(1)通過alert()來進行檢驗
雖然說移動端沒有控制台,但移動端還是可以alert的吖,因此我們只要在我們認為出錯的地方的前后進行alert(),若發現前面的alert運行了,后面的alert沒有運行,那么恭喜你,你的猜測是正確的,出錯的地方就是此處。通過這種辦法我們就可以在移動端知道自己出錯的地方了。
(2)通過全局的onerror來進行檢驗
通過第一種方法我們的確可以在不斷的嘗試下知道出錯的地方,但是這樣效率太低下了,於是我們有第二種辦法,通過監聽全局的error來顯示錯誤,以及顯示錯誤的出處。代碼如下:
<script> window.onerror=function(message,file,row){ alert(message,file,row) } </script>
onerror接受四個參數,第一個是出錯信息,第二個出錯文件,第三個是出錯的行數,第四個是出錯的列數,由於列數在此處對我沒什么太大的作用,因此在上述代碼中我把它省略了。
(3)自己手寫一個console函數法
自己在頁面上寫一塊console的區域,然后把console的值直接顯示在區域內即可,具體代碼如下:
<div id="consoleOutput" style="......"></div>
<script> window.console={ log(x){ let p=document.createElement('p') p.innerText=x consoleOutput.appendChild('p') } } </script>
(4)直接引入騰訊制作的vConsole庫
直接引入騰訊制作的vConsole庫,就可以在移動端擁有一個console了,但是需要記住在調試完之后記得刪掉這些調試工具,以免出現在用戶使用的頁面中。
注意:http-server局域網調試,建議關閉防火牆
3.由於IOS的移動端不支持animation-play-state語句而導致的BUG。(注:IOS12已修復)
在我對移動端中的所有報錯都進行了修復之后,我在歌曲播放頁面又發現了一個新的BUG,那就是在點擊光盤進行播放的時候,光盤沒有如我代碼所寫那樣進行播放,百思不得其解之下,我發現網上有許多人和我出現了類似的BUG,於是乎我就去尋找出現這種現象的原因,以及解決的辦法,最后我發現原來是IOS不支持animation-play-state語句而導致的,而在安卓的移動端上則不會出現這樣的BUG。
如何是好,沒有了animation-play-state,我就無法去控制CSS3動畫的播放和暫停,如果單純的用animation:none;
來控制,就會出現動畫每次都重頭開始進行的錯誤效果,后來我也嘗試了animation-fill-mode: forwards;
來嘗試讓動畫每次停在最后的狀態,但由於歌曲的暫停是隨機的,而不是由動畫是否播放完畢來決定是否暫停的,因此這個方法也是行不通的,怎么辦好。我不斷的嘗試,不斷地搜索資料,最后我看到了一個網上的解決方案:給光盤所在元素添加一個父元素,當每次點擊光盤暫停歌曲播放時,用父元素記錄一下光盤每次暫停時transform的值,並讓父元素的transform也等於這個值,若transform本來就有值,那就在transform后面更新這個值,就可以完成歌曲暫停,光盤動畫暫停,歌曲繼續開始,光盤動畫繼續開始的效果。
具體的實現代碼如下:
recordTransform(){ let coverTransform=this.view.$el.find('.coverWrapper').css('transform') let coverWrapperTransform=this.view.$el.find('.coverWrapperParent').css('transform') let transformDeg=coverWrapperTransform==='none'?coverTransform:coverTransform.concat(' ',coverWrapperTransform) this.view.$el.find('.coverWrapperParent').css('transform',transformDeg) }
4.由於IOS微信不支持webp格式圖片所引起的BUG
由於我的音樂播放器中的歌單用的是webp文件的封面圖片,這個格式的圖片是谷歌開發的一種旨在加快圖片加載速度的圖片格式。WebP 的優勢體現在它具有更優的圖像數據壓縮算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的圖像質量;同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG 和 PNG 上的轉化效果都相當優秀、穩定和統一。
可惜IOS上的微信就是不支持這種格式的圖片,因此沒有辦法,我最后解決這個BUG的方案就是把webp圖片全部轉換成了JPG然后重新上傳至應用中。
解決辦法:更換應用圖片格式
終於算是介紹完了如何仿照網易雲音樂自制一個音樂網頁播放器,當我做完這個應用的時候,我覺得自己對MVC的理解和使用都更為熟練了,接下來我會完成Vue相關的應用,並繼續給大家帶來完成后的心得和感想,希望能對你們有所幫助~