vue3項目,主要通過wavesurfer.js實現前端錄音+實時波形渲染
<template>
<div class="audio-container" @click="onStop">
<div class="flex-between audio-box" v-if="!transform">
<div>你可以開始説話</div>
<div class="audio" id="waveform"></div>
<StopCircleFilledIcon size="24px" class="active-color" />
</div>
<div class="flex-between audio-box" v-else>
<t-space align="center" class="active-color">
<LoadIcon size="24px" class="load-icon" />
正在轉換為文本...
</t-space>
<CloseCircleFilledIcon size="24px" class="active-color" />
</div>
</div>
</template>
<script setup lang="ts">
import { StopCircleFilledIcon, CloseCircleFilledIcon, LoadIcon } from 'tdesign-icons-vue-next';
import WaveSurfer from 'wavesurfer.js';
import RecordPlugin from 'wavesurfer.js/dist/plugins/record.esm.js';
// 配置項
const waveOptions = ref({
waveColor: '#006EFF',
progressColor: '#52C41A',
height: 30, // 波形高度
barWidth: 2, // 條形寬度
barGap: 3, // 條形間距
barRadius: 2, // 條形圓角
barMinHeight: 0.1, // 波形條的最小高度
normalize: true, // 是否根據峯值對音頻波形進行縮放
interact: false, // 開啓鼠標操作
fillParent: true, // 填充父容器
pixelRatio: 1, // 加快渲染速度
minPxPerSec: 50, // 音頻每秒最小像素數
cursorWidth: 0, // 光標寬度
responsive: true, // 調整窗口大小時,調整波形大小
partialRender: true, // 使用PeakCache改善大波形的渲染速度
removeMediaControl: true,
hideScrollbar: true // 是否隱藏水平滾動條
});
// 是否開啓錄音
const isAudio = defineModel('isAudio');
const wavesurfer = ref(); // 實例
const record = ref(); // 錄音插件
const init = async () => {
try {
await destroy();
// 實例
wavesurfer.value = WaveSurfer.create({
container: '#waveform', // 波形容器
...waveOptions.value
});
// 創建錄音插件前先檢查權限
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
console.error('獲取麥克風權限失敗:', error);
return;
}
record.value = wavesurfer.value.registerPlugin(
RecordPlugin.create({
scrollingWaveform: true,
renderRecordedAudio: true,
renderDuringRecording: true,
mediaRecorderOptions: {
audioBitsPerSecond: 128000
}
})
);
record.value.on('record-start', () => {
console.log('錄音開始');
});
record.value.on('record-progress', (time: any) => {
console.log('更新進度條', time);
});
} catch (error) {
console.error('創建錄音實例失敗:', error, isAudio.value);
}
};
// 停止
const transform = ref(false);
const onStop = () => {
transform.value = !transform.value;
isAudio.value = false;
};
// 實例銷燬
const destroy = async () => {
if (wavesurfer.value) {
wavesurfer.value.destroy();
wavesurfer.value = null;
}
};
watch(
isAudio,
async val => {
if (val) {
console.log('開啓錄音');
await init();
await record.value.startRecording();
} else {
console.log('關閉錄音');
if (record.value) {
record.value.stopRecording();
}
destroy();
}
},
{ immediate: true }
);
</script>
<style lang="scss" scoped>
.audio-container {
.audio-box {
box-sizing: border-box;
column-gap: 20px;
height: 56px;
padding: 0 10px;
font-size: $zl-size-1;
color: $zl-text-color-2;
border-radius: 6px;
.audio {
flex: 1;
}
.active-color {
color: #006eff;
}
.load-icon {
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
}
</style>