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

时间:2020-04-18 13:40:56   收藏:0   阅读:90

为了提高自己,最近在学习微信小程序,选题是仿网易云音乐。期间踩过了大把的坑,bug出现的难受和解决bug欢喜,一直是伴随我阶段性学习这个项目的心情。初步完成了项目的主要功能,来分享一下自己的心路历程。

实现功能

  • 列表式渲染数据
  • 图片轮播
  • 传参页面跳转
  • 按钮点击弹窗
  • 音乐播放、暂停、上一曲、下一曲
  • 选歌播放
  • 嵌套template页及传递数据

项目展示

  • 首屏

技术图片
(图一)

  • 页面的切换

技术图片
(图二)

  • 播放效果

技术图片
(图三)

  • 暂停、上一首、下一首

技术图片
(图四)

  • 弹窗及选歌

技术图片
(图五)

项目实现

项目思想

*

小程序的开发模式是MVVM模式。简单的讲就是页面绑定的值改变,页面就发生改变;页面改变,页面的绑定的值改变。这样的话,列表式渲染就十分好用。只用写一个通用的骨架,然后使用列表渲染生成页面,这样非常省时间省力气。所以这个项目,大量充斥着列表渲染。

有的页面部分可能会出现在好几个页面上。在这个项目里,页面顶部的部分出现在多个部分中,同时小程序里很多地方需要用到**弹性布局居中效果**于是我便把常用css样式写在app.wxss中,这样可以复用。

页面右上角的四根竖线出现在多个页面中,那变可以单独拿出来写,然后在通过template,导入至不同的页面中去。

选歌很需要解决的一点就是怎么才知道选择的是哪一首歌,歌单是哪一个歌单。一个页面获取到其他页面想传递过来的数据的方式有很多。其一、可以通过url跳转的时候,传参跳转,再通过跳转页面中onload事件添加options参数获得。其二、可以通过点击事件改变全局属性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取得链接时,就自动开始播放。

  • 播放状态。

    歌曲播放时页面动起来,歌曲未播放时静止

技术图片

看见动画了,第一个反应是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来砸我吧。

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

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!