事情是这样子的,作为一个农村语文老师,想着用一个网页实现检测学生的朗读声波,然后进行针对性指导,也让学生看到自己的朗读有问题的时候是什么样子。
然后我就用小蓝鲸帮我生成一个网页。
然后我发现,效果还不错,但是我需要的历史记录播放出现了问题:它只能播放一次,而且不能移动播放的进度条,一移动就没了。
有大佬帮我看看吗?
代码如下(小白没有一点编程知识):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>朗读检测系统</title>
<style>
body {
background-color: #f0f8ff;
background-image: url('
');
background-repeat: no-repeat;
background-position: bottom;
background-size: contain;
font-family: "Microsoft YaHei", Arial, sans-serif;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
color: #007bff;
background-color: #fff;
padding: 20px;
margin: 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.container {
display: flex;
height: calc(100vh - 60px);
}
.bar-chart {
flex: 3;
display: flex;
align-items: flex-end;
justify-content: center;
padding: 20px;
position: relative;
overflow: hidden;
}
.bar {
width: 10px;
background-color: #007bff;
margin: 0 2px;
transition: height 0.1s;
border-radius: 2px 2px 0 0;
}
.level-line {
position: absolute;
left: 0;
right: 0;
height: 2px;
background-color: #ff4444;
opacity: 0.5;
}
.controls {
flex: 1;
padding: 20px;
background-color: rgba(255, 255, 255, 0.95);
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1);
overflow-y: auto;
}
.controls label {
display: block;
margin-bottom: 10px;
color: #333;
}
.controls input[type="range"] {
width: 100%;
margin-bottom: 20px;
}
.controls button {
display: block;
width: 100%;
padding: 15px;
margin-bottom: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
font-size: 16px;
}
.controls button:hover {
background-color: #0056b3;
transform: translateY(-1px);
}
.history {
margin-top: 20px;
}
.history-item {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
border-radius: 6px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.history-item audio {
width: 100%;
margin-top: 10px;
}
.history-bar-chart {
display: flex;
align-items: flex-end;
justify-content: flex-start;
margin-top: 15px;
height: 80px;
overflow-x: auto;
padding: 5px 0;
background: #f8f9fa;
border-radius: 4px;
}
.history-bar-chart .bar {
width: 4px;
margin: 0 1px;
background-color: #28a745;
border-radius: 2px;
}
</style>
</head>
<body>
<h1>智能朗读检测系统</h1>
<div class="container">
<div class="bar-chart" id="barChart"></div>
<div class="controls">
<label for="sensitivity">声音灵敏度: <span id="sensitivityValue">5</span></label>
<input type="range" id="sensitivity" min="1" max="10" value="5">
<button id="micSwitch">🎤 打开麦克风</button>
<div class="history">
<h3>📁 历史记录</h3>
<div id="historyList"></div>
</div>
</div>
</div>
<script>
const barChart = document.getElementById('barChart');
const sensitivityInput = document.getElementById('sensitivity');
const micSwitch = document.getElementById('micSwitch');
const historyList = document.getElementById('historyList');
const sensitivityValue = document.getElementById('sensitivityValue');
let mediaRecorder;
let audioChunks = [];
let isRecording = false;
let bars = [];
let audioContext;
let analyser;
let source;
let previousVolume = 0;
let intervalId;
// 初始化音量参考线
[20, 40, 60, 80].forEach(level => {
const line = document.createElement('div');
line.className = 'level-line';
line.style.bottom = `${level}%`;
barChart.appendChild(line);
});
// 灵敏度控制
sensitivityInput.addEventListener('input', () => {
sensitivityValue.textContent = sensitivityInput.value;
});
// 麦克风控制
micSwitch.addEventListener('click', async () => {
try {
if (isRecording) {
stopRecording();
} else {
await startRecording();
}
} catch (error) {
console.error('麦克风错误:', error);
alert('无法访问麦克风,请检查权限设置');
}
});
async function startRecording() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
analyser.smoothingTimeConstant = 0.2;
analyser.fftSize = 256;
source = audioContext.createMediaStreamSource(stream);
source.connect(analyser);
mediaRecorder = new MediaRecorder(stream);
setupMediaRecorder();
startVisualization();
micSwitch.textContent = '⏹️ 停止录制';
isRecording = true;
}
function stopRecording() {
mediaRecorder.stop();
clearInterval(intervalId);
bars.forEach(bar => bar.remove());
bars = [];
micSwitch.textContent = '🎤 开始录制';
isRecording = false;
}
function setupMediaRecorder() {
mediaRecorder.ondataavailable = e => audioChunks.push(e.data);
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
audioChunks = [];
addHistoryItem(audioBlob);
};
mediaRecorder.start();
}
function startVisualization() {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
intervalId = setInterval(() => {
analyser.getByteTimeDomainData(dataArray);
updateVisualization(dataArray, bufferLength, barChart, bars);
}, 30);
}
function updateVisualization(dataArray, bufferLength, container, currentBars) {
const sensitivity = sensitivityInput.value / 2;
let sum = 0;
for (let i = 0; i < bufferLength; i++) {
const value = (dataArray[i] - 128) / 128;
sum += value * value;
}
const rms = Math.sqrt(sum / bufferLength);
const volume = Math.min(rms * 100 * sensitivity, 100);
const smoothedVolume = previousVolume * 0.6 + volume * 0.4;
createVolumeBar(smoothedVolume, container, currentBars);
previousVolume = smoothedVolume;
}
function createVolumeBar(volume, container, currentBars) {
const bar = document.createElement('div');
bar.className = 'bar';
bar.style.height = `${volume}%`;
container.appendChild(bar);
currentBars.push(bar);
if (currentBars.length > 60) {
const removedBar = currentBars.shift();
removedBar.remove();
}
}
function addHistoryItem(audioBlob) {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.controls = true;
const barContainer = document.createElement('div');
barContainer.className = 'history-bar-chart';
historyItem.append(audio, barContainer);
historyList.prepend(historyItem);
audio.addEventListener('play', () => {
// 每次播放时重新初始化
barContainer.innerHTML = '';
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyser = audioContext.createAnalyser();
analyser.smoothingTimeConstant = 0.2;
analyser.fftSize = 256;
const source = audioContext.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(audioContext.destination);
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
let historyBars = [];
const intervalId = setInterval(() => {
if (audio.paused) {
clearInterval(intervalId);
audioContext.close();
return;
}
analyser.getByteTimeDomainData(dataArray);
updateVisualization(dataArray, bufferLength, barContainer, historyBars);
}, 30);
});
}
</script>
</body>
</html>
然后我就用小蓝鲸帮我生成一个网页。
然后我发现,效果还不错,但是我需要的历史记录播放出现了问题:它只能播放一次,而且不能移动播放的进度条,一移动就没了。
有大佬帮我看看吗?
代码如下(小白没有一点编程知识):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>朗读检测系统</title>
<style>
body {
background-color: #f0f8ff;
background-image: url('
');
background-repeat: no-repeat;
background-position: bottom;
background-size: contain;
font-family: "Microsoft YaHei", Arial, sans-serif;
margin: 0;
padding: 0;
}
h1 {
text-align: center;
color: #007bff;
background-color: #fff;
padding: 20px;
margin: 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.container {
display: flex;
height: calc(100vh - 60px);
}
.bar-chart {
flex: 3;
display: flex;
align-items: flex-end;
justify-content: center;
padding: 20px;
position: relative;
overflow: hidden;
}
.bar {
width: 10px;
background-color: #007bff;
margin: 0 2px;
transition: height 0.1s;
border-radius: 2px 2px 0 0;
}
.level-line {
position: absolute;
left: 0;
right: 0;
height: 2px;
background-color: #ff4444;
opacity: 0.5;
}
.controls {
flex: 1;
padding: 20px;
background-color: rgba(255, 255, 255, 0.95);
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1);
overflow-y: auto;
}
.controls label {
display: block;
margin-bottom: 10px;
color: #333;
}
.controls input[type="range"] {
width: 100%;
margin-bottom: 20px;
}
.controls button {
display: block;
width: 100%;
padding: 15px;
margin-bottom: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
font-size: 16px;
}
.controls button:hover {
background-color: #0056b3;
transform: translateY(-1px);
}
.history {
margin-top: 20px;
}
.history-item {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
border-radius: 6px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.history-item audio {
width: 100%;
margin-top: 10px;
}
.history-bar-chart {
display: flex;
align-items: flex-end;
justify-content: flex-start;
margin-top: 15px;
height: 80px;
overflow-x: auto;
padding: 5px 0;
background: #f8f9fa;
border-radius: 4px;
}
.history-bar-chart .bar {
width: 4px;
margin: 0 1px;
background-color: #28a745;
border-radius: 2px;
}
</style>
</head>
<body>
<h1>智能朗读检测系统</h1>
<div class="container">
<div class="bar-chart" id="barChart"></div>
<div class="controls">
<label for="sensitivity">声音灵敏度: <span id="sensitivityValue">5</span></label>
<input type="range" id="sensitivity" min="1" max="10" value="5">
<button id="micSwitch">🎤 打开麦克风</button>
<div class="history">
<h3>📁 历史记录</h3>
<div id="historyList"></div>
</div>
</div>
</div>
<script>
const barChart = document.getElementById('barChart');
const sensitivityInput = document.getElementById('sensitivity');
const micSwitch = document.getElementById('micSwitch');
const historyList = document.getElementById('historyList');
const sensitivityValue = document.getElementById('sensitivityValue');
let mediaRecorder;
let audioChunks = [];
let isRecording = false;
let bars = [];
let audioContext;
let analyser;
let source;
let previousVolume = 0;
let intervalId;
// 初始化音量参考线
[20, 40, 60, 80].forEach(level => {
const line = document.createElement('div');
line.className = 'level-line';
line.style.bottom = `${level}%`;
barChart.appendChild(line);
});
// 灵敏度控制
sensitivityInput.addEventListener('input', () => {
sensitivityValue.textContent = sensitivityInput.value;
});
// 麦克风控制
micSwitch.addEventListener('click', async () => {
try {
if (isRecording) {
stopRecording();
} else {
await startRecording();
}
} catch (error) {
console.error('麦克风错误:', error);
alert('无法访问麦克风,请检查权限设置');
}
});
async function startRecording() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
audioContext = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioContext.createAnalyser();
analyser.smoothingTimeConstant = 0.2;
analyser.fftSize = 256;
source = audioContext.createMediaStreamSource(stream);
source.connect(analyser);
mediaRecorder = new MediaRecorder(stream);
setupMediaRecorder();
startVisualization();
micSwitch.textContent = '⏹️ 停止录制';
isRecording = true;
}
function stopRecording() {
mediaRecorder.stop();
clearInterval(intervalId);
bars.forEach(bar => bar.remove());
bars = [];
micSwitch.textContent = '🎤 开始录制';
isRecording = false;
}
function setupMediaRecorder() {
mediaRecorder.ondataavailable = e => audioChunks.push(e.data);
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
audioChunks = [];
addHistoryItem(audioBlob);
};
mediaRecorder.start();
}
function startVisualization() {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
intervalId = setInterval(() => {
analyser.getByteTimeDomainData(dataArray);
updateVisualization(dataArray, bufferLength, barChart, bars);
}, 30);
}
function updateVisualization(dataArray, bufferLength, container, currentBars) {
const sensitivity = sensitivityInput.value / 2;
let sum = 0;
for (let i = 0; i < bufferLength; i++) {
const value = (dataArray[i] - 128) / 128;
sum += value * value;
}
const rms = Math.sqrt(sum / bufferLength);
const volume = Math.min(rms * 100 * sensitivity, 100);
const smoothedVolume = previousVolume * 0.6 + volume * 0.4;
createVolumeBar(smoothedVolume, container, currentBars);
previousVolume = smoothedVolume;
}
function createVolumeBar(volume, container, currentBars) {
const bar = document.createElement('div');
bar.className = 'bar';
bar.style.height = `${volume}%`;
container.appendChild(bar);
currentBars.push(bar);
if (currentBars.length > 60) {
const removedBar = currentBars.shift();
removedBar.remove();
}
}
function addHistoryItem(audioBlob) {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.controls = true;
const barContainer = document.createElement('div');
barContainer.className = 'history-bar-chart';
historyItem.append(audio, barContainer);
historyList.prepend(historyItem);
audio.addEventListener('play', () => {
// 每次播放时重新初始化
barContainer.innerHTML = '';
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyser = audioContext.createAnalyser();
analyser.smoothingTimeConstant = 0.2;
analyser.fftSize = 256;
const source = audioContext.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(audioContext.destination);
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
let historyBars = [];
const intervalId = setInterval(() => {
if (audio.paused) {
clearInterval(intervalId);
audioContext.close();
return;
}
analyser.getByteTimeDomainData(dataArray);
updateVisualization(dataArray, bufferLength, barContainer, historyBars);
}, 30);
});
}
</script>
</body>
</html>