指尖一點歌聲來--微信小程序之仿網易雲音樂心得

爲了提升本身,最近在學習微信小程序,選題是仿網易雲音樂。期間踩過了大把的坑,bug出現的難受和解決bug歡喜,一直是伴隨我階段性學習這個項目的心情。初步完成了項目的主要功能,來分享一下本身的心路歷程。css

實現功能

  • 列表式渲染數據
  • 圖片輪播
  • 傳參頁面跳轉
  • 按鈕點擊彈窗
  • 音樂播放、暫停、上一曲、下一曲
  • 選歌播放
  • 嵌套template頁及傳遞數據

項目展現

  • 首屏


(圖一)html

  • 頁面的切換


(圖二)git

  • 播放效果


(圖三)github

  • 暫停、上一首、下一首


(圖四)小程序

  • 彈窗及選歌


(圖五)微信小程序

項目實現

項目思想

*api

小程序的開發模式是MVVM模式。簡單的講就是頁面綁定的值改變,頁面就發生改變;頁面改變,頁面的綁定的值改變。這樣的話,列表式渲染就十分好用。只用寫一個通用的骨架,而後使用列表渲染生成頁面,這樣很是省時間省力氣。因此這個項目,大量充斥着列表渲染。

有的頁面部分可能會出如今好幾個頁面上。在這個項目裏,頁面頂部的部分出如今多個部分中,同時小程序裏不少地方須要用到**彈性佈局居中效果**因而我便把經常使用css樣式寫在app.wxss中,這樣能夠複用。

頁面右上角的四根豎線出如今多個頁面中,那變能夠單獨拿出來寫,而後在經過template,導入至不一樣的頁面中去。

選歌很須要解決的一點就是怎麼才知道選擇的是哪一首歌,歌單是哪個歌單。一個頁面獲取到其餘頁面想傳遞過來的數據的方式有不少。其1、能夠經過url跳轉的時候,傳參跳轉,再經過跳轉頁面中onload事件添加options參數得到。其2、能夠經過點擊事件改變全局屬性globalData的某項的值,而後再跳轉頁面中獲得globalData中相應的值,便能知道頁面跳轉中想傳遞的值。這個項目使用了這兩種方式,url傳參獲取播放第幾首歌曲,全局屬性globalData傳遞歌單。

項目資源

  • 後臺數據使用的是Easy Mock假數據,一個方便的數據構建平臺。
    固然還有方便的weui框架了,這個框架能夠去github上拷貝,而後再根據weui示例,找到想要的想過,再去下載好了的文件裏找到對應的代碼,試一試便知道要如何使用了。
    圖片和音樂的話,由於使用的是網絡地址。若是直接從網站上,按F12找到資源而後提取的話,一般提取的資源過幾天便會失效。因此咱們須要百度音樂外鏈、圖片外鏈,而後會有相應的網站,能夠生成一個長期不失效的連接,供咱們下載圖片和音樂。

踩過的坑與爬坑路

坑爹的圖片問題:

  • 項目起步第一個問題即是圖片存放與加載。一些須要推送的消息以圖片的形式展現,那麼這個圖是千萬不能存在本地,須要經過src連接網絡地址而後下載顯示。那麼問題來了:首頁結構已經出來,圖片卻要延遲出來。頁面則會出現這種狀況:

  • 通過思考,發現首頁圖片輪播條有不少張圖片,他們會同時加載。而需求最急的是顯示好第一張圖片,第一張圖片加載完成後,再加載其餘圖片。這樣能減小頁面首加載中,圖片加載的將近一半的網速佔用。查文檔發現,小程序中圖片加載完成後,有一個加載完成事件bindload
    由於使用的是列表渲染來填充頁面,因此即可以讓綁定渲染圖片的數據showImage起始爲空,這樣頁面就先不會渲染,而後第一張圖加載完後,再給showImage賦值,而後就會自動渲染頁面,加載其餘圖片了。這樣就能提升頁面的加載速度。
    (備註:這裏有些像懶加載的味道,在用戶須要的時候加載圖片)


這裏有一個小小的技巧,第一張圖的swiper-item不參與頁面渲染,這樣第一張圖就必定會進行加載。在第一張圖加載完成後,再進行頁面渲染。同時讓swiper開始輪播效果和生成指示點。微信

播放頁面動畫和還原

  • 音頻的後臺播放

在播放界面時,一開始使用的<audio>組件,洋洋灑灑敲下了一連串的代碼,播放音樂,一切正常。但當咱們前往另外一個頁面時,播放的音樂就沒聲音了。查了文檔才知道audio並不能實現後臺播放,實現後臺播放的是wx.getBackgroundAudioPlayerState()wx.getBackgroundAudioManager()兩個api。前者在微信客戶端1.2.0版本就不開始維護了,後者低版本需作兼容處理。這個項目我使用的是第二個api。
(備註:wx.getBackgroundAudioManager()官方文檔

值得注意的是,當音頻對象的src取得連接時,就自動開始播放。網絡

  • 播放狀態。

    歌曲播放時頁面動起來,歌曲未播放時靜止app

看見動畫了,第一個反應是css的animation,後來通過思考,若是不進行dom操做就要控制動畫的進行與否須要作的數據綁定就會不少,不方便使用。因而查文檔發現`wx.createAnimation(OBJECT)`這個api,但使用這個api,若是須要作到動畫的循環播放,要寫的js也不少很麻煩。通過了各類踩坑,根據小程序中的`progress`進度條組件,發現一個很棒的方法,那就是行內樣式綁定數據。

(備註:指針動畫簡單,進行一次就結束,這裏不提。。)




(備註:進度條是小程序自帶的組件,已播放時間完成方式和進度條類似,這裏與動畫效果一併提出)

這樣,只用在播放時,設計計時器,定時的按比列更改rotata、left、progress、的值,頁面就會有相關改變。這個函數中,一開始即是清除計時器,由於有一個坑點:點擊下一曲或上一曲時,計時器並未清除,那麼,便會有兩個計時器同時做用於一個值得改變(這種頭疼的狀況相信不少人都遇到過)。把三個計時器(轉盤、按鈕、進度條)在函數運行的開始就作清除,這樣就讓每次調用函數時,都先清除上一層的計時器,作到只有一個計時器做用於一個值。這種方式,用少許的代碼,造成了可控的動畫效果,非常方便。

(我在這收穫了另外一種寫動畫效果的方式,哈哈)
next: function () {
        // 全局定義了proSet rotSet timeSet,由於須要清除計時器
        var timeCount;
        clearInterval(proSet);
        clearInterval(rotSet);
        clearInterval(timeSet);
        proSet = setInterval(() => {
            if (this.data.progress >= 100) {
                i++;
                if(i==this.data.music.length){
                    i=0;
                }
                app.globalData.i=i;
                backgroundAudioManager.stop();                            
                this.nameBackMusic();
                timeCount = 0;
                this.setData({
                    progress: 0,
                    left: 0,
                    songTime: this.data.music[i].songTime,
                    songer: this.data.music[i].songer,
                    songName: this.data.music[i].songName,
                    time: secondToDate(this.data.music[i].songTime)
                });
            }
            else if (this.data.run == 0) {
                clearInterval(proSet);
                clearInterval(rotSet);
                clearInterval(timeSet);
            }
            this.setData({
                progress: 0.01 + this.data.progress,
                left: 0.0458 + this.data.left
            });
        }, this.data.music[i].songTime / 10);
        rotSet = setInterval(() => {
            this.setData({
                rotate: 1 + this.data.rotate,
            });
        }, 24);
        timeCount = backgroundAudioManager.currentTime;
        if (typeof (backgroundAudioManager.currentTime) === "undefined")
        {
            timeCount = 0;
        }
        timeSet = setInterval(()=>{
            timeCount++;
            this.setData({
                currentTime: secondToDate(timeCount), 
                //secondToDate用來把n秒轉換爲xx:xx的顯示形式
            });
        },1000)
    }
  • 頁面還原

在播放界面中,設計一個值run初始值爲0,用來記錄是否播放,播放時run爲1,暫停時run爲0。在頁面跳轉在返回播放頁面時,以前由於播放設置的data值會所有還原,致使頁面靜態顯示(進度條不動、時間不增長等)。這個時候,全局屬性globalData就上場了。不管是在選歌、播放、暫停的時候,globalData中的變量記錄播放的狀態(是否播放、播放哪一首歌等)。而播放頁面從新打開會執行onShow()生命週期函數,這個時候變能夠從全局變量中獲得播放的狀態,而後決定播放頁面是否要動起來和相應的數據)。


由於頁面第一次加載也會執行onShow()函數,而暫停音樂時backgroundAudioManager.paused返回true,播放時放回fals,沒有音樂播放時返回undefined,若是單純的用:if(backgroundAudioManager.paused)則會判斷無音樂和音樂播放時都爲假,這樣兩種不一樣的狀況執行相同的操做,則會發生意外,因此須要添加這樣的判斷
if (typeof (backgroundAudioManager.paused) !== "undefined") 用以區分播放和無音樂事件。

結語

一路學習過來,期間碰到的大大小小的問題數不勝數,收穫很大。目前還有一些功能暫未實現,會在之後繼續完善項目,繼續學習。

這個項目給我最大的啓示就是文檔是一個好東西,鍛鍊看文檔的能力會本身接受新東西的速度變快。再就是不少時候解決問題的方法多種多樣,寫代碼時能夠多作幾回考慮用哪一種方式實現一個功能,這樣既讓項目變得更高校,也讓本身變得更優秀。

我的郵箱:QiuShuiZC@163.com

github地址:秋水白

wx: zcfusheng

你們能夠一塊兒交流學習,若是以爲這個項目不錯的話,用star來砸我吧。

(備註:我給項目裏放的音樂都是周董和姿媽的,哈哈,畢竟男杰倫,女燕姿)

相關文章
相關標籤/搜索