指尖一点歌声来--微信小程序之仿网易云音乐心得

为了提升本身,最近在学习微信小程序,选题是仿网易云音乐。期间踩过了大把的坑,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来砸我吧。

(备注:我给项目里放的音乐都是周董和姿妈的,哈哈,毕竟男杰伦,女燕姿)

相关文章
相关标签/搜索