第三章 音乐播放器

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的值,可以切换当前显示哪一项<swiper-item>,其值是一个从0开始的索引,对应<swiper-item>的顺序。

<swiper current="1">
            <swiper-item>
                0
            </swiper-item>
            <swiper-item>
                1
            </swiper-item>
            <swiper-item>
                2
            </swiper-item>
</swiper>

页面显示1。

除了索引值,我们也可以通过item-id来显示每一个<swiper-item>。

<swiper current-item-id="b">
            <swiper-item item-id="a">
                0
            </swiper-item>
            <swiper-item item-id="b">
                1
            </swiper-item>
            <swiper-item item-id="c">
                2
            </swiper-item>
</swiper>

页面显示1。
2.include代码引用
在wxml中可以使用<include>标签引用其他文件中的代码,相当于将引用的代码复制到<include>标签的位置。
<include src="文件路径">

2.3 编写页面结构和样式

首先在pages/index/index.wxml中编写页面结构代码。

<!-- 标签页标题-->
<view class="tab">
  <view class="tab-item"  >
    音乐推荐
  </view>
  <view class="tab-item"  >
    播放器
  </view>
  <view class="tab-item"  >
    播放列表
  </view>
</view>
<!-- 内容区域 -->
<view class="content">
  <swiper>
    <swiper-item>
      <include src="info.wxml"></include>
    </swiper-item>
    <swiper-item>
      <include src="play.wxml"></include>
    </swiper-item>
    <swiper-item>
      <include src="playlist.wxml"></include>
    </swiper-item>
  </swiper>
</view>
<!-- 底部播放器 -->
<view class="player">
  
</view>

上述代码中,外层结构由三个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

<view style="background:#333;color:#ccc;height:100%;">info</view>

(2)play.wxml

<view style="background:#333;color:#ccc;height:100%;">play</view>

(3)playlist.wxml

<view style="background:#333;color:#eee;height:100%;">playlist</view>

2.4 实现标签页切换

在本小程序中有两种切换标签页的方法,一种是直接滑动content区域,另一种是单击tab区域中的某一个tab-item切换到对应的标签页。优先实现第二种。
修改index.wxml中tab区域,为三个tab-item绑定事件,并设置data-item属性。
代码如下

  <view class="tab-item"  bindtap="changeItem" data-item="0">
    音乐推荐
  </view>
  <view class="tab-item"  bindtap="changeItem" data-item="1">
    播放器
  </view>
  <view class="tab-item"  bindtap="changeItem" data-item="2">
    播放列表
  </view>

上述代码中,data-item的值表示swiper组件中对应的swiper-item的索引。
接下来为content区域中的swiper组件的current属性绑定变量item。

<view class="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样式,代码如下。

  <view class="tab-item {{tab==0?'active':''}}"  bindtap="changeItem" data-item="0">
    音乐推荐
  </view>
  <view class="tab-item {{tab==1?'active':''}}"  bindtap="changeItem" data-item="1">
    播放器
  </view>
  <view class="tab-item {{tab==2?'active':''}}"  bindtap="changeItem" data-item="2">
    播放列表
  </view>

在index.wxss中编写active样式

.tab-item.active{
  color: #c25b5b;
  border-bottom-color:#c25b5b;
}

为了更改tab的值,下面为swiper组件绑定事件,代码如下。

<view class="content">
  <swiper current="{{item}}" bindchange="changeTab">

上述代码中,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的<swiper-item>标签中。
由于content区域的可显示高度是有限的·,所以采用scroll-view组件,用于实现内容的上下滚动。

3.2 前导知识

1.scroll-view组件
官方文档的scroll-view组件介绍
2.image组件
官方文档的image组件介绍

3.3 内容区域滚动

在info.wxml中编写音乐推荐标签页中的代码。代码如下。

<scroll-view class="content-info" scroll-y>
        
</scroll-view>

接下来在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 class="content-info-slide"  indicator-color="rgba(225,225,225,.5)" autoplay circular indicator-dots indicator-active-color="#fff">
            <swiper-item>
                <image src="http://p1.music.126.net/jjxZ0vZxmmKgB4yAkHno4A==/109951165039826320.jpg?imageView&quality=89"></image>
            </swiper-item>
            <swiper-item>
                <image src="http://p1.music.126.net/7XgfJP6ZrHOhb4rfFava4w==/109951165039992796.jpg?imageView&quality=89"></image>
            </swiper-item>
            <swiper-item>
                <image src="http://p1.music.126.net/vhn35mv4_kw6u0yNCvtG1Q==/109951165038927533.jpg?imageView&quality=89"></image>
            </swiper-item>
</swiper>

上述代码中,为swiper组件设置了指示点的颜色为白色半透明,当前选中的指示点颜色为白色不透明。
接下来在index.wxss中编写样式,代码如下。

.content-info-slide{
  height: 302rpx;
  margin-bottom: 20px;
}
.content-info-slide image{
  width: 100%;
  height: 100%;
}

实现效果如下。


轮播图

3.5 功能按钮

功能按钮位于轮播图的下方,在info.wxml中编写页面结构,代码如下。(图片可以在阿里矢量库中下载)

    <view class="content-info-portal">
        <view>
            <image src="/images/01.png"></image>
            <text>私人FM</text>
        </view>
        <view>
            <image src="/images/02.png"></image>
            <text>每日歌曲推荐</text>
        </view>
        <view>
            <image src="/images/03.png"></image>
            <text>云音乐新歌榜</text>
        </view>
    </view>

接下来在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中功能按钮代码的下方编写热门音乐代码。代码如下。

<view class="content-info-list">
        <view class="list-title">
            快乐歌单
        </view>
        <view class="list-inner">
            <view class="list-item">
                <image src="http://p3.music.126.net/EGlk_xp0Bny0DtskyRz1AA==/109951164941089740.jpg?param=200y200"/>
                <view>
                    学习歌单~(超安静纯音)
                </view>
            </view>
            <view class="list-item">
                <image src="http://p4.music.126.net/LJsMADNvkK_0ilmXbKg_TQ==/109951164809294621.jpg?param=200y200"/>
                <view>
                    极光轻音乐 | 与星空同眠
                </view>
            </view>
            <view class="list-item">
                <image src="http://p3.music.126.net/hnu57kRRqzOBunZc2tJTUQ==/109951164895631294.jpg?param=200y200"/>
                <view>
                    淦
                </view>
            </view>
            <view class="list-item">
                <image src="http://p3.music.126.net/kR4V8R2Bu_pfuGboreeaGw==/109951164942702378.jpg?param=200y200"/>
                <view>
                    你搜不到的宝藏系列
                </view>
            </view>
            <view class="list-item">
                <image src="http://p4.music.126.net/pMVnViboITvn_KDmcfoaEA==/109951164873911421.jpg?param=200y200"/>
                <view>
                   嘘,请安静(作业/读书/夜晚/氛围)
                </view>
            </view>
            <view class="list-item">
                <image src="http://p4.music.126.net/CJSYuqYyaZKC5FZWhtPj0g==/109951164954716158.jpg?param=200y200"/>
                <view>
                   「汤 姆·哈 迪 专 属」
                </view>
            </view>
        </view>
    </view>

接下在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的<swiper-item>标签中。
明确页面结构后,对播放器的功能进行分析。具体如下。

  • 音乐信息:显示当前播放曲目的标题和艺术家。
  • 专辑封面:当音乐播放时,专辑封面会顺时针旋转。
  • 播放进度:显示当前曲目的播放时长和总时长,并提供一个进度条,当音乐播放时进度条就会增长,用户也可以手动改变进度条的进度来跳转播放进度。

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中编写底部播放器的代码,代码如下。

<!-- 底部播放器 -->
<view class="player">
  <image class="play-cover" src="{{play.coverImgUrl}}" />
  <view class="play-info">
    <view class="play-info-title">
      {{play.title}}
    </view>
    <view class="play-info-singer">
      {{play.singer}}
    </view>
  </view>
  <view class="play-controls">
    <!-- 切换到播放列表 -->
    <image src="/images/liebiao.png"/>
    <!-- 播放 -->
    <image src="/images/zanting.png"/>
    <!-- 下一曲 -->
    <image src="/images/xiayiqu.png"/>
  </view>
</view>

上述代码中,图片是从阿里矢量库里下载的。
然后在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的值,来显示不同的按钮,代码如下。

<!-- 播放 -->
    <image wx:if="{{state=='paused'}}" src="/images/zanting.png"  bindtap = "play"/>
    <image wx:else src="/images/bofang.png"  bindtap = "pause"/>

在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()事件处理函数,代码如下。

    <!-- 下一曲 -->
    <image src="/images/xiayiqu.png" bindtap = "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()事件处理函数,代码如下。

    <!-- 切换到播放列表 -->
    <image src="/images/liebiao.png" bindtap="changePage" data-page="2" />

在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中编写页面结构,代码如下。

<view class="`content-play">
        <!-- 显示音乐信息 -->
        <view class="content-play-info">
            <text>{{play.title}}</text>
            <view>——{{play.singer}}——</view>
        </view>
        <!-- 显示专辑封面 -->
        <view class="content-play-cover">
        </view>
        <!-- 显示播放进度和时间 -->
        <view class="content-play-progress">
        </view>
    </view>

在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组件中,代码如下。

        <!-- 显示专辑封面 -->
        <view class="content-play-cover">
            <image  src="{{play.coverImgUrl}}" style="animation-play-state:{{state}}" />
        </view>

上述代码中,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中编写播放进度结构代码,代码如下。

<!-- 显示播放进度和时间 -->
        <view class="content-play-progress">
            <text>
               {{play.currentTime}} 
            </text>
            <view>
                <slider value="{{play.percent}}" activeColor="#d33a31" backgroundColor="#dadada" block-size="12" />
            </view>
            <text>
                {{play.duration}}
            </text>
        </view>

上述代码中,用到了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事件,可以实现滑动进度条调节音乐播放进度。

<slider value="{{play.percent}}" activeColor="#d33a31" backgroundColor="#dadada" block-size="12"  bindchange="slideChange"/>

在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的<swiper-item>标签中。

5.2 编写页面结构和样式

播放列表的数据在前面就已经定义,下面在playlist.wxml中编写页面,代码如下。

<scroll-view class="content-playlist" scroll-y>
        <view class="playlist-item" 
        wx:for="{{playlist}}"
        wx:key="id"
        bindtap = "change" data-index="{{index}}">
            <image class="playlist-cover" src="{{item.coverImgUrl}}"/>
            <view class="playlist-info">
                <view class="playlist-info-title">
                    {{item.title}}
                </view>
                <view class="playlist-info-singer">
                    {{item.singer}}
                </view>
            </view>
            <view class="playlist-controls">
                <text
                wx:if="{{index==playIndex}}">
                    正在播放
                </text>
            </view>
        </view>
    </scroll-view>

上述代码中,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();
  }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。