微信小程序—InnerAudioContext实现一个音乐播放器

微信小程序 专栏收录该内容
51 篇文章 7 订阅

直接上效果:

在这里插入图片描述

体验:

自定义系列>>音乐播放器
在这里插入图片描述
功能描述:
1、可播放、暂停内置的一条音乐
2、可控制播放进度
3、音乐封面在播放过程中实现旋转动画
4、背景图片表现为音乐封面的高斯模糊
5、离开页面音乐停止播放

完整代码:

js

const app = getApp()
const innerAudioContext = wx.createInnerAudioContext();
var _animation; // 动画实体
var _animationIndex = 0; // 动画执行次数index(当前执行了多少次)
var _animationIntervalId = -1; // 动画定时任务id,通过setInterval来达到无限旋转,记录id,用于结束定时任务
const _ANIMATION_TIME = 200; // 动画播放一次的时长ms
Page({
  data: {
    menuButtonBoundingClientRect:{},
    systemInfo:{},
    clickPlay: true,
    playSrc: 'play-normal.png',
    preSrc: './pre-normal.png',
    nextSrc: './next-normal.png',
    animation: '',
    isRing: false,

    audiolist: [
      {
        audiosrc: 'https://6e6f-normal-env-4598.tcb.qcloud.la/mp3/04a9_485d_772f_2493d24b7f1b040256002d8283a4cad0.mp3?sign=b4889da8c5373cad5279086359f27fcc&t=1583118236',
  audioImage:"cloud://normal-env-t6f-normal-etac-1300924598/meinv/u=2367451530,795833927&fm=26&gp=0.jpg",audioName:"上海滩",
      }
    ],
    isPlayAudio: false,
    audioSeek: 0,
    audioDuration: 0,
    showTime1: '00:00',
    showTime2: '00:00',
    audioTime: 0,
    textVoiceMarginTop: '',
    viewCircleContainerMarginTop: '',
    audioPlayerMarginTop: '',
    controlContainerMarginTop: '',
  },
  go2Home() {
    wx.navigateBack({
      delta: 1,
    })
  },
  onLoad: function (options) {
    var that = this
    wx.getSystemInfo({
      success: function (res) {
        //model中包含着设备信息
        console.log(res)
        that.setData({
          menuButtonBoundingClientRect: wx.getMenuButtonBoundingClientRect(),
          systemInfo:res
        })
        var model = res.model
        if (model.search('iPhone X') != -1) {
          app.globalData.isIpx = true;
        } else {
          app.globalData.isIpx = false;
        }
      }
    })
    console.log(app.globalData.isIpx)
    let isPhone = app.globalData.isIpx;
    if (isPhone) {
      this.setData({
        textVoiceMarginTop: "105rpx",
        viewCircleContainerMarginTop: "100rpx",
        audioPlayerMarginTop: "200rpx",
        controlContainerMarginTop: "100rpx",
      })
    } else {
      this.setData({
        textVoiceMarginTop: "60rpx",
        viewCircleContainerMarginTop: "80rpx",
        audioPlayerMarginTop: "140rpx",
        controlContainerMarginTop: "30rpx"
      })
    }
  },
  touchPreStart: function () {
    var preSrc = this.data.preSrc;
    this.setData({
      preSrc: './pre-normal.png'
    })
  },
  touchPreEnd: function () {
    var preSrc = this.data.preSrc;
    this.setData({
      preSrc: './pre-normal.png'
    })
  },
  touchNextStart: function () {
    var nextSrc = this.data.nextSrc;
    this.setData({
      nextSrc: './next-normal.png'
    })
  },
  touchNextEnd: function () {
    var nextSrc = this.data.nextSrc;
    this.setData({
      nextSrc: './next-normal.png'
    })
  },
  changePlay: function () {
    var playSrc = this.data.playSrc;
    var isPlayAudio = this.data.isPlayAudio;
    this.playAudio();
    if (!isPlayAudio) {
      this.startAnimationInterval()
      this.setData({
        playSrc: './pause-normal.png',
      })
    } else {
      this.stopAnimationInterval()
      this.setData({
        playSrc: './play-normal.png',
      })
    }
  },
  onReady: function () {
    _animationIndex = 0;
    _animationIntervalId = -1;
    this.data.animation = '';
  },
  onShow: function () {
    this.Initialization();
    this.loadaudio();
    _animation = wx.createAnimation({
      duration: _ANIMATION_TIME,
      timingFunction: 'linear', // "linear","ease","ease-in","ease-in-out","ease-out","step-start","step-end"
      delay: 0,
      transformOrigin: '50% 50% 0'
    })

  },
  onHide: function () {
    this.isShow = false;
  },
  /**
   * 实现image旋转动画,每次旋转 120*n度
   */
  rotateAni: function (n) {
    _animation.rotate(5 * (n)).step()
    this.setData({
      animation: _animation.export()
    })
  },

  /**
   * 开始旋转
   */
  startAnimationInterval: function () {
    var that = this;
    if (that.data.isRing) {
      return
    }
    that.setData({
      isRing: true
    })
    that.rotateAni(++_animationIndex); // 进行一次旋转
    _animationIntervalId = setInterval(function () {
      that.rotateAni(++_animationIndex);
    }, _ANIMATION_TIME); // 每间隔_ANIMATION_TIME进行一次旋转
  },
  /**
   * 停止旋转
   */
  stopAnimationInterval: function () {
    var that = this;
    if (!that.data.isRing) {
      return
    }
    that.setData({
      isRing: false
    })
    if (_animationIntervalId > 0) {
      clearInterval(_animationIntervalId);
      _animationIntervalId = 0;
    }
  },
  //初始化播放器,获取duration
  Initialization() {
    var t = this;
    if (this.data.audiolist[0].audiosrc.length != 0) {
      //设置src
      innerAudioContext.src = this.data.audiolist[0].audiosrc;
      //运行一次
      innerAudioContext.play();
      innerAudioContext.pause();
      innerAudioContext.onCanplay(() => {
        //初始化duration
        innerAudioContext.duration
        setTimeout(function () {
          //延时获取音频真正的duration
          var duration = innerAudioContext.duration;
          var min = parseInt(duration / 60);
          var sec = parseInt(duration % 60);
          if (min.toString().length == 1) {
            min = `0${min}`;
          }
          if (sec.toString().length == 1) {
            sec = `0${sec}`;
          }
          t.setData({ audioDuration: innerAudioContext.duration, showTime2: `${min}:${sec}` });
        }, 1000)
      })
    }
  },
  //拖动进度条事件
  sliderChange(e) {
    var that = this;
    innerAudioContext.src = this.data.audiolist[0].audiosrc;
    //获取进度条百分比
    var value = e.detail.value;
    this.setData({ audioTime: value });
    var duration = this.data.audioDuration;
    //根据进度条百分比及歌曲总时间,计算拖动位置的时间
    value = parseInt(value * duration / 100);
    //更改状态
    this.setData({
      audioSeek: value,
      isPlayAudio: true,
      playSrc: './pause-normal.png',
    });
    //调用seek方法跳转歌曲时间
    innerAudioContext.seek(value);
    //播放歌曲
    innerAudioContext.play();
    this.startAnimationInterval()
  },
  //播放、暂停按钮
  playAudio() {
    //获取播放状态和当前播放时间
    var isPlayAudio = this.data.isPlayAudio;
    var seek = this.data.audioSeek;
    innerAudioContext.pause();
    //更改播放状态
    this.setData({ isPlayAudio: !isPlayAudio })
    if (isPlayAudio) {
      //如果在播放则记录播放的时间seek,暂停
      this.setData({ audioSeek: innerAudioContext.currentTime });
    } else {
      //如果在暂停,获取播放时间并继续播放
      innerAudioContext.src = this.data.audiolist[0].audiosrc;
      if (innerAudioContext.duration != 0) {
        this.setData({ audioDuration: innerAudioContext.duration });
      }
      //跳转到指定时间播放
      innerAudioContext.seek(seek);
      innerAudioContext.play();
    }
  },
  loadaudio() {
    var that = this;
    //设置一个计步器
    this.data.durationIntval = setInterval(function () {
      //当歌曲在播放时执行
      if (that.data.isPlayAudio == true) {
        //获取歌曲的播放时间,进度百分比
        var seek = that.data.audioSeek;
        var duration = innerAudioContext.duration;
        var time = that.data.audioTime;
        time = parseInt(100 * seek / duration);
        //当歌曲在播放时,每隔一秒歌曲播放时间+1,并计算分钟数与秒数
        var min = parseInt((seek + 1) / 60);
        var sec = parseInt((seek + 1) % 60);
        //填充字符串,使3:1这种呈现出 03:01 的样式
        if (min.toString().length == 1) {
          min = `0${min}`;
        }
        if (sec.toString().length == 1) {
          sec = `0${sec}`;
        }
        var min1 = parseInt(duration / 60);
        var sec1 = parseInt(duration % 60);
        if (min1.toString().length == 1) {
          min1 = `0${min1}`;
        }
        if (sec1.toString().length == 1) {
          sec1 = `0${sec1}`;
        }
        //当进度条完成,停止播放,并重设播放时间和进度条
        if (time >= 100) {
          innerAudioContext.stop();
          that.setData({ audioSeek: 0, audioTime: 0, audioDuration: duration, isPlayAudio: false, showTime1: `00:00`, playSrc: './play-normal.png' });
          that.stopAnimationInterval()

          return false;
        }
        //正常播放,更改进度信息,更改播放时间信息
        that.setData({ audioSeek: seek + 1, audioTime: time, audioDuration: duration, showTime1: `${min}:${sec}`, showTime2: `${min1}:${sec1}` });
      }
    }, 1000);
  },
  onUnload: function () {
    innerAudioContext.stop();
    this.stopAnimationInterval()
  }
})

wxml

<image class="image-background" src="{{audiolist[0].audioImage}}"></image>
<view style="width:{{menuButtonBoundingClientRect.height+20}}px;height:{{menuButtonBoundingClientRect.height-1}}px ;position: fixed;z-index: 11;opacity:0.5;border-radius:{{menuButtonBoundingClientRect.height/2}}px;border:1rpx solid #ededed;top:{{menuButtonBoundingClientRect.top}}px;left:{{systemInfo.windowWidth-menuButtonBoundingClientRect.right}}px" hover-class="hover-home" class="home" hover-start-time="20" hover-stay-time="70" bindtap="go2Home">
</view>
<image style="position: fixed;z-index: 12;width:{{menuButtonBoundingClientRect.height-10}}px;height:{{menuButtonBoundingClientRect.height-10}}px;top:{{menuButtonBoundingClientRect.top+5}}px;left:{{systemInfo.windowWidth-menuButtonBoundingClientRect.right+15}}px"  src="../navigationBar/utils/home.png" bindtap="go2Home"></image>
<view class="container">
  <text class="text-voice" style="margin-top:{{textVoiceMarginTop}}">{{audiolist[0].audioName}}</text>
  <view class="view-circle-container" style="margin-top:{{viewCircleContainerMarginTop}}">
    <view class="view-circle"></view>
    <image class="image-circle" src="{{audiolist[0].audioImage}}" animation="{{animation}}"></image>
  </view>
<view class='audioPlayer' style="margin-top:{{audioPlayerMarginTop}}">
  <view class='player'>
    <view class='audioControls'>
      <view class='flex'>
      <view class='bottom'>
         <image bindtap="changePlay" 
src="{{playSrc}}"></image> 
        </view>
        <view class='slider'>
          <slider bindchange='sliderChange' activeColor='red' block-size="12" value='{{audioTime}}' />
        </view>
        <view class='time'>
          {{showTime1}}/{{showTime2}}
        </view>
      </view>
    </view>
  </view>
</view>
  <view class="control-container" style="margin-top:{{controlContainerMarginTop}}">
    <image class="icon-pre" src="{{preSrc}}" bindtouchstart="touchPreStart" bindtouchend="touchPreEnd"></image>
    <image class="icon-play" bindtap="changePlay" 
src="{{playSrc}}"></image>
    <image class="icon-next" src="{{nextSrc}}" bindtouchstart="touchNextStart" bindtouchend="touchNextEnd"></image>
  </view>
</view>

wxss

page{
  width:100%;
  height: 100%;
}
.image-background{
  width: 100%;
  height: 100%;
  filter: blur(10px)
}
.container{
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  position: fixed;
  z-index: 10
}
.control-container{
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row;
}
.icon-play{
  width:80rpx;
  height: 80rpx;
}
.icon-pre{
  width:60rpx;
  height: 60rpx;
  margin-right: 80rpx;
}
.icon-next{
  width:60rpx;
  height: 60rpx;
  margin-left: 80rpx;
}
.text-voice{
  font-size: 36rpx;
  color: white;
}
.image-logo{
  height: 60rpx;
  width: 60rpx;
}
.view-circle-container{
  display: flex;
  text-align: center;
  justify-content: center;
  align-items: center;
}
.view-circle{
  width: 624rpx;
  height: 624rpx;
  border-radius: 317rpx;
  background-color: #000000;
  opacity: 0.6;
  z-index: -1;
  /* position: fixed; */
}
.image-circle{
  width: 600rpx;
  height: 600rpx;
  border-radius: 300rpx;
  z-index: 100;
  position: fixed;
}
.flex{
  display: flex;
  justify-content: center;
  align-items: center
}
.audioPlayer{
  width: 100%;
  margin-bottom: 30rpx;
  box-sizing: border-box;
  padding: 20rpx 30rpx;
}
.player{
  width: 100%;
  height: 100%;
  position: relative;
}
.audioBack{
  width: 100%;
  height: 100%;
}
.audioControls{
  width: 100%;
  height: 80rpx;
  background: black;
  opacity: .6;
  position: absolute;
  bottom: 0;
  color: white;
  font-size: 6pt;
  line-height: 80rpx;
  text-align: center;
}
.audioControls .bottom{
  width: 60rpx;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center
}
.audioControls .bottom image{
  width: 40rpx;
  height: 40rpx;
}
.audioControls .slider{
  width: 520rpx;
  height: 100%;
}
.slider slider{
  width: 95%;
  margin-left: 4%;
  margin-right: 0;
}
.audioControls .time{
  width: 120rpx;
  height: 100%;
}
.home{
  background: white
}
.hover-home{
  background: #ededed
}
  • 4
    点赞
  • 0
    评论
  • 9
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

玩烂小程序

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值