微信小程序实现自定义音乐播放(定时、进度条)

实现关键:

1、wx.getBackgroundAudioManager() 

2、在app.json中配置:"requiredBackgroundModes": ["audio"]

界面

具体实现步骤:

======****进度条组件

wxml

<view class="circle_box" style="{
   
   {size}}px;height:{
   
   {size}}px">
    <!-- <canvas class="circle_bg" canvas-id="{
   
   {draw}}bg" style="{
   
   {size + 10}}px;height:{
   
   {size + 10}}px"></canvas>  -->
    <canvas class="circle_draw" canvas-id="{
   
   {draw}}" style="{
   
   {size }}px;height:{
   
   {size }}px"></canvas> 
    <!-- <text class='circle_txt'> {
   
   {txt}}%  </text>   -->
</view>

wxss 

.circle_box,.circle_draw{ 
  width: 100%;
  top: 50%;
  left:50%;
  transform: translate(-50%,-50%);
  position: absolute; 
}
.circle_bg{
  width: 100%;
  top: 50%;
  left:50%;
  transform: translate(-50%,-50%);
  position: absolute;
}
.circle_box{
  top: 50%;
  left:50%;
  transform: translate(-50%,-50%);
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  position: absolute;
  width: 100%;
  z-index: 1001;
}
.circle_txt{
  position: absolute;
  font-size: 28rpx;
}

js 

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
  properties: {
    draw: {//画板元素名称id
      type: String,
      value: 'draw'
    },
    per: { //百分比 通过此值转换成step
      type: String,
      value: '0'
    },
    r: {//半径
      type: String,
      value: '50'
    }

  },

  observers: {
    "per": function (val) {
      // this.attached()
      this.init()
    }
  },

  data: { /*  私有数据,可用于模版渲染 */
    step: 1, //用来算圆的弧度0-2
    size: 0, //画板大小
    screenWidth: 750, //实际设备的宽度
    txt: 0
  },
  methods: {
    // 初始化
    init() {
      const _this = this;
      //获取屏幕宽度
      wx.getSystemInfo({
        success: function (res) {
          _this.setData({
            screenWidth: res.windowWidth
          });
        },
      });
      //初始化
      const el = _this.data.draw; //画板元素
      const per = _this.data.per; //圆形进度
      const r = Number(_this.data.r); //圆形半径

      _this.setData({
        step: (2 * Number(_this.data.per)) / 100,
        txt: _this.data.per
      });


      //获取屏幕宽度(并把真正的半径px转成rpx)
      let rpx = (_this.data.screenWidth / 750) * r;
      //计算出画板大小
      this.setData({
        size: rpx * 2
      });
      const w = 4;//圆形的宽度
      // console.log("step", _this.data.step)

      //组件入口,调用下面即可绘制 背景圆环和彩色圆环。
      _this.drawCircleBg(el + 'bg', rpx, w);//绘制 背景圆环
      _this.drawCircle(el, rpx, w, _this.data.step);//绘制 彩色圆环
    },

    /**
     * el:画圆的元素
     * r:圆的半径
     * w:圆的宽度
     * 功能:画背景
     */
    drawCircleBg: function (el, r, w) {
      const ctx = wx.createCanvasContext(el, this);
      ctx.setLineWidth(w);// 设置圆环的宽度
      ctx.setStrokeStyle('#E5E5E5'); // 设置圆环的颜色
      ctx.setLineCap('round') // 设置圆环端点的形状
      ctx.beginPath();//开始一个新的路径
      ctx.arc(r, r, r - w, 0, 2 * Math.PI, false);
      //设置一个原点(110,110),半径为100的圆的路径到当前路径
      ctx.stroke();//对当前路径进行描边
      ctx.draw();

    },
    /**
 * el:画圆的元素
 * r:圆的半径
 * w:圆的宽度
 * step:圆的弧度 (0-2)
 * 功能:彩色圆环
 */
    drawCircle: function (el, r, w, step) {
      var context = wx.createCanvasContext(el, this);
      // 设置渐变
      var gradient = context.createLinearGradient(2 * r, r, 0);
      gradient.addColorStop("0", "#F1CAAD");
      gradient.addColorStop("0.5", "#F1CAAD");
      gradient.addColorStop("1.0", "#F1CAAD");
      context.setLineWidth(w);
      context.setStrokeStyle(gradient);
      context.setLineCap('round')
      context.beginPath();//开始一个新的路径
      // step 从0到2为一周
      context.arc(r, r, r - w, -Math.PI / 2, step * Math.PI - Math.PI / 2, false);
      context.stroke();//对当前路径进行描边
      context.draw()
    }

  },

  lifetimes: {
    // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
    attached: function () {
      this.init()
    }

  }


})

json 

{
    "component": true,
    "usingComponents": {}
}

======****调用部分

wxml

 <view class="playBox">
        <circle draw='circwewle1' class="mycanvas" per="{
   
   { percentage }}" r='242' />
        <image mode="aspectFill" class="playbg {
   
   {isPlay&&'disc-animate'}}" src="{
   
   { detailObj.playerdiagram }}"></image>
        <image mode="aspectFill"
            src="{
   
   {  isPlay ? 'https://laikangland-dev.oss-cn-beijing.aliyuncs.com/202209081030199l40.png?Expires=2293324220&OSSAccessKeyId=LTAIykCrXSP1fmei&Signature=Tc7RjNRt7VnPQdFxPgtob76AVE0%3D' : 'https://laikangland-dev.oss-cn-beijing.aliyuncs.com/20220908103053X31u.png?Expires=2293324253&OSSAccessKeyId=LTAIykCrXSP1fmei&Signature=eIFRbPEqzCCtptvNtb3Y3VJER%2Bg%3D' }}"
            data-src="{
   
   { detailObj.resUrl }}" class="play" catchtap="playMusic"></image>
    </view>

less 

 .playBox {
        width: 484rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        height: 484rpx;
        border-radius: 50%;
        margin-bottom: 140rpx;
        position: relative;
        // overflow: hidden;
        // border: 8rpx solid #F1CAAD;
        .playbg {
            width: 475rpx;
            height: 475rpx;
            // top: 50%;
            // left: 50%;
            position: absolute;
            transform: rotate(0deg)
            // translate(-50%,-50%)
        }

        .disc-animate {
            animation: rotate 3s 0s linear infinite;
        }

        .play {
            width: 96rpx;
            height: 96rpx;
            z-index: 1002;
            border-radius: 50%;
        }
    }

json 

{
    "usingComponents": {
      "circle": "/components/circle/circle"
    },
    "navigationStyle":"custom"
  }

js

1、在onLoad中创建播放对象

data: {
        detailObj: null,
        tabIndex: null,
        tab: [15, 30, 60, 90, 120],
        isPlay: false,//是否播放
        countdown: null, //倒计时
        percentage: null //当前进度
},
 onLoad(options) {
        let platform;
        wx.getSystemInfo({
            success: function (res) {
                platform = res.platform
            }
        });
        this.platform = platform
        let detailObj = JSON.parse(decodeURIComponent(options.item))
        this.data.audioCtx = wx.getBackgroundAudioManager()
        this.data.audioCtx.title = detailObj.name
        this.setData({ customTopHeiht: app.globalData.customTopHeiht, detailObj, showTip: detailObj.announcements ? true : false })
        // 创建播放对象
        this.createPlay()
    },
onShow() {

        // this.createPlay()
        if (wx.getStorageSync("nowTime")) {
            this.newTime = wx.getStorageSync("nowTime")
            let tabIndex = Number(wx.getStorageSync("tabIndex"))
            this.setData({ tabIndex })
            this.countDown(this.data.tab[tabIndex])
        }else {
            this.setData({ tabIndex: null })
        }
        if(wx.getStorageSync("fmtCurrentTime")) {
            this.fmtCurrentTime  = wx.getStorageSync("fmtCurrentTime")
        }
    },

2、将播放的步骤封装成一个方法

注意事项:

1、代码中的src为需要播放的地址

2、为了防止中文的地址在ios中无法播放,需要对其通过encodeURIComponent进行转码处理

3、必须设置title,否则无法播放

  // 创建播放音乐对象
     createPlay() {
        let src = this.data.detailObj.resUrl
        src = src.indexOf('ai-algorithm-nlp') >= 0 ? src.slice(0, src.indexOf('ai-algorithm-nlp') + 6) + encodeURIComponent(src.slice(src.indexOf('ai-algorithm-nlp') + 6)) : src;
        this.data.audioCtx.src = src
        if (app.globalData.tabIndex || app.globalData.tabIndex == 0) {
            this.setData({ isPlay: true, tabIndex: app.globalData.tabIndex })
        }
        if(wx.getStorageSync('nowTime') && (this.data.countdown&&this.data.countdown.sec <= 0)) {
            this.setData({ tabIndex: null })
            this.data.audioCtx.stop()
        }else {
            setTimeout(() => {
                this.data.audioCtx.play()
            }, 300)
        }
        
        // 监听音乐播放
        this.data.audioCtx.onPlay(() => {
            this.handleList(true)
            this.setData({ isPlay: true })
        })
        // 监听音乐暂停
        this.data.audioCtx.onPause(() => {
            this.handleList(false)
            this.setData({ isPlay: false })
        })
        // 监听音乐停止
        this.data.audioCtx.onStop(() => {
            this.handleList(false)
            this.setData({ isPlay: false })
        })
        // 监听音乐结束
        this.data.audioCtx.onEnded(() => {
            console.log("音乐结束")
            // 循环播放
            this.data.audioCtx.pause()
            this.data.audioCtx.play()
        })
        // 监听播放进度
        this.data.audioCtx.onTimeUpdate(() => {
            let fmtCurrentTime = this.fmtCurrentTime ? this.fmtCurrentTime : moment(this.data.audioCtx.currentTime * 1000).format("mm");
            let tabIndex = this.data.tabIndex
            let time = this.data.tab[tabIndex]
            app.globalData.tabIndex = tabIndex
            wx.setStorageSync("fmtCurrentTime",fmtCurrentTime)
            let percentage = (this.data.audioCtx.currentTime/this.data.audioCtx.duration)*100
            this.setData({ percentage: percentage+''})
            // console.log("percentage",percentage)
            if(this.fmtCurrentTime && wx.getStorageSync('nowTime') && this.data.countdown) {
                if (Number(this.data.countdown.sec) <= 0) {
                    // 定时结束
                    console.log("定时结束")
                    this.setData({ isPlay: false,tabIndex: null })
                    this.data.audioCtx.stop()
                }
            }else {
                if (wx.getStorageSync('nowTime')&&Number(fmtCurrentTime) >= time) {
                    // 定时结束
                    console.log("定时结束")
                    this.setData({ isPlay: false,tabIndex: null })
                    this.data.audioCtx.stop()
                }
            }
            
            
        })
        if (this.data.detailObj.isPlay) {
            this.data.audioCtx.src = this.data.detailObj.resUrl
            // this.data.audioCtx.pause()
            this.data.audioCtx.play()
            this.setData({ isPlay: true })
        }
    },

3、处理播放按钮点击时对应的事件

  // 播放音乐
    playMusic(e) {
        this.setData({ isPlay: !this.data.isPlay })
        // let src = e.currentTarget.dataset.src
        // src = src.indexOf('ai-algorithm-nlp') >= 0 ? src.slice(0,src.indexOf('ai-algorithm-nlp')+6) + encodeURIComponent(src.slice(src.indexOf('ai-algorithm-nlp') + 6 )) : src;
        // this.data.audioCtx.src = src
        // this.data.audioCtx.src = 'https://video.laikang.com/68514b723d30472ba691c9b91b529631/4b8b9e29983c41848d01e173a115bc68-a186ac753c224d0a3647598c3ba3aab6-ld.mp4'
        if (this.data.isPlay) {
            this.data.audioCtx.pause()
            this.data.audioCtx.play()

        } else {
            this.setData({ isPlay: false })
            this.data.audioCtx.pause()
        }

    },

4、处理定时弹框关闭对应事件

 know() {
        if (this.data.isWho != 1) {
            clearInterval(this.data.timer)
            this.setData({ countdown: null })
            this.newTime = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
            wx.setStorageSync("nowTime", this.newTime); //定时
            wx.setStorageSync("tabIndex", this.data.tabIndex);
            this.countDown(this.data.tab[this.data.tabIndex])
        }
        this.setData({ isWho: null })
    },

 4、处理定时时间切换事件

 change(e) {
        let tabIndex = e.currentTarget.dataset.index
        app.globalData.tabIndex = tabIndex
        this.setData({ tabIndex })
    },

 5、处理定时器以及倒计时回显

 countDown: function (time) {
        let that = this,
            countdown = this.data.countdown
        // let num = 0;
        clearInterval(this.data.timer)
        that.data.timer = setInterval(function () {
            let createdTime = that.newTime
            if (that.platform == 'ios') {
                createdTime = createdTime.replace(/-/g, '/')
            }
            // 30分钟-半小时
            let lastTime = new Date(new Date(createdTime).getTime() + (1 / 60) * time * 3600 * 1000)
            let nowTime = new Date()
            let flag = that.compareDate(lastTime,nowTime)
            if (flag > 0) {
                countdown = that.myFunction(nowTime,lastTime)
            } else {
                countdown = null
                // 定时结束清除定时时间
                wx.removeStorageSync("fmtCurrentTime")
                wx.removeStorageSync('nowTime')
                wx.removeStorageSync('tabIndex')
                clearInterval(that.data.timer)
            }
            that.setData({ countdown })
        }, 1000)
    },
    myFunction(startDate, endDate) {
        let min = Math.floor((new Date(endDate).getTime() - new Date(startDate).getTime()) / 60000)
        let sec = Math.floor((new Date(endDate).getTime() - new Date(startDate).getTime()) / 1000)
        let tmp = min <= 0 ? sec : sec%60 >= 0 ?sec%60 : sec
        let str = min > 0 ? `${min}:${ tmp }` : `${min}:${sec }`
        return { str,sec }
    },

    // 比较两个日期的小
    compareDate(date1, date2) {
        let tmp1 = date1.getTime();
        let tmp2 = date2.getTime();
        return tmp1 - tmp2
    },

猜你喜欢

转载自blog.csdn.net/qq_41687299/article/details/126828932
今日推荐