音乐播放器微信小程序

一:学习目标:

  1. 掌握swiper组件、scroll-view组件的使用;
  2. 掌握image组件的使用;
  3. 掌握slider组件的使用;
  4. 掌握音频API的使用;

 二:目录:

1. 开发前的准备

1.1 音乐小程序项目展示

1.1.1:音乐推荐界面展示:

1.1.2:播放器界面展示:

1.1.3:播放列表界面展示:

1.2:项目分析:

①:音乐播放器项目(项目结构图)

  • tab导航栏
  • content内容区
  • player音乐播放控件

 ②:音乐播放器小程序项目主要文件

  • app.js:应用程序的逻辑文件
  • app.json:应用程序的配置文件
  • pages/index/index1.js:index页面的逻辑文件
  • pages/index/index1.json:index页面的配置文件
  • pages/index/index1.wxss:index页面的样式文件
  • pages/index/index1.wxml:index页面的结构文件
  • pages/index/info.wxml:“音乐推荐”标签页的结构文件
  • pages/index/play.wxml:“播放器”标签页的结构文件
  • pages/index/playlist.wxml:“播放列表”标签页的结构文件
  • images:图片文件

 1.3:项目初始化

开发者工具创建项目:

2. 标签页切换 

2.1:任务分析:

标签页和页面结构图:

2.2 前导知识:

swiper组件常用属性:

可选值

说明

默认

indicator-dots

Boolean

是否显示面板指示点,默认为false

indicator-color

Color

指示点颜色,默认为rgba(0,0,0,.3)

indicator-active-color

Color

当前选中的指示点颜色,默认为#000000

autoplay

Boolean

是否自动切换,默认为false

current

Number

当前所在滑块的index,默认为0

current-item-id

String

当前所在滑块的item-id(不能同时指定current)

interval

Number

自动切换时间间隔(毫秒),默认为5000

duration

Number

滑动动画时长(毫秒),默认为500

circular

Boolean

是否采用衔接滑动,默认为false

vertical

Boolean

滑动方向是否为纵向,默认为false

bindchange

EventHandle

current改变时会触发change事件

swiper组件编写滑动页面结构 index1.wxml(本项目已完成,无此项代码)

③:include主要用途:

  • 将代码拆分到多个文件中,可以更方便地查找代码。
  • 将代码公共部分抽取出来。通过外部文件引入。

④:编写页面结构和样式-tab导航栏  

 2.3 编写页面结构和样式 :

①:音乐小程序基础页面和样式:

②:音乐小程序基础页面和样式-tab导航的样式:

 tab导航样式效果:

 2.4:实现标签页切换:

单击导航栏选项卡实现标签页切换(js):

通过滚动事件切换页面效果(js):

 效果:

 3. 音乐推荐

3.1 任务分析

音乐推荐页面结构图:

3.2 前导知识 

image组件属性及说明:

可选值

说明

默认

src

String

图片资源地址

mode

String

图片裁剪、缩放的模式,默认为'scaleToFill'

lazy-load

Boolean

图片是否懒加载,默认为false。只针对page与scroll-view下的image有效

binderror

HandleEvent

图片发生错误时的事件

bindload

HandleEvent

图片载入完成时的事件

scaleToFill

不保持纵横比缩放图片,使图片的宽高完全拉伸至填满image元素。适用于容器与图片宽高比相同的情况,如果不同,图片会变形。

aspectFit

保持纵横比缩放图片,使图片的长边能完全显示出来。适用于将图片完整显示出来。例如,详情页的图片

aspectFill

保持纵横比缩放图片,只保证图片的短边能完全显示出来,长边将会发生截取。适用于容器固定,图片自动缩放的情况,如列表页的缩略图

widthFix

宽度不变,高度自动变化,保持原图宽高比不变

top

不缩放图片,只显示图片的顶部区域

bottom

不缩放图片,只显示图片的底部区域

center

不缩放图片,只显示图片的中间区域

left

不缩放图片,只显示图片的左边区域

right

不缩放图片,只显示图片的右边区域

top left

不缩放图片,只显示图片的左上边区域

top right

不缩放图片,只显示图片的右上边区域

bottom left

不缩放图片,只显示图片的左下边区域

bottom right

不缩放图片,只显示图片的右下边区域

image组件缩放模式和裁剪模式测试(效果图):

3.3 轮播图

在info.wxml中使用swiper组件实现轮播图:

3.4 功能按钮 

flex布局实现功能按钮:

flex布局样式:

 效果图:

3.5 热门音乐 

①:flex布局实现页面布局:

  flex布局实现页面布局(样式):

②:index页面底部播放:

index页面底部播放(样式):

index页面播放底部呈现效果 :

4. 播放器

4.1 任务分析

4.1.1 播放器的具体功能进行分析:

  • 音乐信息:显示当前播放曲目的标题和艺术家。
  • 专辑封面:当音乐播放时,专辑封面会顺时针旋转。
  • 播放进度:显示播放进度,调节音乐进度。 

4.1.2 播放器标签页结构图:

4.2 前导知识

①:音频API接口的属性及说明:

可选值

名称

说明

属性

src

音频资源的地址,用于直接播放

startTime

开始播放的位置(秒),默认为0

autoplay

是否自动开始播放,默认为false

loop

是否循环播放,默认为false

volume

音量。范围0~1。默认为1

属性

duration

音频的长度(秒)。在当前有合法的src时返回(只读)

currentTime

音频的播放位置(秒)。在当前有合法的src时返回(只读)

paused

当前是是否暂停或停止状态(只读)

方法

play()

播放

pause()

暂停(暂停后的音频再播放会从暂停处开始播放)

stop()

停止(停止后的音频再播放会从头开始播放)

seek()

跳转到指定位置

destroy()

销毁当前实例

onCanplay()

音频进入可以播放状态的事件(参数为回调函数)

onPlay()

音频播放事件(参数为回调函数)

方法

onPause()

音频暂停事件(参数为回调函数)

onStop()

音频停止事件(参数为回调函数)

onEnded()

音频自然播放至结束的事件(参数为回调函数)

onSeeked()

音频进行跳转操作的事件(参数为回调函数)

onTimeUpdate()

音频播放进度更新事件(参数为回调函数)

onError()

音频播放错误事件(参数为回调函数)

②:innerAudioContext案例使用:

③:slider组件属性及说明:

可选值

类型

说明

min

Number

最小值,默认为0

max

Number

最大值,默认为100

step

Number

步长,取值大于0,可被(max-min)整除,默认为1

value

Number

当前取值,默认为0

activeColor

Color

已选择的颜色,默认为#1aad19

backgroundColor

Color

背景条的颜色,默认为#e9e9e9

block-size

Number

滑块的大小,取值范围为12~28,默认为28

block-color

Color

滑块的颜色,默认为#ffffff

show-value

Boolean

是否显示当前value,默认为false

bindchange

EventHandle

完成一次拖动后触发的事件

bindchanging

EventHandle

拖动过程中触发的事件

4.3 定义基础数据

4.4 实现音乐播放功能

①:音乐播放逻辑代码(js):

②:底部播放器暂停/播放按钮控制歌曲:

js:

③:播放器切换到下一首歌曲:

js:

4.5 编写播放器页面

①:播放页面 play.wxml 结构代码:

播放页面样式代码:

效果:

②:播放器样式代码:

③:通过样式动画实现海报的旋转功能:

海报的旋转功能样式代码:

4.6 控制播放进度

播放器页面下方的滑块结构(play.wxml中):

滑块样式代码:

5. 播放列表

5.1 任务分析结构图

5.2 编写页面结构和样式

控制进度条的长度控制歌曲播放进度:

控制进度条的长度控制歌曲播放进度样式:

5.3 实现换曲功能

换曲功能(js)代码:

代码:

pages/index/index1.js:

// pages/index/index1.js
Page({
  /**
   * 页面的初始数据
   * item控制页面显示的,0-2,表示显示不同的页面和顶部不同的导航
   *  state控制图标或当前音乐播放的状态:'paused'表示暂停,'running'表示在运行
   */
  data: {
    item:2,
    state:'paused',
    playindex:0,
    playlist:[{
      id:1,
      title:"운명(命运)",
      singer:"why",
      coverImage:"../images/cover.jpg",
      src:"http://www.ytmp3.cn/down/78392.mp3"
    },{
      id:2,
      title:"玫瑰花的葬礼",
      singer:"许嵩",
      coverImage:"../images/cover.jpg",
      src:"http://www.ytmp3.cn/down/78393.mp3"
    },{
      id:3,
      title:"小酒窝",
      singer:"蔡卓妍/林俊杰",
      coverImage:"../images/cover.jpg",
      src:"http://www.ytmp3.cn/down/78378.mp3"
    },{
      id:4,
      title:"十年",
      singer:"陈奕迅",
      coverImage:"../images/cover.jpg",
      src:"http://www.ytmp3.cn/down/78384.mp3"
    }],
    currentplay:{
      id:0,
      title:"",
      singer:"",
      coverImage:"",
      src:"",
      duration:"00:01",
      currentTime:"00:01",

    }

  },
  changeItem:function(e){
   
    this.setData({
      item:e.target.dataset.item
    })

  },
  changeTab:function(e){
    console.log(e)
    this.setData({
      item:e.detail.current
    })
  },
  play:function(e){
    // 还缺音乐播放里面暂停播放的逻辑
    this.audioCtx.play();
    this.setData({
      state:'running'
    })
  },
  pause:function(e){
    // 还缺音乐播放里面暂停播放的逻辑
    this.audioCtx.pause();
   this.setData({
     state:'paused'
   })
  },
  next:function(e){
    var index = this.data.playindex
    var count = this.data.playlist.length;
    index = index +1;
    index = index%count;
    this.setMusic(index)
    this.play()
  
  },
  changeMusic:function(e){
    console.log(e)
    var index = e.currentTarget.dataset.index;
    this.setMusic(index);
    this.play();

  },
  scroll: function(e) {
    console.log(e.detail)
  },
  testBind:function(e){
    console.log(e)
  } ,
  testBinding:function(e){
    console.log(e)
  } ,
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  audioCtx:null,
  onReady() {
    this.audioCtx = wx.createInnerAudioContext();
    this.setMusic(0);//默认选择第一首
    var outthis = this;
    this.audioCtx.onTimeUpdate(function(){
      var duration = outthis.timeFormat(outthis.audioCtx.duration);
      var currentTime = outthis.timeFormat(outthis.audioCtx.currentTime);
      console.log("播放回调方法调用"+duration+";currenttime"+currentTime)
      outthis.setData({
        'currentplay.currentTime':currentTime,
        'current.duration':duration
      })
    });
   
  },
  timeFormat:function(time){
    var minutes = Math.floor(time/60);
    var lminutes = ""
    if(minutes<10){
      lminutes = '0'+ minutes
    }else{
      lminutes = minutes
    }
    var seconds = Math.floor(time)%60;
    var lseconds = ""
    if(seconds<10){
      lseconds = '0'+ seconds
    }else{
      lseconds = seconds;
    }
    console.log(lminutes+":"+lseconds)
    return(lminutes+":"+lseconds);
  },
  setMusic:function(tempIndex){
    //当前播放歌曲的信息
     var currentMusic =  this.data.playlist[tempIndex];
     //设置音频播放的地址
     this.audioCtx.src = currentMusic.src;
     this.setData({
      playindex:tempIndex,
      'currentplay.id':currentMusic.id,
      'currentplay.title':currentMusic.title,
      'currentplay.singer':currentMusic.singer,
      'currentplay.coverImage':currentMusic.coverImage,
      'currentplay.src':currentMusic.src,
     });

  },


  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})

pages/index/index1.wxss:

.tab{
  display: flex;
  flex-direction: row;
}
.tab-item{
  flex: 1;
  text-align: center;
  border-bottom: 6rpx solid #eee;
  font-size: 10pt;
  line-height: 72rpx;
  color: white;
}

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

page{
  background-color: #1a1818;
  display: flex;
  flex-direction: column;
  height: 100%;
  color: #ccc;
}
.content{
  flex: 1;
}
.content-info-portal{
  display: flex;
  flex-direction: row;
}

.content-info-portal image{
  width: 120rpx;
  height:120rpx;
}

/* 轮播图 */
.content-info-side{
  height: 150px;
}
.content-info-side image{
  width: 100%;
  height: 100%;
}
.content >swiper{
  height: 100%;
}
/* 功能按钮 */
.content-info-portal{
  display: flex;
  flex-direction: row;
  }
  .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;
  }
  .list-inner{
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
  }
  .list-item{
    flex:1;
    margin: 15rpx;
    font-size: 10pt;
  }
  
  .list-item image{
    width: 200rpx;
    height: 200rpx;
    display: block;
    margin: 0 auto;
    border-radius: 10rpx;
    border: 1rpx solid #555;
  }
  /* 播放器部分 */
.player{
  height: 112rpx;
  display: flex;
  flex-direction: row;
  align-items: center;
  background: chocolate;
}
.player>image{
  width: 80rpx;
  height: 80rpx;
  margin-left: 15rpx;
  border-radius: 10rpx;
  border: 1px solid #333;
}

.player-control image{
  width: 80rpx;
  height: 80rpx;
}
.player-info{
  flex: 1;
  font-size: 10pt;
  line-height: 38rpx;
  margin-left: 20rpx;

}
.player-info-singer{
  color: #555;
}

/* 实现播放器的样式 */
.content-play{
  display: flex;
  flex-direction: column;
  height: 100%;
  text-align: center;
  justify-content: space-around;
}
.content-play-cover>image{
    width: 320rpx;
    height: 320rpx;
    border-radius: 50%;
    border: 1px solid #555;
    animation: roateImage 10s linear infinite;
}
@keyframes roateImage{
  from{
    transform: rotate(0deg);
  }
  to{
    transform: rotate(360deg);
  }
}

/* 播放进度条的样式 */
.content-play-progress{
  display: flex;
  text-align: center;
  margin: 0 35rpx;
  font-size: 10pt;
  align-items: center;
}
.content-play-progress>slider{
  flex: 1;
}
.playlist-content{
  display: flex;
  flex-direction: column;
  height: 100%;
}
.playlist-item{
display: flex;
height: 112rpx;
align-items: center;
border-bottom: 1px solid #555;
}
.playlist-item>image{
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
border: 1px solid #555;
margin-left: 15rpx;
}
.playlist-item-info{
  flex: 1;
  font-size: 10pt;
  line-height: 38rpx;
  margin-left: 20rpx;
  padding: 0 5rpx;
}
.playlist-item-play{
  font-size: 10pt;
  color: #c25b5b;
  margin-right: 20rpx;
}

pages/index/index1.wxml:

<view class="tab">
  <view class="tab-item {
   
   {item ==0?'active':'' }}" data-item="0" bindtap="changeItem">音乐推荐</view>
  <view class="tab-item {
   
   {item ==1?'active':'' }}" data-item="1" bindtap="changeItem">播放器</view>
  <view class="tab-item {
   
   {item ==2?'active':'' }}" data-item="2" bindtap="changeItem">播放列表</view>
</view>
<!-- 正文信息-->
<view class="content">
  <swiper current="{
   
   {item}}" bindchange="changeTab">
    <swiper-item>
      <include src="./info"></include>
    </swiper-item>
    <swiper-item>
      <include src="./play"></include>
    </swiper-item>
    <swiper-item>
      <include src="./playlist"></include>
    </swiper-item>
  </swiper>
</view>
<!-- 播放器-->
<view class="player">
  <image src="{
   
   {currentplay.coverImage}}" />
  <view class="player-info">
    <view>{
   
   {currentplay.title}}</view>
    <view class="player-info-singer">{
   
   {currentplay.singer}}</view>
  </view>
  <view class="player-control">
    <image src="../images/01.png" />
    <!-- 播放或暂停 -->
    <image wx:if="{
   
   {state=='paused'}}" src="../images/02.png" bindtap="play" />
    <image wx:if="{
   
   {state =='running'}}" src="../images/02stop.png" bindtap="pause" />
    <!-- 切换到下一首 -->
    <image src="../images/03.png" bindtap="next" />
  </view>
</view>

pages/index/info.wxml:

<swiper class="content-info-side" indicator-dots="true" indicator-color="#eee" indicator-active-color="#ffffff">
  <swiper-item>
    <image src="../images/banner.jpg" mode="aspectFill"></image>
  </swiper-item>
  <swiper-item>
    <image src="../images/banner.jpg" mode="aspectFill"></image>
  </swiper-item>
  <swiper-item>
    <image src="../images/banner.jpg" mode="aspectFill"></image>
  </swiper-item>
</swiper>
<!--功能按钮功能区域-->
<view class="content-info-portal">
  <view>
    <image src="../images/04.png"></image>
    <text>私人FM</text>
  </view>
  <view>
    <image src="../images/05.png"></image>
    <text>每日歌曲推荐</text>
  </view>
  <view>
    <image src="../images/06.png"></image>
    <text>云音乐新歌榜</text>
  </view>

</view>
<!--推荐歌曲区域-->
<view class="content-info-list">
  <text>推荐歌曲</text>
  <view class="list-inner">
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>紫罗兰</text>
    </view>
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>欢乐颂</text>
    </view>
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>再见</text>
    </view>
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>紫罗兰</text>
    </view>
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>紫罗兰</text>
    </view>
    <view class="list-item">
      <image src="../images/cover.jpg"></image>
      <text>紫罗兰</text>
    </view>

  </view>

</view>

pages/index/play.wxml:

<view class="content-play">
  <!-- 显示播放信息 -->
  <view>
    <view>肖邦的夜曲</view>
    <view>--肖邦--</view>
  </view>
  <view class="content-play-cover">
    <image src="../images/cover.jpg" style="animation-play-state: {
   
   {state}};" />
  </view>
  <view class="content-play-progress">
    <text>{
   
   {currentplay.currentTime}}</text>
    <slider activeColor="#555" backgroundColor="#e9e9e9" block-size="12" block-color="#e9e9e9" value="80" bindchange="testBind" bindchanging="testBinding"></slider>
    <text>{
   
   {currentplay.duration}}</text>
  </view>
</view>

pages/index/playlist.wxml:

<view class="playlist-content">
  <label wx:for="{
   
   {playlist}}" wx:key="id">
    <view class="playlist-item" data-index="{
   
   {index}}" bindtap="changeMusic">
      <!-- <image src="../images/cover.jpg" /> -->
      <image src="{
   
   {item.coverImage}}"></image>
      <view class="playlist-item-info">
        <!-- <view>肖邦的夜曲</view> -->
        <view>{
   
   {item.title}}</view>
        <view>{
   
   {item.singer}}</view>
      </view>
      <view class="playlist-item-play" wx:if="{
   
   {index==playindex}}">
        正在播放
      </view>
    </view>
  </label>
</view>

猜你喜欢

转载自blog.csdn.net/RosalynZhuo/article/details/128355645