小工具知识网

当前位置: 首页 > 行业知识 > uniapp 小程序端长按录音、保存、播放录音功能实现和问题记录

行业知识

uniapp 小程序端长按录音、保存、播放录音功能实现和问题记录

2026-11-06 07:22:26 来源:互联网转载

背景

使用 uniapp 开发小程序,使用 Vue3 + uview-plus ,做一个长按录音的功能,分享一下实现过程

开发环境

  • NodeJs:14.18.3
  • uni-app Vue3 版本
  • uview-plus:^3.1.41

    主要功能:

    显示已上传音频文件

    • 在进入页面的时候查询接口,如果有已上传的录音,显示上传音频文件,支持播放、暂停、删除功能;

      录音

      • 进入页面先获取录音权限状态;
      • 按钮长按录音,先校验是否有录音权限,用户拒绝或者没有录音权限提示用户进入小程序设置中开启录音权限;
      • 录音时显示录音倒计时,最多录制60秒,这个可以自己设置;
      • 松开按钮录音结束,上传音频文件;

        效果截图

        主要代码

        示例代码可以直接复制使用

              已录制                                          {{ getVoiceDuration }}                              {{ '剩余:' + count + 's' }}      松手 完成              import { ref, reactive, computed } from 'vue';import { onLoad } from '@dcloudio/uni-app';const recordAuth = ref(false) // 是否授权录音// 录音const recorderManager = uni.getRecorderManager() // 录音管理对象let init: any = null // 录制时长计时器let timer: any = null // 播放 录制倒计时const count = ref(0) // 录制倒计时const longPress = ref(1) // 1显示 按住说话 2显示 说话中function recordingTimer(_time: any) {  if (_time == undefined) {    // 将计时器赋值给init    init = setInterval(() => {      time.value++    }, 1000);  } else {    clearInterval(init)  }}// 录音授权function authorRecord() {  uni.authorize({    scope: 'scope.record',//这里选择scope.(录音、位置、用户信息...都可以)    success() { // 允许授权      console.log('已授权');      recordAuth.value = true    },    fail() { // 拒绝授权      uni.showModal({        content: '检测到您没打开录音功能权限,是否去设置打开?',        confirmText: "确认",        confirmColor: '#1874f5',        cancelText: '取消',        success: (res) => {          if (res.confirm) {            uni.openSetting({ // opensetting 是调起设置页面的  success: (res) => {                // 获取设置授权的状态                const setting = res.authSetting;                recordAuth.value = setting['scope.record'] || false              }            })          } else {            return false;          }        }      })      return false;    }  })}// 长按录音事件function longpressBtn() {  if(!recordAuth.value) {    authorRecord()    return;  }  longPress.value = 2;  clearInterval(init) // 清除定时器  countdown(60); // 倒计时  recorderManager.onStop((res) => {    tempFilePath.value = res.tempFilePath;    recordingTimer(time.value);  })  const options = {    duration: duration.value, // 指定录音的时长,单位 ms    sampleRate: 16000, // 采样率    numberOfChannels: 1, // 录音通道数    encodeBitRate: 96000, // 编码码率    format: 'mp3', // 音频格式,有效值 aac/mp3    frameSize: 10, // 指定帧大小,单位 KB  }  recordingTimer(undefined);  recorderManager.start(options);  // 监听音频开始事件  recorderManager.onStart((res) => {    console.log(res)  })}// 长按松开录音事件function touchendBtn() {  if(!recordAuth.value) {    return;  }    longPress.value = 1;  recorderManager.onStop((result) => {    // 录音结束,可以先调用删除接口将旧录音删除,然后再上传最新的录音    // 通过 uni.uploadFile 上传录音文件,filePath 传 voice.tempFilePath,指定要上传文件资源路径    // 下面是示例,模拟上传成功之后的处理    tempFilePath.value = "https://web-ext-storage.dcloud.net.cn/uni-app/ForElise.mp3";    innerAudioContext.src = "https://web-ext-storage.dcloud.net.cn/uni-app/ForElise.mp3";    time.value = 30;    voiceAllTime.value = time.value;  })  recordingTimer(time.value)  clearInterval(timer)  recorderManager.stop();}// 音频const innerAudioContext = uni.createInnerAudioContext() // audio 上下文对象const time = ref(0) //录音时长const duration = ref(60000) //录音最大值ms 60000/1分钟const tempFilePath: any = ref('') //音频路径const playStatus = ref(0) //录音播放状态 0:未播放 1:正在播放const voiceAllTime = ref(0) //记录音频原始总时长// 显示音频剩余秒数const getVoiceDuration = computed(() => {  let _t = String(time.value)  let str = _t.length > 1 ? `00:${_t}` : `00:0${_t}`;  return str})// 播放function updateTime(val: number) {  time.value = Number(val);  timer = setInterval(() => {    if (time.value >= 1) {      time.value--    } else {      time.value = voiceAllTime.value      clearInterval(timer);    }  }, 1000);}function playBtn() {  //点击播放  if (playStatus.value == 0) {    playStatus.value = 1;    innerAudioContext.play();    updateTime(time.value); // 倒计时  } else {    playStatus.value = 0;    innerAudioContext.pause()    clearInterval(timer)  }  //播放结束  innerAudioContext.onEnded(() => {    playStatus.value = 0;  })}// 删除录音function delBtn() {  // 调用接口删除  // 下面是示例,删除后的重置  time.value = 0  tempFilePath.value = ''  playStatus.value = 0  time.value = voiceAllTime.value = 0}// 倒计时function countdown(val: any) {  count.value = Number(val);  timer = setInterval(() => {    if (count.value > 0) {      count.value--    } else {      longPress.value = 1;      clearInterval(timer);    }  }, 1000);}// 获取录音权限状态function getRecordSetting() {  uni.getSetting({    success(res) {      const setting = res.authSetting;      recordAuth.value = setting['scope.record'] || false    }  })}// 设置录音function setVoice() {  // 进入页面时调用接口获取上传的路由文件路径  // 下面是示例  tempFilePath.value = "https://web-ext-storage.dcloud.net.cn/uni-app/ForElise.mp3";  innerAudioContext.src = tempFilePath.value;  time.value = 30;  voiceAllTime.value = time.value;}onLoad(() => {  setVoice()  getRecordSetting()}).card {  margin: 0 0 24rpx;  padding: 100rpx 24rpx;  background: #ffffff;  .title {    margin-bottom: 16rpx;    color: #333333;    font-size: 28rpx;    font-weight: 700;  }  .desc {    color: #666666;    font-size: 24rpx;    font-weight: 400;  }  .app-tags {    .tag {      padding: 12rpx 24rpx;      border-radius: 28rpx;      color: #666666;      font-size: 24rpx;      font-weight: 400;      background: #f8f8f8;    }  }  .player {    padding: 8rpx 16rpx 8rpx 8rpx;    width: 354rpx;    height: 64rpx;    border-radius: 40rpx;    background: #f8f8f8;    .icon-play {      width: 48rpx;      height: 48rpx;    }    .icon-sound {      width: 172rpx;      height: 32rpx;    }    .time {      color: #666666;      font-size: 24rpx;      font-weight: 400;    }  }}.sbmit-btn {  position: absolute;  left: 0;  right: 0;  bottom: 114rpx;  margin: auto;  border-radius: 50%;  width: 152rpx;  height: 152rpx;  background: #666666;  box-shadow: 0 0 32rpx 0 #bababa40;  z-index: 1;  &::after {    border: none;  }  .inner {    position: absolute;    top: 0;    left: 0;    right: 0;    bottom: 0;    margin: auto;    border-radius: 50%;    width: 52rpx;    height: 52rpx;    background: #ffffff;  }}.prompt-layer {  position: absolute;  left: 0;  top: 0;  width: 100%;  height: 100%;  background: rgba(0, 0, 0, 0.4);  z-index: 5;  .prompt-popup {    position: absolute;    left: 0;    bottom: 0;    width: 100%;    height: 220rpx;    // line-height: 200rpx;    color: #000000;    font-size: 28rpx;    font-weight: 400;    text-align: center;    &::after {      content: '';      position: absolute;      left: -10%;      bottom: -52%;      border-radius: 50%; //顶部圆弧,底部圆弧: 0 0 50% 50%      width: 120%;      height: 185%;      background: #cccccc;      z-index: -1;    }  }}

        问题记录

        在调用录音等这些需要授权的接口之前,一定要校验是否用户已授权

        • 长按录音时,之前未校验是否已授权录音权限,一些人在使用时会出各种诡异现象,录音始终录制不成功,刚开始以为是代码逻辑问题,排查一圈之后才意识到是授权原因;
        • 第一次在调用 recorderManager 对象开始录音之前,微信会弹出录音授权弹窗,如果这时拒绝授权,后面再长按录音按钮不会二次弹出,需要用户点击小程序右上角三个点进设置手动开启录音授权,在开始录音前校验是否已得到授权就解决这个问题了。

上一篇:怎么测试托管服务器带宽?

下一篇:网页服务器怎么换手机端?