1. 开发前准备
1.1 项目展示
1.2 项目分析
1.页面结构
从图看出,小程序分为三个区域。分别将他们命名为tab,content,player。
tab区域用来显示音乐推荐,播放器,播放列表这三个标签页的标题。如果位于当前页,该前标题文字和标题下方的线条就会变红,其他显示为白色。
content区域通过左右滑动可以实现标签页的切换。
- 音乐推荐:向用户展示一些热门的音乐。
- 播放器:显示当前播放的时间和进度,用户可跳转进度。
- 播放列表:显示当前正在播放的曲目列表,用户可以进行曲目切换。
player用来显示当前正在播放的音乐信息,并提供三个按钮,功能依次为“打开播放列表”,“播放/暂停”,“下一曲”。
2. 目录结构
本程序实际上只有一个页面,即pages/index/index,而wxml被拆分成了四个文件,分别是index.wxml,info.wxml,play.wxml,playlist.wxml,整体结构在index.wxml中编写,content区域由于不同的标签页的结构不同,拆分到了三个wxml文件。
1.3 项目初始化
利用微信开发者工具新建一个项目,然后再pages/index/index.json文件中编入页面配置代码。
{
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "音乐",
"navigationBarTextStyle":"black"
}
指定导航栏背景颜色为白色,背景颜色为黑色,标题为"音乐"。
2. 标签页切换
2.1 分析
本功能为“音乐推荐”,“播放器”,“播放列表”三个标签页的切换效果。
设计思路:跟轮播图很像,content是可见区域,上面三个标签类似于焦点,用户向左划,play进入content可见区域,info进入不可见区域。
2.2 前导知识
1.swiper组件
官方文档的swiper组件介绍
通过官方文档我们知道通过改变current的值,可以切换当前显示哪一项
0
1
2
页面显示1。
除了索引值,我们也可以通过item-id来显示每一个
0
1
2
页面显示1。
2.include代码引用
在wxml中可以使用
2.3 编写页面结构和样式
首先在pages/index/index.wxml中编写页面结构代码。
音乐推荐
播放器
播放列表
上述代码中,外层结构由三个view标签(相当于div标签)组成,对应了页面的三个基本区域。然后在内容区域使用了swiper组件,用于标签页的切换,还用include标签引入了info,play,playlist三个wxml文件。
接下来在index.wxss中书写样式代码。
page{
display:flex;
flex-direction:column;
background: #17181a;
color: #ccc;
height: 100%;
}
.tab{
display: flex;
}
.tab-item{
flex: 1;
font-size: 10pt;
text-align: center;
line-height: 72rpx;
border-bottom: 6rpx solid #eee;
}
.content{
flex: 1;
}
.content> swiper {
height: 100%;
}
.player{
background: #222;
border-top: 1px solid #252525;
height: 112rpx;
}
上述代码中,
page使用了flex布局,设置子元素沿垂直方向从上到下排列;将page高度设置为100%,并将content区域设置flex:1,实现页面占满整个屏幕,tab和player分别固定在上方和下方,content的高度自动拉伸为page减去tab和player的高度,适应不同的手机屏幕。
tab区域设为flex布局,将子元素设为flex:1,使这三个子元素沿水平方向从左到右排列,并平局分布每一项的宽度。
设置content区域的swiper组件的高度为100%,从而占领整个content区域。
然后在info.wxml,play.wxml,playlist.wxml中编写简单的代码。
(1)info.wxml
info
(2)play.wxml
play
(3)playlist.wxml
playlist
2.4 实现标签页切换
在本小程序中有两种切换标签页的方法,一种是直接滑动content区域,另一种是单击tab区域中的某一个tab-item切换到对应的标签页。优先实现第二种。
修改index.wxml中tab区域,为三个tab-item绑定事件,并设置data-item属性。
代码如下
音乐推荐
播放器
播放列表
上述代码中,data-item的值表示swiper组件中对应的swiper-item的索引。
接下来为content区域中的swiper组件的current属性绑定变量item。
然后在tab-item的changeItem事件中,将item的值设为data-item的值,即可实现切换到对应的标签页。修改index.js文件,将item和changeItem添加到代码中。
Page({
data: {
item:0
},
changeItem:function(e){
this.setData({
item:e.target.dataset.item
})
},
······
在切换标签页后,还需要修改当前标签页对应的tab-item的样式。下面通过判断变量tab的值,来为当前活跃的data-item增加一个active样式,代码如下。
音乐推荐
播放器
播放列表
在index.wxss中编写active样式
.tab-item.active{
color: #c25b5b;
border-bottom-color:#c25b5b;
}
为了更改tab的值,下面为swiper组件绑定事件,代码如下。
上述代码中,changeTab事件处理函数会在swiper组件发生标签页切换时调用,接下来修改index.js,在页面数据中加入tab变量。
Page({
data: {
item:0,
tab:0
},
然后编写changeTab事件处理函数,将tab值与当前标签页一致。
changeTab:function(e){
this.setData({
tab:e.detail.current
})
},
切换标签页功能完成。
3 音乐推荐
3.1 分析
音乐推荐(content-info)是content区域中的一个标签页,其内容由三部分组成,分别是轮播图(content-info-slide),功能按钮(content-info-portal),热门音乐(content-info-list)。
content区域是swiper组件,该标签页位于索引为0的
由于content区域的可显示高度是有限的·,所以采用scroll-view组件,用于实现内容的上下滚动。
3.2 前导知识
1.scroll-view组件
官方文档的scroll-view组件介绍
2.image组件
官方文档的image组件介绍
3.3 内容区域滚动
在info.wxml中编写音乐推荐标签页中的代码。代码如下。
接下来在index.wxss中将scroll-view的高度设为100%,代码如下。
.content-info{
height: 100%;
}
如果希望隐藏滚动条,还可加入以下代码。
/*隐藏滚动条*/
::-webkit-scrollbar{
width: 0;
height: 0;
color: transparent;
}
3.4 轮播图
轮播图位于音乐推荐标签页的最上方,切换效果通过swiper组件来实现,图片通过image组件(可本地图片也可在网上找,我引入的是网易云音乐的轮播图,以后网易换了,就引别的)引入。在info.wxml的scroll-view组件中编写如下代码。
上述代码中,为swiper组件设置了指示点的颜色为白色半透明,当前选中的指示点颜色为白色不透明。
接下来在index.wxss中编写样式,代码如下。
.content-info-slide{
height: 302rpx;
margin-bottom: 20px;
}
.content-info-slide image{
width: 100%;
height: 100%;
}
实现效果如下。
3.5 功能按钮
功能按钮位于轮播图的下方,在info.wxml中编写页面结构,代码如下。(图片可以在阿里矢量库中下载)
私人FM
每日歌曲推荐
云音乐新歌榜
接下来在index.wxss中编写样式代码吗,代码如下。
.content-info-portal{
display: flex;
margin-bottom: 15px;
}
.content-info-portal>view{
flex: 1;
font-size: 11pt;
text-align: center;
}
.content-info-portal image{
width: 120rpx;
height: 120rpx;
display: block;
margin: 20rpx auto;
}
上述代码中,设置了外层容器为flex布局,然后设置内层view为flex:1,从而使3个view标签平均分配整个容器的宽度。
编写玩代码,实现效果如下。
3.6 热门音乐
热门音乐位于功能按钮的下方,实现思路与功能按钮类似,在info.wxml中功能按钮代码的下方编写热门音乐代码。代码如下。
快乐歌单
学习歌单~(超安静纯音)
极光轻音乐 | 与星空同眠
淦
你搜不到的宝藏系列
嘘,请安静(作业/读书/夜晚/氛围)
「汤 姆·哈 迪 专 属」
接下在index.wxss中编写样式代码,代码如下。
.content-info-list{
font-size: 11pt;
margin-bottom: 20rpx;
}
.content-info-list>.list-title{
margin: 20rpx 35rpx;
}
.content-info-list>.list-inner{
display: flex;
flex-wrap: wrap;
margin: 0 20rpx;
}
.content-info-list>.list-inner>.list-item{
flex: 1;
}
.content-info-list>.list-inner>.list-item>image{
display: block;
width: 200rpx;
height: 200rpx;
margin: 0 auto;
border-radius: 10rpx;
border: 1rpx solid #555;
}
.content-info-list>.list-inner>.list-item>view{
width: 200rpx;
margin: 10rpx auto;
font-size: 10pt;
}
实现效果如下。
4 播放器
4.1 分析
播放器(content-play)是content区域中的一个标签页,其内容由三部分组成,分别是音乐信息(content-play-info),专辑封面(content-play-cover),播放进度(content-play-progress)。该标签页位于索引为1的
明确页面结构后,对播放器的功能进行分析。具体如下。
- 音乐信息:显示当前播放曲目的标题和艺术家。
- 专辑封面:当音乐播放时,专辑封面会顺时针旋转。
- 播放进度:显示当前曲目的播放时长和总时长,并提供一个进度条,当音乐播放时进度条就会增长,用户也可以手动改变进度条的进度来跳转播放进度。
4.2 前导知识
1.音频API
官方文档的介绍
2.slide组件(用于进度条)
官方文档的介绍
4.3 定义基础数据
在index.js的data对象中定义基础数据playlist,主要包括音乐的信息和音乐播放路径,代码如下所示。
playlist:[{
id:1,
title:'玄武之死',
singer:'黎允文',
src:'http://isure.stream.qqmusic.qq.com/C400002MIiSk3m0nse.m4a?guid=6844422626&vkey=DD7562F237BE001039E20BA8BB4081EF045CDE88C6C141EFF338772AF1CC4FD08004C64E00CFB590AEDCF5AA6F19128A7D51C804A07615AA&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M000002hjNNE2TooAe.jpg?max_age=2592000'
},{
id:2,
title:'Without U',
singer:'Sergio',
src:'http://isure.stream.qqmusic.qq.com/C400004gVyzF0D1PPf.m4a?guid=6844422626&vkey=5CE954664D201CF3DCED19F6FD3A8DB6F0D58E83877411A4DE3F466D482A8D8D721CE5477295E986FBB8B6A67344BBF92262D47CFB44F5A3&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M000003meScq4WJVQ6.jpg?max_age=2592000'
},{
id:3,
title:'美丽的神话',
singer:'金喜善 (김희선) _ 成龙',
src:'http://isure.stream.qqmusic.qq.com/C400000k9jj51TOfNy.m4a?guid=6844422626&vkey=CCE79A2C68488D98C0D842313BABE5358E30B9FA77715F2E2FA99DFC4046701D8569964639A4A6F2D6E0DAFAEEF869371D02B063A213D1E4&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M0000023YRbq0UUpdn.jpg?max_age=2592000'
},{
id:4,
title:'Daisuke',
singer:'横田商会',
src:'http://27.221.15.143/amobile.music.tc.qq.com/C400004NHvoq4bezhA.m4a?guid=6844422626&vkey=2835623C04A146286277B62B4AD94F83E43D158940D9D11C864668209EE9A6173416A6821A0C6BBC10E602900B4470595B393A2F80906EF4&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M000000bdyFf1DKofx.jpg?max_age=2592000'
},{
id:5,
title:'Pokemon Ü',
singer:'It`s different',
src:'http://ws.stream.qqmusic.qq.com/C400003ly9LE18HHpK.m4a?guid=6844422626&vkey=E92C38E8583B3DE8701B08A7A1301F4BB63E004DFAB64923FDB15F7FBBCDBD10550351279E71FF47829FA58CCB6DE4E0DA7ACDA223C53CFD&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M0000045liZB2NGp3I.jpg?max_age=2592000'
},{
id:6,
title:'Miss You',
singer:'Fox Stevenson',
src:'http://ws.stream.qqmusic.qq.com/C400003jErC91gSJKz.m4a?guid=6844422626&vkey=52E72CB9C7B2496FC36DEB37296E39200A417F51F68FD16C0F85E233ED0D5757F0D75769588BA361D72BBF4A112E69F842CDE7F7983A063F&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T001R300x300M000001Rthwx2mTz1n.jpg?max_age=2592000'
},{
id:7,
title:'Alone',
singer:'Marshmello',
src:'http://ws.stream.qqmusic.qq.com/C400001T5sEj19MXkW.m4a?guid=6844422626&vkey=8656483DCD374965B5189B65617BB87B5726883F5144CF3C735AE22346859414C73FBD17DBF1361C1A09074B8722AE6CA95B53D652C9BFED&uin=0&fromtag=66',
coverImgUrl:'https://y.gtimg.cn/music/photo_new/T002R300x300M000000SBmte03ICax.jpg?max_age=2592000'
}
],
上述代码中,
id是每条记录的唯一标识,
title是歌名,
singer是歌手,
src是网络中的音频文件链接地址,这里我找的是qq音乐的资源,方法就是点击播放后右键检查元素,看网络,然后把文件类型设置为媒体,最低下那个就是音乐然后复制值填入src就行,但是它每天都会换,一整就无效,emmm,当然你也可以自己搭个服务器,
coverImgUrl是专辑封面图片的链接地址,当然也可以用本地的。
播放列表数据准备好后,还需要再data中定义一些状态属性,来记录音乐的播放状态,播放位置等,代码如下。
state:'paused',
playIndex:0,
play:{
currentTime:'00:00',
duration:'00:00',
percent:0,
title:'',
singer:'',
coverImgUrl:'/images/test.jpg'
}
上述代码中,
state表示音乐的播放状态,paused表示暂停,running表示正在播放,
playIndex表示当前播放的歌曲在播放列表中索引,
play对象记录了当前播放曲目的信息,
currentTime表示播放时长,
duration表示总时长,
percent表示播放进度,
title,singer,coverImgUrl,分别表示,歌名,歌手,专辑封面。
4.4 实现音乐播放功能
音乐的播放功能需要通过调用微信小程序的wx.createInnerAudioContext()接口获得一个audioCtx对象,然后利用audioCtx对象的属性和方法来实现音乐控制。
在index.js中编写代码,实现在页面打开时,自动选择播放列表的第一个曲目,代码如下。
audioCtx:null,
onReady:function(){
this.audioCtx=wx.createInnerAudioContext();
//默认选择第一曲
this.setMusic(0);
},
setMusic:function(index){
var music=this.data.playlist[index];
this.audioCtx.src=music.src;
this.setData({
playIndex:index,
'play.title':music.title,
'play.singer':music.singer,
'play.coverImgUrl':music.coverImgUrl,
'play.currentTime':'00:00',
'play.duration':'00:00',
'play.percent':0
});
},
上述代码中,setMusic()函数用于切换当前播放的曲目,其参数index表示播放列表数组中的某一个成员的索引值,根据索引值到播放列表中取出音乐信息之后,将src赋值给音频接口,然后将曲目信息渲染到页面。
接下来在index.wxml中编写底部播放器的代码,代码如下。
{{play.title}}
{{play.singer}}
上述代码中,图片是从阿里矢量库里下载的。
然后在index.wxss中编写样式,代码如下。
.player{
display: flex;
align-items: center;
background: #222;
border-top: 1px solid #252525;
height: 112rpx;
}
.play-cover{
width: 80rpx;
height: 80rpx;
margin-left: 15rpx;
border-radius: 8rpx;
border: 1px solid #333;
}
.play-info{
flex: 1;
font-size: 10pt;
line-height: 38rpx;
margin-left: 20rpx;
padding-bottom: 8rpx;
}
.play-info-singer{
color: #888;
}
.play-controls image{
width: 80rpx;
height: 80rpx;
margin-right: 15rpx;
}
上述代码中,父元素设置flex布局,内部音乐封面和播放按钮设置为固定宽高,放在页面两端,中间部分是音乐信息的显示区域。
实现效果。
为了实现单击播放按钮播放音乐,再次点击暂停音乐的效果,修改播放按钮的代码,利用条件渲染判断state的值,来显示不同的按钮,代码如下。
在index.js中编写play()和pause(),代码如下。
play:function(){
this.audioCtx.play();
this.setData({state:'running'})
},
pause:function(){
this.audioCtx.pause();
this.setData({state:'paused'})
},
上述代码中,分别调用了audioCtx对象的play()和pause()方法,实现音乐的播放与暂停,同时更改当前音乐的播放状态state。
接下来还需要实现单击下一曲按钮进行换曲,在index.wxml中为按钮绑定next()事件处理函数,代码如下。
在index.js中编写事件处理函数next(),代码如下。
next:function(){
var index=this.data.playIndex>=this.data.playlist.length-1 ? 0:this.data.playIndex + 1;
var that=this;
this.setMusic(index);
if(this.data.state==="running"){
this.play();
}
},
上述代码中,index用于增加当前播放的播放列表的索引,通过判断避免超过最大索引,使其在播放列表循环,还判断了当前的播放状态,如果当前状态为暂停,不会立即播放。
不知道是不是我看的书有问题(微信读书看的,白送30天,美汁儿汁儿),这代码有bug,就是在后面写进度条那段,点击下一曲之后,进度条全变为默认的00:00,所以我又在next()中加了一段代码来使下一曲之后重新设置当前曲目的总时长。代码如下。
this.audioCtx.onCanplay(function(){
that.setData({
'play.duration':that.formatTime(that.audioCtx.duration),
})
})
上述代码中,使用了audioCtx的onCanplay方法,在音乐进入可播放状态时,设置总时长为当前歌曲的总时长,formatTime是调整时间格式的函数,与next()同级,之后在页面初次渲染时也会用到。代码如下。
formatTime:function(time){
var minute=Math.floor(time/60)%60;
var second=Math.floor(time)%60;
return (minute <10?'0'+minute:minute)+':'+(second<10?'0'+second:second)
},
接下来还需要实现单击播放列表按钮跳转到播放列表标签页,在index.wxml中为按钮绑定changePage()事件处理函数,代码如下。
在index.js中编写事件处理函数changePage(),代码如下。
changePage:function(e){
this.setData({
tab:e.currentTarget.dataset.page,
item:e.currentTarget.dataset.page
})
},
上述代码中,点击按钮后将tab和item设置为2,实现跳转。
4.5 编写播放器页面
播放器页面主要用于显示当前播放曲目的信息,以及显示播放进度和时间。页面中显示的信息都可以在data数据中获取。在play.wxml中编写页面结构,代码如下。
{{play.title}}
——{{play.singer}}——
在index.wxss中编写样式,代码如下。
.content-play{
display: flex;
justify-content: space-around;
flex-direction: column;
height: 100%;
text-align: center;
}
.content-play-info>view{
color: #888;
font-size: 11pt;
}
实现效果如下。
接下来在音乐信息的下方展示音乐专辑封面,在音乐列表中获取到当前音乐专辑封面的src路径,绑定到页面image组件中,代码如下。
上述代码中,animation-play-state用于控制专辑封面旋转动画的播放和暂停。
完成结构代码后,在index.wxss中编写样式,代码如下。
.content-play-cover image{
animation: rotateImage 10s linear infinite;
width: 400rpx;
height: 400rpx;
border-radius: 50%;
border: 1px solid #333;
}
@keyframes rotateImage{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
实现效果如下。
4.6 控制播放进度
在播放器页面的底部,用于显示播放进度,通过调节滑块的位置可以控制播放进度。在play.wxml中编写播放进度结构代码,代码如下。
{{play.currentTime}}
{{play.duration}}
上述代码中,用到了slide组件,其值为播放进度play.percent。
接下来在index.wxss中编写样式,代码如下。
.content-play-progress{
display: flex;
align-items: center;
margin: 0 35rpx;
font-size: 9pt;
text-align: center;
}
.content-play-progress>view{
flex: 1;
}
.content-play-progress>text{
color: white;
margin-top: -35rpx;
}
效果如下。
然后在index.js中控制进度条的进度和时间显示(修改onReady函数),代码如下。
onReady:function(){
this.audioCtx=wx.createInnerAudioContext();
var that=this;
//播放失败检测
this.audioCtx.onError(function(){
console.log('播放失败'+that.audioCtx.src);
})
//播放完成自动换下一曲
this.audioCtx.onEnded(function(){
that.next();
})
//自动更新播放进度
this.audioCtx.onPlay(function(){})
this.audioCtx.onTimeUpdate(function(){
that.setData({
'play.duration':that.formatTime(that.audioCtx.duration),
'play.currentTime':that.formatTime(that.audioCtx.currentTime),
'play.percent':that.audioCtx.currentTime/that.audioCtx.duration*100
})
})
//默认选择第一曲
this.setMusic(0);
},
上述代码中,通过调用audioCtx的onTimeUpdate()方法,获取音乐状态信息,并通过formatTime()函数处理时间格式,最后渲染到页面实现定时更新效果。
在slide组件上绑定bindchange事件,可以实现滑动进度条调节音乐播放进度。
在index.js中编写slideChange函数获取当前用户选择的进度,将时间通过audioCtx对象的seek()方法进行设置,代码如下。
slideChange:function(e){
var that=this;
var second=e.detail.value*this.audioCtx.duration/100;
this.audioCtx.seek(second);
},
上述代码中,我运行之后发现了一个bug,在用户调节进度条之后,进度条左边的播放时间并不会跟着一起变,所以我在slideChange中添加了一些代码达到此功能,代码如下。
this.audioCtx.onSeeked(function(){
that.setData({
'play.duration':that.formatTime(that.audioCtx.duration),
'play.currentTime':that.formatTime(that.audioCtx.currentTime),
'play.percent':that.audioCtx.currentTime/that.audioCtx.duration*100
})
})
上述代码使用了audioCtx对象的onSeeked()方法,当发生跳转时间时,会重新渲染页面。
5 播放列表
5.1 分析
播放列表(content-play)是content区域中的最后一个标签页,其内容是一个列表,通过列表渲染(wx:for循环)自动生成。该标签页位于索引为2的
5.2 编写页面结构和样式
播放列表的数据在前面就已经定义,下面在playlist.wxml中编写页面,代码如下。
{{item.title}}
{{item.singer}}
正在播放
上述代码中,wx:if判断当前播放的索引playIndex是否与当前播放列表数据的索引In,如果index相同则显示正在播放。
在index.wxss中编写样式,代码如下。
.playlist-item{
display: flex;
align-items: center;
border-bottom: 1rpx solid #333;
height: 112rpx;
}
.playlist-cover{
width: 80rpx;
height: 80rpx;
margin-left: 15rpx;
border-radius: 8rpx;
border: 1px solid #333;
}
.playlist-info{
flex: 1;
font-size: 10pt;
line-height: 28rpx;
margin-left: 20rpx;
padding-bottom: 8rpx;
}
.playlist-info-singer{
margin-top: 15rpx;
color: #888;
}
.playlist-controls{
font-size: 10pt;
margin-top: -50rpx;
margin-right: 20rpx;
color: #c25b5b;
}
实现效果如下。
5.3 实现换曲功能
当用户单击播放列表中的某一项之后,会触发执行对应的曲目。在index.js中编写代码。
change:function(e){
this.setMusic(e.currentTarget.dataset.index);
this.play();
}