uniapp微信小程序-长按按钮百度语音识别回显文字

流程图:

话不多说,上代码:

javascript 复制代码
<template>
  <view class="content">
    <view class="speech-chat" @longpress="startSpeech" @touchend="endSpeech">
      <view class="animate-block" v-if="voiceState">
        <view class="dot-ripple"></view>
        <view class="dot-ripple"></view>
      </view>
      <image class="speech-icon" src="/static/aichat/icon-speech.png" mode="aspectFit"></image>
    </view>
  </view>
</template>

<script lang="ts" setup>
const recorderManager = ref(uni.getRecorderManager()) // 获取全局唯一的录音管理器
// 这俩有啥用?
const innerAudioContext = uni.createInnerAudioContext() // 创建并返回内部audio上下文对象
innerAudioContext.autoplay = true // 开启自动播放设置

const voicePath = ref<string>('') // 语音文件路径
const result = ref<string>('') // 语音识别结果
const voiceState = ref<boolean>(false) // 语音输入状态
const authState = ref<boolean>(false) // 录音授权状态
const bdData = reactive({
  pid: '你的pid',
  cuid: '你的cuid',
  token: '你的token',
  voiceUrl: 'https://vop.baidu.com/server_api',
})

const emit = defineEmits(['speech-result'])

// 初始化录音配置
const initRecorder = () => {
  recorderManager.value.onStart(() => {
    // console.log('录音开始')
  })
  recorderManager.value.onStop((res) => {
    readFile(res.tempFilePath) // 文件读取事件
    voicePath.value = res.tempFilePath
  })
}
/**
 * @param {*} voiceFilePath 文件流路径
 * @desc 文件流或文件路径获取
 */
const readFile = (voiceFilePath) => {
  // 通过全局唯一的<文件管理器>读取文件信息
  uni.getFileSystemManager().readFile({
    filePath: voiceFilePath,
    success: (res) => {
      //   console.log('文件编码成功', res)
      getRecognizeResult(res.data) // 调用百度语言识别API
    },
    fail: (err) => {
      // console.log('文件编码失败', err)
      return err
    },
  })
}
/**
 * @param {*} speechResult 文件流路径
 * @desc 百度语音识别获取
 */
const getRecognizeResult = (speechResult) => {
  const questUrl = `${bdData.voiceUrl}?cuid=${bdData.cuid}&token=${bdData.token}&dev_pid=${bdData.pid}`
  uni.request({
    url: questUrl,
    method: 'POST',
    header: {
      'Content-Type': 'audio/pcm;rate=16000', // 注意这个header用于设置音频文件格式和码率
    },
    data: speechResult,
    success: (res) => {
      console.log('请求成功', res)
      result.value = res.data.result[0]
      emit('speech-result', res.data.result[0])
    },
    fail: (err) => {
      console.log('请求失败', err)
      uni.showToast({
        title: '语言识别异常,稍后重试',
        icon: 'error',
        duration: 2000,
      })
    },
    complete: () => {
      voiceState.value = false
    },
  })
}
const startRecord = () => {
  // 开始录音并设置格式
  recorderManager.value.start({
    format: 'PCM',
  })
}
/**
 * @desc 长按触发录音
 */
const startSpeech = (e) => {
  // 震动提示用户--不一定生效
  uni.vibrateLong({
    success: function () {
      //   console.log('success')
    },
  })
  // 已经授权 则开始录音
  if (authState.value) {
    voiceState.value = true
    startRecord()
    return
  }
  // 未授权--主要用于检查用户是否已授权某些敏感权限(如录音、摄像头、位置等)
  uni.getSetting({
    success(settingRes) {
      // 未授权
      if (!settingRes.authSetting['scope.record']) {
        // 只保留一种授权方式,避免重复弹框
        uni.authorize({
          scope: 'scope.record',
          success() {
            authState.value = true
            voiceState.value = true
            startRecord()
          },
          fail() {
            // 用户拒绝授权后,引导进入设置页面开启授权
            uni.showModal({
              title: '提示',
              content: '需要录音权限才能使用语音识别功能',
              confirmText: '去设置',
              success(res) {
                if (res.confirm) {
                  uni.openSetting({
                    success(res) {
                      if (res.authSetting['scope.record']) {
                        authState.value = true
                        voiceState.value = true
                        startRecord()
                      }
                    },
                  })
                }
              },
            })
          },
        })
      } else {
        authState.value = true
        voiceState.value = true
        startRecord()
      }
    },
  })
}
/**
 * @desc 检查录音授权状态
 */
const checkRecordAuth = () => {
  // 获取应用的权限设置
  uni.getSetting({
    success(res) {
      // 如果已经授权录音权限,更新状态
      if (res.authSetting['scope.record']) {
        authState.value = true
      }
    },
  })
}
const endSpeech = (e) => {
  // 只有在录音状态时才停止录音
  if (voiceState.value) {
    voiceState.value = false
    // 结束录音
    recorderManager.value.stop()
  }
}

onMounted(() => {
  // 初始化录音管理器
  initRecorder()
  // 初始化检查录音权限状态
  checkRecordAuth()
})
</script>

<style lang="scss" scoped>
.speech-chat {
  position: relative;
  bottom: 0;
  left: 50%;
  box-sizing: border-box;
  width: 140rpx;
  height: 140rpx;
  border-radius: 30rpx;
  transform: translateX(-50%);
  image {
    box-sizing: border-box;
    width: 140rpx;
    height: 140rpx;
    border-radius: 50%;
  }
}
.speech-icon {
  position: absolute;
  z-index: 2;
}
/* 波纹动画容器 */
.dot-ripple {
  position: absolute;
  top: 40%; /* 定位到父容器垂直中点 */
  /* 新增居中代码 */
  left: 50%; /* 定位到父容器水平中点 */
  z-index: 1;
  width: 20rpx;
  height: 20rpx;
  background-color: #ff4200;
  border-radius: 50%;
  box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.3) inset;
  transform: translate(-50%, -50%); /* 自身尺寸反向位移50% */
  animation: ripple 1s ease infinite;
}
/* 动画定义 */
@keyframes ripple {
  0% {
    width: 0;
    height: 0;
    opacity: 0.75;
    transform: translate(-50%, -50%) scale(0); /* 保持居中缩放 */
  }
  100% {
    width: 280rpx;
    height: 280rpx;
    opacity: 0;
    transform: translate(-50%, -50%) scale(1); /* 保持居中放大 */
  }
}
/* 小程序兼容 */
/* #ifdef MP-WEIXIN */
.dot-ripple {
  transform: translateZ(0); /* 触发硬件加速 */
}
/* #endif */
.dot-ripple:nth-child(2) {
  animation-delay: 0.2s;
}
</style>
相关推荐
哎哟喂_!2 分钟前
Node.js 循环依赖问题详解:原理、案例与解决方案
前端·chrome·node.js
热爱前端的小君同学12 分钟前
长按拖拽移动的vue3组件
前端·javascript·vue.js
Rhys..32 分钟前
如何禁止chrome自动更新
前端·chrome
noravinsc34 分钟前
InforSuite AS 可以发布django和vue项目是否可行
vue.js·python·django
巴巴_羊39 分钟前
AJAX 使用 和 HTTP
前端·http·ajax
刺客-Andy1 小时前
React 第四十一节Router 中 useActionData 使用方法案例以及注意事项
前端·react.js·前端框架
岁岁岁平安1 小时前
Vue3学习(组合式API——reactive()和ref()函数详解)
前端·javascript·vue.js·学习·vue3·reactive·ref
肠胃炎1 小时前
React事件机制
前端·javascript·react.js
CUIYD_19891 小时前
javascript —— ! 和 !! 的区别与作用
前端·javascript·vue.js
慌糖2 小时前
vue3搭建脚手架前的前置知识
vue.js