WeChat applet implements music player (5)

Recap

pubsubjs implements page communication

  1. npm install --save pubsub-js, install the third-party library pubsub-js.
  2. import PubSub from "pubsub-js", introduce the third-party library pubsub.
  3. Subscribe message: id = PubSub.subscribe(消息名,callback), and the first formal parameter of callback is the message name by default, followed by the parameters passed in by the developer.
  4. Release message: PubSub.publish(消息名,开发者的参数).
  5. Unsubscribe: PubSub.unsubscribe(id).

moment.js implements time formatting

This example uses the third-party library moment.js to format the time.

  1. Install dependencies. npm install --save moment.
  2. In the WeChat developer tools, Tools > Build npm .
  3. Introduce and use moment in the page.
    import moment from "moment";
    moment(时间).format(格式);

BackgroundAudioManager

BackgroundAudioManager, is the only global background audio manager. The applet cuts into the background, and if the audio is playing, it can continue playing. But the background state cannot manipulate the playback state of the audio by calling the API. Starting from version 6.7.2 of the WeChat client, if you need the Mini Program to continue playing audio after switching to the background, you need to configure properties in
the global configuration file , such as: .app.jsonrequiredBackgroundModes"requiredBackgroundModes": ["audio"]

BackgroundAudioManagerInstances can be wx.getBackgroundAudioManager()obtained . BackgroundAudioManagerInstances have the following properties and methods:

  1. title, audio title, required .
  2. src, the audio data source, the default value is an empty string, not required. However, the audio will only play automatically if it is setsrc .
  3. duration, the length of the current audio, the type of number, and the unit is s, that is, seconds.
  4. currentTime, the current playback position of the audio, the type of number, the unit is s, that is, seconds.
  5. play(),play music.
  6. pause(), to pause the music.
  7. onPlay(function callback), listen for background audio playback events.
  8. onPause(function callback), to listen for background audio pause events.
  9. onStop(function callback), to listen for background audio stop events.
  10. onEnded(function callback), to monitor the background audio natural playback end event.
  11. onTimeUpdate(function callback), to monitor background audio playback progress update events.

applet project

The main files involved in the code are:

  1. app.json
  2. app.wxss
  3. app.js
  4. pages/index/index.json
  5. pages/index/index.wxml
  6. pages/index/index.wxss
  7. pages/index/index.js
  8. pages/music/music.json
  9. pages/music/music.wxml
  10. pages/music/music.wxss
  11. pages/music/music.js

insert image description here

app.json

{
    
    
  "pages": [
    "pages/index/index",
    "pages/music/music"
  ],
  "window": {
    
    
    "navigationBarBackgroundColor": "#624d2e",
    "navigationBarTitleText": "首页",
    "navigationBarTextStyle": "white"
  },
  "requiredBackgroundModes": [
    "audio"
  ],
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

app.wxss

page{
    
    
  height: 100%;
}

app.js

App({
    
    
  globalData:{
    
    
    isPlayGlobal:false,   //当前是否有歌曲在播放
    musicIdGlobal:''     //当前正在播放的歌曲是哪首
  }
})

pages/index/index.json

{
    
    
  "usingComponents": {
    
    },
  "navigationBarTitleText": "播放列表"
}

pages/index/index.wxml

<view class="index-container">
  <view class="header">
    <image src="/static/images/icon-play-square.png"></image>
    <text>播放全部</text>
    <text>{
   
   {musicList.length}}</text>
  </view>
  <scroll-view class="music-list" enable-flex scroll-y >
    <view class="music-item" wx:for="{
     
     {musicList}}" wx:key="id" bindtap="handleTap" data-musicindex="{
     
     {index}}" data-musicitem="{
     
     {item}}">
      <view class="music-index">{
   
   {index+1}}</view>
      <image class="music-image" src="{
     
     {item.picUrl}}"></image>
      <view class="music-info">
        <view class="musci-name">{
   
   {item.name}}</view>
        <view class="music-author">{
   
   {item.author}}</view>
      </view>
      <image class="btn-more" src="/static/images/icon-more.png"></image>
    </view>
  </scroll-view>
</view>

pages/index/index.wxss

.index-container{
    
    
  padding: 20rpx;
  background:#624d2e
}
.header{
    
    
  display: flex;
  align-items: center;
}
.header image{
    
    
  width: 28rpx;
  height: 28rpx;
  margin-right: 20rpx;
}
.header text{
    
    
  color: #fff;
  height: 28rpx;
  line-height: 28rpx;
  margin-right: 20rpx;
  font-size: 28rpx;
}
.music-list{
    
    
  margin-top: 20rpx;
  color: #fff;
  height: calc(100vh - 88rpx);
}
.music-item{
    
    
  display: flex;
  align-items: center;
  height: 100rpx;
  margin: 20rpx 0;
  position: relative;
}
.music-item .music-index{
    
    
  width: 50rpx;
  font-size: 28rpx;
  color: #aaa;
}
.music-item .music-image{
    
    
  width: 80rpx;
  height: 80rpx;
  border-radius: 6rpx;
  margin-right: 20rpx;
}
.music-item .music-info{
    
    
  display: flex;
  flex-direction: column;
}
.music-item .music-info .music-author{
    
    
  font-size: 24rpx;
  color: #aaa;
  margin-top: 10rpx;
}
.music-item .btn-more{
    
    
  width: 36rpx;
  height: 36rpx;
  position: absolute;
  right: 0;
}

pages/index/index.js

import PubSub from "pubsub-js";
const host = "http://localhost:3000";

Page({
    
    
  data:{
    
    
    musicList:[], //歌曲列表
    musicindex:0  //进入某首歌曲后,记录该歌曲在列表中的索引
  },
  onLoad(){
    
    
    this.getDataFromServer();

    //接收消息,接收来自页面pages/music/music的消息,根据“上一首”or“下一首”,确定当前应该显示哪首歌曲
    PubSub.subscribe("switchsong",(msgName,type) => {
    
    
      // console.log(msgName,type);
      let {
    
    musicindex,musicList} = this.data;
      if(type === "prev"){
    
    
        if(musicindex===0) {
    
    
          musicindex = musicList.length-1;
        }else{
    
    
          musicindex--;
        }
      }else if(type === "next"){
    
    
        if(musicindex === musicList.length-1){
    
    
          musicindex = 0;
        }else{
    
    
          musicindex++;
        }
      }
      this.setData({
    
    musicindex});
      const music = musicList[musicindex];
      //发送消息,告诉页面pages/music/music,告诉它切换完成后的歌曲的详细消息。
      PubSub.publish("refreshmusic",music);
    })
  },
  getDataFromServer(){
    
    
    const result = [
      {
    
    id:"001",name:"滂沱大雨里","author":"李若溪","picUrl":host+"/images/滂沱大雨里.jpg","url":host+"/audios/滂沱大雨里.mp3",duration:161000},
      {
    
    id:"002",name:"Last Dance","author":"伍佰","picUrl":host+"/images/Last Dance.jpg","url":host+"/audios/Last Dance.mp3",duration:271000},
      {
    
    id:"003",name:"国王与乞丐","author":"华晨宇","picUrl":host+"/images/国王与乞丐.jpg","url":host+"/audios/国王与乞丐.mp3",duration:178000},
      {
    
    id:"004",name:"奇洛李维斯回信","author":"薛凯琪","picUrl":host+"/images/奇洛李维斯回信.jpg","url":host+"/audios/奇洛李维斯回信.mp3",duration:243000},
      {
    
    id:"005",name:"红日","author":"李克勤","picUrl":host+"/images/红日.jpg","url":host+"/audios/红日.mp3",duration:291000},
      {
    
    id:"006",name:"快乐崇拜","author":"潘玮柏 张韶涵","picUrl":host+"/images/快乐崇拜.jpg","url":host+"/audios/快乐崇拜.mp3",duration:329000},
      {
    
    id:"007",name:"门没锁","author":"黄品冠","picUrl":host+"/images/门没锁.jpg","url":host+"/audios/门没锁.mp3",duration:233000},
      {
    
    id:"008",name:"就是爱你","author":"陶喆","picUrl":host+"/images/就是爱你.jpg","url":host+"/audios/就是爱你.mp3",duration:340000},
      {
    
    id:"009",name:"快乐的小青蛙","author":"刘士鸣","picUrl":host+"/images/快乐的小青蛙.jpg","url":host+"/audios/快乐的小青蛙.mp3",duration:130000},
      {
    
    id:"0010",name:"友情岁月","author":"陈小春 郑伊健 谢天华 林晓峰 钱嘉乐","picUrl":host+"/images/友情岁月.jpg","url":host+"/audios/友情岁月.mp3",duration:209000},
      {
    
    id:"0011",name:"温柔","author":"五月天","picUrl":host+"/images/温柔.jpg","url":host+"/audios/温柔.mp3",duration:269000}
    ];
    this.setData({
    
    musicList:result});
  },
  handleTap(event){
    
    
    const {
    
    musicitem,musicindex} = event.currentTarget.dataset;
    this.setData({
    
    musicindex})
    wx.navigateTo({
    
    
      url: '/pages/music/music?musicitem='+JSON.stringify(musicitem),
    })
  }
})

pages/music/music.json

{
    
    
  "usingComponents": {
    
    },
  "navigationBarBackgroundColor": "#2f434e",
  "navigationBarTitleText": "音乐详情"
}

pages/music/music.wxml

<view class="music-container">
  <view class="music-name">{
   
   {music.name}}</view>
  <view class="music-author">{
   
   {music.author}}</view>
  <image class="arm {
     
     {isPlay&&'arm-reset'}}" src="/static/images/arm.png"></image>
  <view class="disc-container {
     
     {isPlay&&'disc-animate'}}">
    <image class="disc" src="/static/images/disc.png"></image>
    <image class="music-image" src="{
     
     {music.picUrl}}"></image>
  </view>

  <view class="progress-container">
    <text class="current-time">{
   
   {fmtCurrentTime}}</text>
    <view class="bar-box">
      <view class="current-progress" style="width:{ 
        { 
        currentWidth}}rpx">
        <view class="progress-circle"></view>
      </view>
    </view>
    <text class="duration">{
   
   {fmtDuration}}</text>
  </view>

  <view class="player">
    <view class="btns">
      <image class="loop-btn" src="/static/images/loop.png"></image>
      <image class="prev-btn" src="/static/images/prev.png" id="prev" bindtap="handleSwitch"></image>
      <image class="play-btn" src="{
     
     {isPlay?'/static/images/stop.png':'/static/images/play.png'}}" bindtap="handlePlay"></image>
      <image class="next-btn" src="/static/images/next.png" id="next" bindtap="handleSwitch"></image>
      <image class="list-btn" src="/static/images/list.png"></image>
    </view>
  </view>
</view>

pages/music/music.wxss

.music-container{
    
    
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: #2f434e;
  position: relative;
}
.music-container .music-name{
    
    
  margin: 10rpx 0;
  color: #fff;
  font-size: 36rpx;
}
.music-container .music-author{
    
    
  color: #bbb;
  font-size: 28rpx;
  margin: 6rpx 0;
}
.music-container .arm{
    
    
  width:204rpx;
  height: 358rpx;
  position: relative;
  left: 72rpx;
  z-index: 99;
  transform: rotate(-15deg);
  transform-origin: 30rpx 30rpx;
  transition: transform .7s linear;
}
.disc-container{
    
    
  position: relative;
  top: -128rpx;
  width: 490rpx;
  height: 490rpx;
}
.disc-container .disc{
    
    
  width: 100%;
  height: 100%;
}
.disc-container .music-image{
    
    
  width: 270rpx;
  height: 270rpx;
  border-radius: 100%;
  position: absolute;
  left: 0;right: 0;top: 0;bottom: 0;
  margin: auto;
}
.music-container .arm-reset{
    
    
  transform: rotate(0deg);
}
.disc-animate{
    
    
  animation: rotate 2.5s 1s linear infinite;
}
@keyframes rotate{
    
    
  from{
    
    
    transform: rotate(0deg);
  }
  to{
    
    
    transform: rotate(360deg);
  }
}
.player{
    
    
  width: 100%;
  position: absolute;
  bottom: 60rpx;
}
.btns{
    
    
  display: flex;
  align-items: center;
  justify-content: space-evenly;
}
.btns image{
    
    
  width: 36rpx;
  height: 36rpx;
}
.btns .play-btn,.btns .stop-btn{
    
    
  width: 90rpx;
  height: 90rpx;
}
.progress-container {
    
    
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
.progress-container .bar-box{
    
    
  width: 456rpx;
  height: 4rpx;
  line-height: 4rpx;
  background: #888;
}
.bar-box .current-progress{
    
    
  position: relative;
  height: 4rpx;
  line-height: 4rpx;
  background: #fff;
}
.progress-circle{
    
    
  width: 16rpx;
  height: 16rpx;
  border-radius: 50%;
  background: #fff;
  position: absolute;
  right: -16rpx;
  top: -6rpx;
}
.progress-container text{
    
    
  color: #ccc;
  font-size: 22rpx;
  margin: 0 40rpx;
}

pages/music/music.js

import PubSub from "pubsub-js";
import moment from 'moment';
const appInstance = getApp();

Page({
    
    
  data:{
    
    
    isPlay:false,
    music:{
    
    },
    fmtDuration:"00:00",
    fmtCurrentTime:"00:00",
    currentWidth:0
  },
  onLoad(options){
    
    
    const music = JSON.parse(options.musicitem);
    let fmtDuration = moment(music.duration).format("mm:ss");
    this.setData({
    
    music,fmtDuration})

    const {
    
    isPlayGlobal,musicIdGlobal} = appInstance.globalData;
    const {
    
    id} = this.data.music;
    if(isPlayGlobal && musicIdGlobal === id) {
    
    
      this.setData({
    
    isPlay:true});
    }
  
    this.bam = wx.getBackgroundAudioManager();
    this.bam.onPlay(() => {
    
    
      this.setData({
    
    isPlay:true})
      appInstance.globalData.isPlayGlobal = true;
      appInstance.globalData.musicIdGlobal = this.data.music.id;
    })
    this.bam.onPause(() => {
    
    
      this.setData({
    
    isPlay:false})
      appInstance.globalData.isPlayGlobal = false;
    })
    this.bam.onStop(() => {
    
    
      this.setData({
    
    isPlay:false})
      appInstance.globalData.isPlayGlobal = false;
    })
    this.bam.onEnded(() => {
    
    
      // this.setData({isPlay:false});
      // appInstance.globalData.isPlayGlobal = false;

      // 音乐自然播放结束,则切换至下一首
      PubSub.publish("switchsong","next");
      //接收消息,接收来自pages/index/index的消息,显示切换后的歌曲详情
      const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
    
    
        PubSub.unsubscribe(eventId);
        this.setData({
    
    music})
        this.musicControl();

        const fmtDuration = moment(this.data.music.duration).format("mm:ss");
        this.setData({
    
    fmtDuration,fmtCurrentTime:"00:00"})
      })  
    })
    this.bam.onTimeUpdate(() => {
    
    
      const currentWidth = Math.floor(456 * this.bam.currentTime / this.bam.duration);
      const fmtCurrentTime = moment(this.bam.currentTime * 1000).format("mm:ss");
      this.setData({
    
    currentWidth,fmtCurrentTime});
    })
  },
  handlePlay(){
    
    
    const isPlay = !this.data.isPlay;
    this.setData({
    
    isPlay});
    this.musicControl();
  },
  musicControl(){
    
    
    const {
    
    isPlay} = this.data;
    if(isPlay){
    
    
      this.bam.src = this.data.music.url;
      this.bam.title = this.data.music.name;
    }else{
    
    
      this.bam.pause();
    }
  },
  handleSwitch(event){
    
    
    const type = event.target.id;
    //发送消息,告诉pages/index/index:切上一首还是下一首。prev代表切上一首,next代表切下一首。
    PubSub.publish("switchsong",type);

    //接收消息,接收来自pages/index/index的消息,显示切换后的歌曲详情
    const eventId = PubSub.subscribe("refreshmusic",(msgName,music) => {
    
    
      PubSub.unsubscribe(eventId);
      this.setData({
    
    music})
      this.musicControl();
    })
  }
})

Related Links

moment.js Chinese document
pubsubjs
WeChat applet realizes music player (4) (using pubsubjs to realize inter-page communication)

Guess you like

Origin blog.csdn.net/qzw752890913/article/details/125973413