web網(wǎng)頁(yè)上實(shí)現(xiàn)錄音功能(vue3組件示例)
一. 前言
在Web開(kāi)發(fā)中實(shí)現(xiàn)音頻錄制功能是許多應(yīng)用場(chǎng)景的常見(jiàn)需求。本文將通過(guò)一個(gè)完整的Vue 3組件示例,詳細(xì)解析如何利用現(xiàn)代瀏覽器API實(shí)現(xiàn)網(wǎng)頁(yè)端的錄音功能。
二. 技術(shù)實(shí)現(xiàn)
1.核心API介紹
MediaRecorder API 是實(shí)現(xiàn)錄音功能的核心,它允許我們直接捕獲來(lái)自用戶設(shè)備的媒體流。主要方法:
getUserMedia() 獲取媒體設(shè)備權(quán)限
MediaRecorder() 創(chuàng)建錄音實(shí)例
ondataavailable 接收音頻數(shù)據(jù)
onstop 處理錄音結(jié)束事件
2.模板部分
<template>
<div class="voice-recorder">
<!-- 錄音控制 -->
<button @click="toggleRecording" :class="{ recording: isRecording }">
{{ isRecording ? "停止錄音" : "開(kāi)始錄音" }}
</button>
<!-- 生成的音頻文件列表 -->
<div v-if="audioFiles.length > 0" class="audio-list">
<div v-for="(audio, index) in audioFiles" :key="index">
<audio controls :src="audio.url"></audio>
</div>
</div>
</div>
</template>
3.核心邏輯實(shí)現(xiàn)
const isRecording = ref(false);
const mediaRecorder = ref(null);
const audioChunks = ref([]);
const audioFiles = ref([]);
// 開(kāi)始錄音
const startRecording = async (e) => {
if (isRecording.value) return;
try {
audioChunks.value = [];
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder.value = new MediaRecorder(stream);
mediaRecorder.value.ondataavailable = (e) => {
audioChunks.value.push(e.data);
};
mediaRecorder.value.onstop = () => {
const audioBlob = new Blob(audioChunks.value, { type: "audio/webm" });
const audioURL = URL.createObjectURL(audioBlob);
audioFiles.value.push({
name: `recording_${Date.now()}.${getFileExtension(audioBlob.type)}`,
url: audioURL,
});
audioChunks.value = [];
};
mediaRecorder.value.start();
isRecording.value = true;
} catch (error) {
console.error("錄音錯(cuò)誤:", error);
}
};
4. 關(guān)鍵功能點(diǎn)解析
(1) 音頻格式處理
通過(guò)MIME類型映射獲取文件擴(kuò)展名:
// 獲取文件擴(kuò)展名
const getFileExtension = (mimeType) => {
const extensions = {
"audio/webm": "webm",
"audio/ogg": "ogg",
"audio/mp4": "mp4",
"audio/mpeg": "mp3",
"audio/wav": "wav",
};
return extensions[mimeType] || "webm";
};
(2) 資源管理
組件卸載時(shí)自動(dòng)釋放資源:
//停止錄音
const stopRecording = () => {
console.log("停止錄音");
isRecording.value = false;
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
};
// 清理資源
onUnmounted(() => {
stopRecording();
});

三. 完整代碼
<template>
<div class="voice-recorder">
<!-- 錄音控制 -->
<button @click="toggleRecording" :class="{ recording: isRecording }">
{{ isRecording ? "停止錄音" : "開(kāi)始錄音" }}
</button>
<!-- 生成的音頻文件列表 -->
<div v-if="audioFiles.length > 0" class="audio-list">
<div v-for="(audio, index) in audioFiles" :key="index">
<audio controls :src="audio.url"></audio>
</div>
</div>
</div>
</template>
<script setup>
const isRecording = ref(false);
const mediaRecorder = ref(null);
const audioChunks = ref([]);
const audioFiles = ref([]);
// 開(kāi)始錄音
const startRecording = async (e) => {
if (isRecording.value) return;
try {
audioChunks.value = [];
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder.value = new MediaRecorder(stream);
mediaRecorder.value.ondataavailable = (e) => {
audioChunks.value.push(e.data);
};
mediaRecorder.value.onstop = () => {
const audioBlob = new Blob(audioChunks.value, { type: "audio/webm" });
const audioURL = URL.createObjectURL(audioBlob);
audioFiles.value.push({
name: `recording_${Date.now()}.${getFileExtension(audioBlob.type)}`,
url: audioURL,
});
audioChunks.value = [];
};
mediaRecorder.value.start();
isRecording.value = true;
} catch (error) {
console.error("錄音錯(cuò)誤:", error);
}
};
//停止錄音
const stopRecording = () => {
console.log("停止錄音");
isRecording.value = false;
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
};
// 獲取文件擴(kuò)展名
const getFileExtension = (mimeType) => {
const extensions = {
"audio/webm": "webm",
"audio/ogg": "ogg",
"audio/mp4": "mp4",
"audio/mpeg": "mp3",
"audio/wav": "wav",
};
return extensions[mimeType] || "webm";
};
// 切換錄音狀態(tài)
const toggleRecording = () => {
if (!isRecording.value) {
startRecording();
} else {
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
}
isRecording.value = !isRecording.value;
};
// 清理資源
onUnmounted(() => {
stopRecording();
});
</script>
<style lang="scss" scoped>
.voice-recorder {
max-width: 800px;
margin: 0 auto;
padding: 20px;
button {
padding: 12px 24px;
background: #42b983;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background 0.3s;
&.recording {
background: #ff4444;
}
}
button:hover {
background: #3aa876;
}
button:disabled {
background: #ddd;
cursor: not-allowed;
}
}
.recording {
background-color: #ff4444;
color: white;
}
.audio-list {
margin-top: 20px;
}
</style>
四. 功能擴(kuò)展建議
添加錄音時(shí)長(zhǎng)顯示
實(shí)現(xiàn)音頻波形可視化
總結(jié)
到此這篇關(guān)于web網(wǎng)頁(yè)上實(shí)現(xiàn)錄音功能的文章就介紹到這了,更多相關(guān)vue3 web網(wǎng)頁(yè)錄音功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3在css中使用v-bind綁定js/ts變量(在scss和less中使用方式)
v-bind是Vue.js中的一個(gè)核心指令,用于在Vue組件或DOM元素上綁定數(shù)據(jù)屬性,下面這篇文章主要給大家介紹了關(guān)于Vue3在css中使用v-bind綁定js/ts變量的相關(guān)資料,也可以在scss和less中使用方式,需要的朋友可以參考下2024-04-04
如何解決element-ui中多個(gè)table在tab切換時(shí)出現(xiàn)寬度縮小問(wèn)題
這篇文章主要介紹了如何解決element-ui中多個(gè)table在tab切換時(shí)出現(xiàn)寬度縮小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
解決vue做詳情頁(yè)跳轉(zhuǎn)的時(shí)候使用created方法 數(shù)據(jù)不會(huì)更新問(wèn)題
這篇文章主要介紹了解決vue做詳情頁(yè)跳轉(zhuǎn)的時(shí)候使用created方法 數(shù)據(jù)不會(huì)更新問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決
這篇文章主要為大家介紹了vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
Vue CLI 3.x 自動(dòng)部署項(xiàng)目至服務(wù)器的方法
本教程講解的是 Vue-CLI 3.x 腳手架搭建的vue項(xiàng)目, 利用scp2自動(dòng)化部署到靜態(tài)文件服務(wù)器 Nginx。非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2019-04-04

