记录使用Recorder实现H5页面录音
技术栈:vue3 + element-plus + Recorder
这段代码主要实现了以下功能:
- 长按录音,松开发送
- 上滑至一定位置取消
- 音频blob转成base64格式
- 录音音频下载
- 模拟音频声波的动画
安装依赖
yarn add recorder-core
// 或者
npm i recorder-core
引入依赖
在使用录音的页面文件中添加如下代码引入Recorder 及 音频编码器。
import Recorder from 'recorder-core'
import 'recorder-core/src/engine/wav.js' // 引入对应格式的编码器
编码器必须引入,否则录音时会报错未加载对应的编码器。我使用的是wav格式,如果使用其他音频格式,可引入对应的编码器文件。下图是依赖中编码器文件所在位置,按需引入即可。
使用
<template>
<el-button
class="audio-btn"
@touchstart="startRecording"
@touchmove="handleTouchMove"
@touchend="sendRecording"
>
{{ recording ? '松开发送' : '按住说话' }}
</el-button>
<el-drawer
v-model="dialogVisible"
direction="btt"
:show-close="false"
:with-header="false"
>
<div v-if="recording">
<p v-if="canceling">松手取消发送</p>
<p v-else-if="seconds && seconds < 10">{{ seconds }}s后将停止录音</p>
<p v-else>松开发送,上滑取消</p>
// 模式音频声波的动画
<div class="audio-animation">
<span class="audio-wave" :class="'wave' + index" v-for="index in 8" :key="index"></span>
</div>
</div>
</el-drawer>
</template>
const recording = ref(false);
const cancel = ref(false); // 上滑并松手,确定取消录音(用于判断是否将录音发送给服务端)
const canceling = ref(false); // 正在上滑,也可能还会下滑继续录音(用于在上滑时提示用户上滑会取消)
const dialogVisible = ref(false);
let startY = 0; // 用于判断上滑取消
const rec = ref(null);
let countdownTimer = null;
const seconds = ref(60); // 录音限制60s
function startRecording(event) {
cancel.value = false;
recording.value = true;
dialogVisible.value = true;
startY = event.touches[0].clientY;
seconds.value = 60;
recOpen()
};
// 开启录音
function recOpen() {
// 传入 音频采样所需的格式,采样频率和采样位数
rec.value = Recorder({
type: "wav",
sampleRate: 8000,
bitRate: 16,
});
rec.value.open(function(){
// 这个定时器是限制录音60s,最后10s的时候给用户提示
countdownTimer = setInterval(() => {
console.log(`倒计时:${seconds.value}秒`);
seconds.value--;
if (seconds.value < 0) {
clearInterval(countdownTimer);
// 到达录音限制时间,自动停止录音并发送给服务端
sendRecording();
}
}, 1000);
console.log('开始录音');
rec.value.start(); // 开始录音
},function(msg, isUserNotAllow){
console.log((isUserNotAllow ? "UserNotAllow," : "")+"无法录音:" + msg);
});
};
// 结束录音
function recStop(){
rec.value.stop(function(blob, duration){
// window.URL.createObjectURL(blob) 是blob格式链接,可直接播放音频;duration为音频时长(ms)
console.log(blob, window.URL.createObjectURL(blob), "时长:" + duration + "ms");
rec.value.close(); // 释放录音资源,最好释放,否则会导致当前页面一直占用麦克风
rec.value = null;
if (countdownTimer) {
clearTimeout(countdownTimer); // 清除倒计时计时器
}
// 如果调试时想要下载录音文件,可把这段注释打开。
// const a = document.createElement('a');
// a.href = (window.URL).createObjectURL(blob);
// a.download = 'recording.wav';
// a.style.display = 'none';
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
console.log('cancel', cancel.value);
// 如果没有上滑取消则转成base64发给服务端,否则不做操作
if (!cancel.value) {
transBase64(blob)
}
},function(msg){
console.log(msg);
rec.value.close();
rec.value = null;
});
}
// 把blob格式的录音文件转成base64格式
function transBase64(wavBlob) {
let reader = new FileReader();
reader.onloadend = function(){
const base64Str = (/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1];
// 调用接口,发给服务端
sendBase64AudioToBackend(base64Str);
};
reader.readAsDataURL(wavBlob);
}
// 上滑超过50px则取消录音
function handleTouchMove(event) {
if (recording.value) {
const currentY = event.touches[0].clientY;
const deltaY = startY - currentY;
console.log(deltaY);
if (deltaY > 50) {
cancel.value = true;
canceling.value = true;
} else {
cancel.value = false;
canceling.value = false;
}
}
};
// 结束录音时,恢复各字段默认值并执行recStop方法
function sendRecording() {
dialogVisible.value = false;
recording.value = false;
canceling.value = false;
recStop()
};
样式部分只包含模拟声波的动画部分,其余省略
.audio-animation {
display: flex;
align-items: center;
justify-content: center;
margin-top: 50px;
}
.audio-wave {
width: 2px;
height: 6px;
margin: 0 2px;
background-color: #0078d4;
animation: waveAnimation 0.5s ease infinite;
animation-fill-mode: both; /* 保持动画结束状态 */
}
.audio-wave.wave1 { animation-delay: -.9s; }
.audio-wave.wave2 { animation-delay: -.8s; }
.audio-wave.wave3 { animation-delay: -.7s; }
.audio-wave.wave4 { animation-delay: -.6s; }
.audio-wave.wave5 { animation-delay: -.5s; }
.audio-wave.wave6 { animation-delay: -.4s; }
.audio-wave.wave7 { animation-delay: -.3s; }
.audio-wave.wave8 { animation-delay: -.2s; }
@keyframes waveAnimation {
0%, 100% {
transform: scaleY(1);
}
50% {
transform: scaleY(3);
}
}
其他
录音需要调用navigator.mediaDevices.getUserMedia()获取权限,如果报错navigator.mediaDevices is undefined,请参考文章要实现录音功能,但是navigator.mediaDevices 是 undefined这样解决