微信小程序--音乐播放器

微信小程序页面结构和组件练习--音乐播放器

说明:

  • 这个项目旨在练习组件以及页面的设计。页面和交互的实现可能有多种方式,这里只为了对组件和项目的结构进行熟悉了解。后续会有更加完善的项目。
  • 由于涉及到mp3外链导入音乐,预览二维码在外链失效时会出错,这里不放预览二维码了;另预览二维码存在有效时长,有兴趣的可以自己试着运行程序,在运行程序之前更换mp3外链的地址,以防出错。mp3外链获取(音乐外链在线获取_音乐外链生成网站_音乐在线听-mp3外链网 (xf1433.com))。
  • 喜欢的可以点个赞,如果有问题或者想要完整的项目代码及素材,评论区有百度云链接。

效果图:

           

  • 三个页面通过轮播图的方式进行切换并实现一定的交互效果。其中,音乐推荐页没有设定交互;播放器页的进度条具有滑动可改变进度;播放列表页具有播放列表音乐的事件;底部的显示区域具有控制音频的交互。
  • 遇到的问题:布局嵌套;初始数据的访问;渲染的嵌套使用;

bug:

  • 播放器页面的播放时长和进度条的显示:页面首次加载获取不到默认曲目的音频时长。按下一首和播放列表进行播放也不显示,按暂停后再按播放就能正常显示。

代码部分:

  • 这里给出项目的代码,没给出代码却存在的文件内容为空。关于素材方面,icon和img分别是图片和图标文件夹;照片是平时拍的,图标在阿里巴巴图标库找的(iconfont-阿里巴巴矢量图标库)。

app.json 

{

  "pages": [

    "pages/index/index"

  ],

  "window": {

    "navigationBarBackgroundColor": "#e0e0e0",

    "navigationBarTextStyle": "black",

    "navigationBarTitleText": "音乐"

  },

  "sitemapLocation": "sitemap.json"

}

 index.wxml

<view class="tap"><!--导航区域-->

  <view class="tap1" bindtap="onRecommend" data-item="0">音乐推荐</view>

  <view class="tap1" bindtap="onPlay" data-item="1">播放器</view>

  <view class="tap1" bindtap="onList" data-item="2">播放列表</view>

</view>

<view class="contain"><!--内容区域-->

  <swiper current="{{item}}">

      <swiper-item><include src="recommend.wxml" /></swiper-item>

      <swiper-item><include src="play.wxml" /></swiper-item>

      <swiper-item><include src="list.wxml" /></swiper-item>

  </swiper>

</view>

<view class="footer"><!--底部区域-->

  <view>

    <view class="footer1">

      <image src="{{playSing.musiccoverImg}}" class="footerImage"></image>

    </view>

    <view class="footer2">

      <view>{{playSing.musicsong}}</view>

      <view>{{playSing.musicsinger}}</view>

    </view>

    <view class="footer3">

      <view>

        <image src="../../icon/菜单.png" bindtap="menuTap" data-item="2"></image>

        <view>

          <image wx:if="{{!isPlayMusic}}" src="../../icon/暂停.png" bindtap="pauseTap"></image>

          <image wx:else src="../../icon/播放.png" bindtap="playTap"></image>

        </view>

        <image src="../../icon/下一曲.png" bindtap="nextTap"></image>

      </view>

    </view>

  </view>

</view>

 index.wxss

page{

  display: flex;flex-direction: column;background: rgb(146, 128, 121);height: 100%;

}

.tap{display: flex;

  background-color: rgb(82, 240, 121);height: 70rpx;

}

.tap1{flex: 1;font-size: 20px;text-align:center;border-bottom: solid 5rpx rgb(50, 9, 235);} 

.contain{flex:1;}

.contain>swiper{height:100%;background-color: rgb(42, 182, 147);}

.recom1{margin-bottom: 60rpx;height: 550rpx;}

.recom1 image{width: 100%;height: 100%;}

.recom2{display: flex;flex-wrap:wrap;margin-bottom: 20rpx;}

.recom2>view{flex: 1;text-align: center;}

.recom2 image{width: 120rpx;height: 100rpx;border: solid 4rpx rgb(120, 223, 197);border-radius: 10%;}

.recom3{display: flex;flex-wrap:wrap;flex: 1;margin-bottom: 20rpx;}

.recom3>view{text-align:center;margin: 10rpx 20rpx;}

.recom3 image{width: 210rpx;height: 140rpx;border-radius: 10%;}

.list{display: flex;flex: 1;flex-direction: column; }

.list>view{border-bottom: solid 3rpx rgb(24, 216, 223);display: flex;}

.list image{width: 100rpx;height: 100rpx;margin: 15rpx;}

.list2{flex: 1;}

.list2_1{diaplaly: flex;}

.list2_1>view:first-child{text-align: left;margin-top: 20rpx;}

.list2_1>view:last-child{flex:1;text-align: right;margin-top: 50rpx;margin-right: 20rpx;}

.play{height:100%;display: flex;flex-direction: column;justify-content: center;}

.play1{text-align: center;margin:20rpx;font-size: 40rpx;}

.play2{text-align: center;flex: 1;}

.play2 image{width: 350rpx;height:350rpx;border-radius: 50%;margin-top: 130rpx;}

.playrotate{animation: animateRotate 10s linear infinite;}

.play3{display: flex;}

.play3 slider{flex: 1;}

.play3>view:first-child{margin: 20rpx 0rpx 20rpx 20rpx;padding:0rpx;}

.play3>view:last-child{margin: 20rpx 20rpx 20rpx 0rpx;padding:0rpx;}

.footer{background-color:rgb(12, 228, 243);font-size: 18px;}

.footer>view{display: flex;}

.footerImage{width: 80rpx;height: 80rpx;margin: 15rpx;}

.footer2{display: flex;flex-direction: column;}

.footer2 view{text-align: center;margin: 5rpx;font-size: 30rpx;}

.footer3{flex: 1;}

.footer3>view{display: flex;justify-content: flex-end;padding-top: 20rpx;}

.footer3 image{width: 70rpx;height: 70rpx;margin: 0rpx 15rpx;}

@keyframes animateRotate{

  from{transform: rotate(0deg);}

  to{transform: rotate(360deg);}

}

 index.js

Page({

  data: {

    item:0,

    isactive:0,

    isPlayMusic:false,

    isrotate:false,

    isplay:true,

    listSing:[

      {id:"1",song:"八月夜桂花",singer:"还潮",coverImg:"../../img/sw1.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107232134/30280d96986673559df9ab931c6bc75b/G174/M02/0D/01/7g0DAF3n4iOAcc9TAD3rmaDghjI270.mp3"},

      {id:"2",song:"晚风",singer:"陈婧霏",coverImg:"../../img/sw2.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107232131/e87b792dd4206b1c082ed389d423512d/G164/M03/04/00/5A0DAF1c-AKAeVXaACsys46W7mQ528.mp3"},

      {id:"3",song:"亲密爱人",singer:"梅艳芳",coverImg:"../../img/sw3.jpg",audioSrc:"http://mp.333ttt.com/mp3music/22851062.mp3"},

      {id:"4",song:"在冬天和奶奶一起晒太阳",singer:"赵照",coverImg:"../../img/sw1.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107232134/7a25da194582ddcf4cf6f856701b50e5/G189/M0A/15/18/nZQEAF5ZEk6AfipaAEPqyN-VYhI072.mp3"},

      {id:"5",song:"晚安",singer:"林宥嘉",coverImg:"../../img/sw3.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107222148/67074aa123d09ee52aa715578b487694/KGTX/CLTX001/c59c04254efa6f54d7bdd675947f68ac.mp3"},

      {id:"6",song:"Sofia",singer:"Alvaro Soler",coverImg:"../../img/sw2.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107231545/a70098ef00c6fdbb3722f0c0a32a12e7/G201/M01/0A/04/aYcBAF5ABeiAL5WOADNmeboY3wg848.mp3"},

      {id:"7",song:"Wild",singer:"Monogem",coverImg:"../../img/sw1.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107231546/2a37951e34e374ad35749dcefa39f28e/G119/M04/04/17/tw0DAFpOGEOAKYizADS68iNCyy4692.mp3"},

      {id:"8",song:"童话镇",singer:"暗杠",coverImg:"../../img/sw3.jpg",audioSrc:"https://sharefs.ali.kugou.com/202107231550/275d29499eba4450e5751bba98ad2563/G062/M03/14/11/fg0DAFd1IvSAJlbkAD0PMS2Gwek596.mp3"}

    ],

    playSing:{

      musicsong:'',

      musicsinger:'',

      musiccoverImg:'',

      currenttime:'00:00',

      totaltime:'00:00',

      percent:0

    }

    

  },

  //可直接访问并赋值属性

  audio:null,

  indexlist:0,

  musicnum:0,

  //顶部栏事件

  onRecommend:function(e){

    this.setData({item:e.target.dataset.item,isactive:0})

  },

  onPlay:function(e){this.setData({item:e.target.dataset.item,isactive:1})},

  onList:function(e){this.setData({item:e.target.dataset.item,isactive:2})},

  //recommend页面事件

  

  //play页面事件

  onMusic:function(index){

    var music = this.data.listSing[index];

    this.audio.src=music.audioSrc;

    this.setData({

      'playSing.musicsong':music.song,

      'playSing.musicsinger':music.singer,

      'playSing.musiccoverImg':music.coverImg,

      'playSing.totaltime':'00:00',

      'playSing.currenttime':'00:00'       

    })

  },

  onSlider:function(e){

    this.audio.seek(e.detail.value/100*this.audio.duration)

    this.setData({

      'playSing.currenttime':formatTime(e.detail.value/100*this.audio.duration),

      'playSing.totaltime':formatTime(this.audio.duration),

      'playSing.percent':this.audio.currentTime/this.audio.duration*100

    })

    function formatTime(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)

    }

  },

  //底部播放栏事件

  menuTap:function(e){

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

  },

  pauseTap:function(){

    this.onAuxiliary()

    this.audio.play()

  },

  playTap:function(){

    this.audio.pause()

    this.setData({isPlayMusic:false,isrotate:false})

  },

  nextTap:function(){

    if(this.musicnum <= (this.indexlist+1)){

      this.indexlist=0;

      this.onMusic(this.indexlist)  //音乐信息

      this.onAuxiliary()             //音乐辅助事件

      this.audio.play()             //播放音乐

      return

    }

    this.onMusic(++this.indexlist)

    this.onAuxiliary()

    this.audio.play()

  },

  //list页面事件

  musicList:function(e){

    //这里对应底部的播放器的变化,同时右边当前播放显示,且音乐播放。

    this.onMusic((e.currentTarget.dataset.listorder)-1)

    this.indexlist=(e.currentTarget.dataset.listorder)-1;

    this.onAuxiliary()

    this.audio.play()

  },

  //辅助函数

  onAuxiliary:function(){   //此辅助函数为添加播放的事件监听

    this.setData({isPlayMusic:true,isrotate:true})

    this.audio.onError(()=>{wx.showToast({    /*在播放的界面里写错误和结束事件 */

      title: '播放地址错误'

    })})

    this.audio.onEnded(()=>{

      this.nextTap()

    })

    /*使用播放事件来时刻显示播放进度 */

    // this.audio.onPlay(()=>{ })

    this.audio.onTimeUpdate(()=>{

      this.setData({

        'playSing.totaltime':formatTime(this.audio.duration),

        'playSing.currenttime':formatTime(this.audio.currentTime),

        'playSing.percent':this.audio.currentTime/this.audio.duration*100

      })

    })

    function formatTime(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)

    }

  },

})

 recommend.wxml

<scroll-view class="recom" scroll-y="true" style="height:100%;">

  <swiper class="recom1" indicator-dots="true" autoplay circular>

    <swiper-item><image src="../../img/sw1.jpg" mode="aspectFill"></image></swiper-item>

    <swiper-item><image src="../../img/sw2.jpg" mode="aspectFill"></image></swiper-item>

    <swiper-item><image src="../../img/sw3.jpg" mode="aspectFill"></image></swiper-item>

  </swiper>

  <view class="recom2">

    <view> 

      <image src="../../img/sw1.jpg"></image>

      <view>私人FM</view>

    </view>

    <view>

      <image src="../../img/sw11.jpg"></image>

      <view>每日歌曲推荐</view>

    </view>

    <view>

      <image src="../../img/sw3.jpg"></image>

      <view>云音乐新歌榜</view>

    </view>

  </view>

  <view style="margin-left:20rpx;margin-bottom:10rpx;">热门音乐</view>

  <view class="recom3">

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>紫罗兰</view>

    </view>

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>五月之家</view>

    </view>

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>菩提树</view>

    </view>

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>紫罗兰</view>

    </view>

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>五月之家</view>

    </view>

    <view class="recom3_1">

      <image src="../../img/sw11.jpg"></image>

      <view>菩提树2</view>

    </view>

  </view>

</scroll-view>

play.wxml

<view class="play">

   <view class="play1">

      <view>{{playSing.musicsong}}</view>

      <view>--{{playSing.musicsinger}}--</view>

   </view>

   <view class="play2">

      <image src="{{playSing.musiccoverImg}}" class="{{isrotate?'playrotate':'pauserotate'}}"></image>

   </view>

   <view class="play3">

      <view>{{playSing.currenttime}}</view>

      <slider block-size="12" value="{{playSing.percent}}" bindchange="onSlider"></slider>

      <view>{{playSing.totaltime}}</view>

   </view>

</view>

list.wxml

<scroll-view scroll-y="true" style="height:100%">

  <view class="list">

    <view wx:for="{{listSing}}" wx:key="id" wx:for-item="item1">

      <view class="list1">

        <image src="{{item1.coverImg}}" mode="aspectFill"></image>

      </view>

      <view class="list2">

        <view bindtap="musicList" class="list2_1" data-listorder="{{item1.id}}">

        <view class="list3">

          <view>{{item1.song}}</view>

          <view>{{item1.singer}}</view>

        </view>

        <view class="list3 list3-{{isplaycolor ? 'iscolor' : ''}}"><view wx:if="{{true}}">点击播放</view></view>

      </view>

      </view>

    </view>

  </view>

  <view style="text-align:center;font-size:40rpx;">待更新~</view>

</scroll-view>

  • 16
    点赞
  • 157
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值