《QQ音乐小电台》主要分享在开发过程中核心功能实现和踩过的坑,希望对开发音频播放的同学有所帮助。
作者:任洋--腾讯web前端助理工程师
@IMWeb前端社区
QQ音乐电台小程序的核心功能
- 开启电台
- 好友卡片
- 引导页(引导用户用微信登录QQ音乐或开启冷启动)
- 冷启动
- 卡片详情(好友相似度,好友偏好,评论)
- 歌曲播放页(播放暂停,歌词滚动,收藏歌曲,切换歌曲,听歌流水上报,背景魔法色,适配)
- miniplayer (切换歌曲,状态同步)
核心功能实现
音频状态同步
涉及播放歌曲状态同步,不能使用audio组件。而音频播放API本质上是借助微信native的播放组件。
使用wx.navigateTo() 跳转到应用内的某个页面,会保留当前页面。点击左上角返回,之前页面会触发onShow监听页面显示,不会触发onLoad事件。播放页和首页miniplayer状态同步相关逻辑处理应该在onShow事件监听。
歌曲信息以及变更(包括歌曲列表,播放状态,切换音频,专辑图,歌曲名,歌手等)存储在小程序提供的storage下,方便不同页面数据同步
歌词滚动
音频组件API目前没有提供类似audio的onTimeUpdate事件,需要开了一个定时器做歌词滚动,缺点是定时器做歌词渲染有不太精准。好消息是:微信后期会支持OnTimeUpdate事件。 歌词处理相关逻辑如下:
歌词背景魔法色
根据专辑图拉取对应十六进制的魔法色。有些色值较亮,有点刺眼,这里需要对色值转为HSL通过降低饱和度S和亮度L来使得背景色看着柔和。
将后台返回十六进制,转为RGB值
RGB转为HSL
降低HSL 中S饱和度,L亮度让背景色不刺眼
将降低后HSL转为RGB
降低饱和度和亮度之后的效果图,背景变柔和
异常处理
image组件图片数据没有返回或图片加载失败异常处理
弱网络环境下cgi加载慢或cgi异常菊花提示,加载成功后隐藏菊花,wx.showToast最大延迟时间是10000
对网络异常或cgi逻辑处理失败做友好提示,且对cgi成功率做上报。
前端异常上报,当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并返回错误信息,获取设备信息,方便后期定位问题
音乐播放控制bug&fix
安卓下,暂停不能继续播放的问题
原因是暂停再播放时,微信旧版本安卓上会检测playBackgroundAudio的title和coverImgUrl参数,后来fix这个bug,为了兼容之前版本,还是将参数传入。
IOS、安卓播放暂停切歌
IOS,安卓机下播放过程中先暂停在切换歌曲,发现播放的歌曲为原先的歌曲。
解决方法:暂停场景下更换音频地址wx.playBackgroundAudio({dataUrl:’xxx’})之前需要调用wx.stopBackgroundAudio。
带来问题:音频播放完成以及播放音频文件有误403或500都会触发wx.onBackgroundAudioStop事件。好消息是微信之后会对播放音频API进行大的改动,用不同事件分别触发停止播放,播放结束,播放错误。
安卓同步播放状态
安卓机下播放一首歌曲且同时打开新页面(播放页),同步上一页面播放态,wx.getBackgroundAudioPlayerState在有歌曲播放的情况下status返回为2,且状态返回dataUrl和播放url对不上。(wx.getBackgroundAudioPlayerState的播放有三个状态:1是播放中,0是暂停,2是没有音乐播放。2状态是微信小程序后期加的。)
解决方法:将播放链接存在storage里面,获取storage链接作为播放链接。
小程序基础学习
小程序官方文档很详细,下面是对小程序一些关键知识提炼。
mina框架
js:逻辑层的开发
WXML:页面布局
WXSS:页面的样式
整个系统分为两块视图层(View)和逻辑层(App Service)。
页面js中,data数据是需要约定为只读。MINA是单向数据绑定,修改data中的数据不会自动更新View;更新view,需要使用setData()方法。setData()更新View时,与data中的数据进行Diff比较,不同才会更新。如果直接修改data,很容易造成data中的数据与View不一致。setData单次设置的数据不能超过1024kB,需要避免一次设置过多的数据。
每个小程序分为两个线程,view和appServer。其中view线程负责解析渲染页面(wxml和wxss),而appServer线程负责运行js。appServer线程运行在jsCore中(安卓下运行在X5中,开发工具中运行在nwjs中),所以js不跑在webview里,不能直接操纵DOM和BOM,这就是为什么小程序没有window全局变量。
目录结构(小程序包含一个描述整体程序的 app 和多个描述各自页面的 page) 小程序的框架程序包含一个描述整体程序的app 和多个描述页面的page。其中,app由三个文件构成,公共设置的app.json 、公共样式的app.wxss、主体逻辑的app.js 。每个page由四部分组成,页面设置page.json、页面文件page.wxml、页面样式page.wxss、页面主体逻辑page.js。
逻辑层
App()
函数用来注册一个小程序。接受一个 object 参数,其指定小程序的生命周期函数等
object参数说明:
Page object 参数说明:
一个page的生命周期从onLoad开始,整个生命周期内onLoad、onReady、onUnload这三个事件仅执行一次,而onHide和onShow在每次页面隐藏和显示时都会触发,执行顺序是onLoad,onShow,onReady。当用户手动触发左上角的退出箭头时,小程序仅触发app.onHide,下次进入小程序时会触发app.onShow以及当前page.onShow。仅当小程序在后台运行超过一定时间未被唤起、或者用户手动在小程序的控制栏里点击退出程序、或者小程序内存占用过大被关闭时,小程序被销毁,会触发app.onUnload事件。
一个完整的小程序执行的生命周期如下
模块化
将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过module.exports
或者 exports
才能对外暴露接口。在需要使用这些模块的文件中,使用 require(path)
将公共代码引入。框架对各个js的模块化,你编写的代码,执行之前会帮你AMD化处理
视图层
1、字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
2、保留关键字 *this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字
3、数据绑定
WXML 中的动态数据均来自对应 Page 的 data,数据绑定使用 Mustache 语法(双大括号)将变量包起来。支持组件属性,控制属性,关键字。支持多种运算如:三目运算,算数运算,字符串运算,逻辑判断
4、条件渲染
wx:if
有更高的切换消耗而 hidden
有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden
更好,如果在运行时条件不大可能改变则 wx:if
较好。
5、列表渲染
wx:key 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
wx:key
的值以两种形式提供
6、模版
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用
7、事件
key 以bind
或catch
开头,然后跟上事件的类型,如bindtap
, catchtouchstart
bind
事件绑定不会阻止冒泡事件向上冒泡,catch
事件绑定可以阻止冒泡事件向上冒泡
8、引用
WXML 提供两种文件引用方式import
和include
。区别在于:import可以引入定义好的template模板,模板是有作用域的;而include就是拷贝一个公用的代码片段到目标文件中,适合做公共页面片的拆分
WXSS
1、WXSS新引入了一个rpx的概念来做自适应布局。rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在iPhone6上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
2、本地资源无法通过WXSS获取,所以WXSS中的样式都是用的网络图片,或者base64
api
wx.request
wx.request
发起的是 HTTPS 请求。
data 数据说明 最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String 。转换规则如下
content-type 默认为 ‘application/json’
要注意 method 的 value 必须为大写
request 的最大并发数是 5
网络请求的 referer 是不可以设置的,格式固定为https://servicewechat.com/{appid}/{version}/page-frame.html
,其中 {appid}
为小程序的 appid,{version}
为小程序的版本号,版本号为 0 表示为开发版。
request 的默认超时时间和最大超时时间都是 60s
通讯域名配置:小程序可以跟指定的域名进行网络通信
1、对于 header[‘content-type’] 为 ‘application/json’ 的数据,会对数据进行 JSON 序列化
2、对于 header[‘content-type’] 为 ‘application/x-www-form-urlencoded’ 的数据,会将数据转换成 query string (encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…)
音乐播放
wx.getBackgroundAduioPlayerState 获取后台音乐播放状态,(播放状态同步和歌词渲染)
wx.playBackrgoundAudio 使用后台播放器播放音乐,对于微信客户端来说,只能同时有一个后台音乐在播放。当用户离开小程序后,音乐将暂停播放;当用户点击“显示在聊天顶部”时,音乐不会暂停播放;当用户在其他小程序占用了音乐播放器,原有小程序内的音乐将停止播放。
wx.pauseBackgroundAudio() 暂停播放音乐
wx.seekBackgroundAudio()控制音乐播放进度
1、wx.stopBackgroundAudio() 停止播放音乐,会触发wx.onBackgroundAudioStop()
2、wx.onBackgroundAudioStop() 监听音乐停止
数据缓存
微信小程序没有Cookie、sessionStorage和localStorage,而是提供了一个app的本地存储,对存储的操作可以异步、同步的增删改查。
设备(用在手机适配,异常错误上报获取设备信息,以及不同微信版本做兼容)提供异步和同步二种方式wx.getSystemInfo() || wx.getSystemInfoSync()
success回调参数说明:
界面
交互反馈
wx.showToast() wx.hideToast(
wx.showModal()
1、wx.navigateTo() 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack
可以返回到原页面
2、wx.redirectTo 关闭当前页面,跳转到应用内的某个页面。
一个应用同时只能打开5个页面,当已经打开了5个页面之后,wx.navigateTo
不能正常打开新页面。请避免多层级的交互方式,或者使wx.redirectTo
开发接口
1、wx.login 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)。用户数据的加解密通讯需要依赖会话密钥完成。
2、wx.getUserInfo 获取用户信息,需要先调用 wx.login 接口。
3、分享图片不能自定义;会取当前页面,从顶部开始,高度为 80% 屏幕宽度的图像作为分享图片。
扫码下方二维码,
随时关注更多前端干货文章!
▼
微信:IMWebTech