微信小程序原生 -- 音频播放器

178 阅读3分钟

需求: 先目前需要实现一个音频播放器

左右布局 按钮控制播放 暂停 进度条展示播放进度,放在页面最下方

实现步骤:

1.先实现基础样式结构 wxml + wxss  

2.实现 找到API wx.createInnerAudioContext 创建实例实现需求

3.实现进度条和播放器时间进度联动 监听时间变化 并且给进度条value赋值

4.封装成组件,方便其他页面使用,title + voiceUrl 由父组件传递

1.在 wxml中 实现样式

image.png

<!--components/player/player.wxml-->
<!--底部播放器 -->
<view class="player">
  <!-- 左边的播放器封面和控制按钮 -->
  <view class="player-left" bind:tap="changePage">
    <image class="player-cover" src="{{play.coverImgUrl}}" />
    <view class="player-controls">
      <image wx:if="{{state=='paused'}}" src="/image/player-p.png" />
      <image wx:else src="/image/player-d.png" />
    </view>
  </view>
  <!-- 中间的播放器信息和进度条 -->
  <view class="player-center ">
    <!-- 显示播放进度和时间 -->
    <view class="content-play-progress">
      <text>{{currentTime}}</text>
      <view>
        <slider bindchange="sliderChange" activeColor="#d33a31" block-size="12" backgroundColor="#dadada" value="{{percent}}" />
      </view>
      <text>{{duration}}</text>
    </view>
    <view class="player-info-title">{{playerTitle}}</view>
  </view>
</view>

问题??

A : 为什么不直接使用 # audio 组件

Q : 官方说 从基础库 1.6.0 开始,本接口停止维护,请使用 wx.createInnerAudioContext 代替,也就是意味着这里需要自己手写,然后在js中自己调 wx.createInnerAudioContext() 这个API 接口

2.在wxss 中完善样式

因为是放在页面最下方,所以直接 fixed 定位 了

.player {
  position: fixed;
  bottom: 0;
  left: 0;
  background-color: #ffffff; /* 设置背景色 */
  border-top: 1px solid rgb(218, 218, 218); /* 添加顶部边框 */
  display: flex;
  align-items: center;
  color: #7a7a7a;
  padding: 20rpx;
}

.player-left {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative; 
  height: 100rpx;
  margin-right: 20rpx;
}

.player-cover {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
}

.player-controls {
  position: absolute; /* 添加绝对定位 */
  top: 50%; /* 居中 */
  left: 50%; /* 居中 */
  transform: translate(-50%, -50%); /* 居中 */
}
.player-controls image{
  width: 40rpx;
  height: 40rpx;
}

.player-center {
  display: flex;
  flex-direction: column;
}
.content-play-progress{
  display: flex;
  align-items: center;
}
.content-play-progress view{
  width: 400rpx;
} 
.player-info-title{
  margin-bottom: 20rpx;
  margin-left: 20rpx;
}
.player-right {
  position: relative; /* 设置相对定位,以便在其内部进行绝对定位 */
  display: flex;
}

.button {
  position: relative; /* 设置相对定位,以便在其内部进行绝对定位 */
  display: inline-block;
  cursor: pointer;
}

.content {
  display: inline-block;
  padding: 10rpx 25rpx;
  border-radius: 15rpx;
  border: 2rpx solid #7a7a7a;
  color: #7a7a7a;
  font-size: 28rpx;
}

.badge {
  position: absolute; /* 设置绝对定位 */
  top: -30rpx; /* 距离按钮顶部的距离 */
  right: -10rpx; /* 距离按钮右侧的距离 */
  background-color:#ffffff;
  color: #7a7a7a;
  padding: 4rpx 8rpx;
  border-radius: 50%;
  font-size: 24rpx;
}

3.在 js 中完善功能

// components/player/player.js
Component({

  /**
   * 组件的属性列表
   */
  properties: {
    playerTitle: String,
    voiceUrl: String
  },

  /**
   * 组件的初始数据
   */
  data: {
    play: {
      coverImgUrl: "/image/1.jpg",
      title: "三环钮铜盖鼎、四环钮",

    },
    state: 'paused', // 默认播放状态为暂停
    multiple: 1,
    currentTime: '00:00', // 当前播放时间
    duration: '00:00', // 音频总时长
    percent: 0,
  },

  /**
   * 组件的方法列表
   */
  methods: {
    changePage() {
      // 切换播放状态
      const newState = this.data.state === 'paused' ? 'playing' : 'paused';
      if (newState === 'playing') {
        this.innerAudioContext.play(); // 播放音频
      } else {
        this.innerAudioContext.pause(); // 暂停音频
      }
      this.setData({
        state: newState
      });
      // innerAudioContext.stop() // 停止

      // innerAudioContext.destroy() // 释放音频资源
    },
    sliderChange(e) {
      const value = e.detail.value;
      // 计算音频播放的目标时间
      const duration = this.innerAudioContext.duration;
      const targetTime = duration * value / 100;
      // 设置音频播放进度
      this.innerAudioContext.seek(targetTime);
      // 更新当前播放时间
      this.setData({
        currentTime: this.formatTime(targetTime)
      });
    },
    // 格式化时间
    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)
    },
  },

  ready() {
    // 初始化音频实例
    this.innerAudioContext = wx.createInnerAudioContext({
      useWebAudioImplement: false
    });
    this.innerAudioContext.src = this.properties.voiceUrl;

    // 监听音频时间更新事件
    this.innerAudioContext.onTimeUpdate(() => {
      // 更新当前播放时间和进度百分比
      this.setData({
        currentTime: this.formatTime(this.innerAudioContext.currentTime),
        duration: this.formatTime(this.innerAudioContext.duration),
        percent: this.innerAudioContext.currentTime / this.innerAudioContext.duration * 100
      });
    });

  },
  detached() {
    // 在组件销毁时释放音频资源
    this.innerAudioContext.destroy();
  }

})

注意点

在页面初始化就实例化一个音频实例,并且因为有播放时间和进度 ,所以需要监听 onTimeUpdate 它们的变化

当滑动进度条的时候,对应的播放时间要变化,这里需要setData 一下,否则初始时间不变化
更新当前播放时间 this.setData({ currentTime: this.formatTime(targetTime) });

 ready() {
    // 初始化音频实例
    this.innerAudioContext = wx.createInnerAudioContext({
      useWebAudioImplement: false
    });
    this.innerAudioContext.src = this.properties.voiceUrl;

    // 监听音频时间更新事件
    this.innerAudioContext.onTimeUpdate(() => {
      // 更新当前播放时间和进度百分比
      this.setData({
        currentTime: this.formatTime(this.innerAudioContext.currentTime),
        duration: this.formatTime(this.innerAudioContext.duration),
        percent: this.innerAudioContext.currentTime / this.innerAudioContext.duration * 100
      });
    });

  },
  detached() {
    // 在组件销毁时释放音频资源
    this.innerAudioContext.destroy();
  }

4.在json 中开启组件为true

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

5.最后在页面中引入

5.1 需现在json 中usingComponents 中以键值对的形式引入

image.png

5.2 在wxml中引入该组件

image.png

6.最后实现效果

image.png