<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>按住录音 Demo</title>
<style>
#recordButton {
width: 80px;
height: 80px;
background-color: #f44336;
border-radius: 50%;
border: none;
color: white;
font-size: 16px;
cursor: pointer;
margin: 20px;
outline: none;
}
#recordButton:active {
background-color: #d32f2f;
}
#status {
margin: 10px;
font-weight: bold;
}
.recording {
color: red;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
audio {
display: block;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>语音录制 Demo</h1>
<button id="recordButton">按住录音</button>
<div id="status">准备就绪</div>
<div id="recordingsList"></div>
<script>
let mediaRecorder;
let audioChunks = [];
const recordButton = document.getElementById('recordButton');
const statusDisplay = document.getElementById('status');
const recordingsList = document.getElementById('recordingsList');
async function initRecorder() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
createAudioElement(audioBlob);
audioChunks = [];
statusDisplay.textContent = "录音完成";
};
statusDisplay.textContent = "麦克风已启用";
} catch (error) {
statusDisplay.textContent = "麦克风权限被拒绝或设备不支持";
console.error("录音初始化失败:", error);
}
}
function createAudioElement(blob) {
const audioURL = URL.createObjectURL(blob);
const audioElement = document.createElement('audio');
const downloadLink = document.createElement('a');
audioElement.controls = true;
audioElement.src = audioURL;
downloadLink.href = audioURL;
downloadLink.download = `recording-${new Date().toISOString()}.webm`;
downloadLink.textContent = "下载录音";
const container = document.createElement('div');
container.appendChild(audioElement);
container.appendChild(downloadLink);
container.appendChild(document.createElement('hr'));
recordingsList.prepend(container);
}
recordButton.addEventListener('mousedown', async () => {
console.log('mouse');
if (!mediaRecorder) await initRecorder();
if (mediaRecorder.state === 'inactive') {
mediaRecorder.start();
statusDisplay.textContent = "录音中...";
statusDisplay.classList.add('recording');
}
});
recordButton.addEventListener('mouseup', () => {
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
statusDisplay.classList.remove('recording');
}
});
recordButton.addEventListener('touchstart', async (e) => {
console.log('touch');
e.preventDefault();
if (!mediaRecorder) await initRecorder();
if (mediaRecorder.state === 'inactive') {
mediaRecorder.start();
statusDisplay.textContent = "录音中...";
statusDisplay.classList.add('recording');
}
});
recordButton.addEventListener('touchend', (e) => {
e.preventDefault();
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
statusDisplay.classList.remove('recording');
}
});
</script>
</body>
</html>