🎧 听力测试
点击播放按钮开始测试
250Hz
📊 听力检测结果
📖 测试说明
使用方法:
1. 请在安静环境中测试,佩戴耳机效果更佳
2. 点击播放按钮,仔细聆听是否有声音
3. 如听到声音点「听到了」,没听到点「没听到」
4. 共测试6个频率,每个频率会自动调整音量

⚠️ 注意:本测试仅为自测参考,受设备音量、环境噪音等因素影响较大。如有听力问题,请到专业医疗机构进行纯音测听检查。
const FREQUENCIES = [250, 500, 1000, 2000, 4000, 8000]; const FREQ_LABELS = ['250Hz', '500Hz', '1kHz', '2kHz', '4kHz', '8kHz']; let audioCtx = null; let currentFreqIdx = 0; let currentVolume = 50; // dB equivalent (0-100) let testResults = []; let oscillator = null; let gainNode = null; let isPlaying = false; function playTone() { if (isPlaying) return; if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const freq = FREQUENCIES[currentFreqIdx]; // Map volume to gain (approximate) // 0dB HL ≈ 0.001, normal hearing threshold // We'll use a simpler mapping for web audio const baseGain = 0.00001; const maxGain = 0.3; // Volume 0=very quiet, 100=loud let gain = baseGain * Math.pow(10, (currentVolume / 100) * 4); if (gain > maxGain) gain = maxGain; oscillator = audioCtx.createOscillator(); gainNode = audioCtx.createGain(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(freq, audioCtx.currentTime); gainNode.gain.setValueAtTime(gain, audioCtx.currentTime); // Fade out after 1 second gainNode.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + 1.5); oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 1.5); isPlaying = true; document.getElementById('playBtn').textContent = '♪'; document.getElementById('playBtn').classList.add('playing'); document.getElementById('answerRow').style.display = 'flex'; setTimeout(() => { isPlaying = false; document.getElementById('playBtn').textContent = '▶'; document.getElementById('playBtn').classList.remove('playing'); }, 1600); } function answerTest(heard) { if (!heard) { // Increase volume and try again currentVolume += 15; if (currentVolume > 95) { // Record as threshold exceeded recordResult(currentVolume); } else { document.getElementById('statusText').textContent = `增大音量,再次试听...`; isPlaying = false; document.getElementById('playBtn').textContent = '▶'; document.getElementById('playBtn').classList.remove('playing'); return; } } else { recordResult(currentVolume); } } function recordResult(vol) { testResults.push({ freq: FREQUENCIES[currentFreqIdx], label: FREQ_LABELS[currentFreqIdx], threshold: vol, heard: vol <= 80 }); currentFreqIdx++; currentVolume = 25; // Reset for next frequency updateProgress(); if (currentFreqIdx >= FREQUENCIES.length) { showResults(); } else { document.getElementById('freqDisplay').innerHTML = FREQUENCIES[currentFreqIdx] + 'Hz'; document.getElementById('answerRow').style.display = 'none'; document.getElementById('statusText').textContent = `请播放下一个频率 (${FREQ_LABELS[currentFreqIdx]})`; isPlaying = false; document.getElementById('playBtn').textContent = '▶'; document.getElementById('playBtn').classList.remove('playing'); } } function updateProgress() { const pct = (currentFreqIdx / FREQUENCIES.length) * 100; document.getElementById('progressFill').style.width = pct + '%'; } function startTest() { currentFreqIdx = 0; currentVolume = 25; testResults = []; isPlaying = false; document.getElementById('testCard').style.display = 'block'; document.getElementById('resultCard').style.display = 'none'; document.getElementById('freqDisplay').innerHTML = FREQUENCIES[0] + 'Hz'; document.getElementById('playBtn').textContent = '▶'; document.getElementById('playBtn').classList.remove('playing'); document.getElementById('answerRow').style.display = 'none'; document.getElementById('statusText').textContent = '点击播放按钮开始测试'; updateProgress(); } function showResults() { document.getElementById('testCard').style.display = 'none'; document.getElementById('resultCard').style.display = 'block'; // Calculate average threshold const avgThreshold = Math.round(testResults.reduce((s,r)=>s+r.threshold,0)/testResults.length); const maxThreshold = Math.max(...testResults.map(r=>r.threshold)); let level, cls, advice; if (avgThreshold <= 35) { level = '✅ 听力正常'; cls = 'level-normal'; advice = '您的听力在正常范围内!建议继续保持良好的用耳习惯,避免长时间大音量使用耳机。'; } else if (avgThreshold <= 50) { level = '🔶 轻度听力损失'; cls = 'level-mild'; advice = '存在轻度听力损失,可能在高频部分较为明显。建议减少噪音暴露,必要时就医检查。'; } else if (avgThreshold <= 70) { level = '🔴 中度听力损失'; cls = 'level-moderate'; advice = '存在中度听力损失。建议尽快到耳鼻喉科或听力中心做专业测听检查。'; } else if (avgThreshold <= 90) { level = '⛔ 重度听力损失'; cls = 'level-severe'; advice = '存在重度听力损失!请务必尽快就医,可能需要助听器等辅助设备。'; } else { level = '🚨 极重度听力损失'; cls = 'level-profound'; advice = '极重度听力损失!请立即就医进行全面听力评估。'; } document.getElementById('resultLevel').className = 'result-level ' + cls; document.getElementById('resultLevel').textContent = level; // Frequency grid let gridHtml = ''; testResults.forEach(r => { let statusColor = r.heard ? '#4CAF50' : '#F44336'; gridHtml += `
${r.threshold}dB
${r.label}
`; }); document.getElementById('freqGrid').innerHTML = gridHtml; document.getElementById('resultAdvice').innerHTML = '💡 建议:' + advice; drawAudiogram(); } function drawAudiogram() { const canvas = document.getElementById('audiogramCanvas'); const ctx = canvas.getContext('2d'); const dpr = window.devicePixelRatio || 1; const w = canvas.clientWidth; const h = 280; canvas.width = w * dpr; canvas.height = h * dpr; ctx.scale(dpr, dpr); const padL = 45, padR = 20, padT = 20, padB = 35; const plotW = w - padL - padR; const plotH = h - padT - padB; ctx.fillStyle = '#fff'; ctx.fillRect(0,0,w,h); // Grid lines ctx.strokeStyle = '#E5E6EB'; ctx.lineWidth = 1; // Horizontal (frequency - log scale) for (let i = 0; i < FREQUENCIES.length; i++) { const x = padL + (i / (FREQUENCIES.length - 1)) * plotW; ctx.beginPath(); ctx.moveTo(x, padT); ctx.lineTo(x, h-padB); ctx.stroke(); } // Vertical (dB) for (let db = 0; db <= 100; db += 20) { const y = padT + (db / 100) * plotH; ctx.beginPath(); ctx.moveTo(padL, y); ctx.lineTo(w-padR, y); ctx.stroke(); } // Labels ctx.fillStyle = '#86909C'; ctx.font = '11px sans-serif'; ctx.textAlign = 'center'; FREQ_LABELS.forEach((l,i) => { const x = padL + (i/(FREQUENCIES.length-1))*plotW; ctx.fillText(l, x, h-10); }); ctx.textAlign = 'right'; for (let db = 0; db <= 100; db += 20) { const y = padT + (db/100)*plotH; ctx.fillText(db+'dB', padL-5, y+4); } // Plot data points & line ctx.beginPath(); ctx.strokeStyle = '#3370FF'; ctx.lineWidth = 2.5; testResults.forEach((r,i) => { const x = padL + (i/(testResults.length-1))*plotW; const y = padT + (Math.min(r.threshold,100)/100)*plotH; if (i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); }); ctx.stroke(); // Points testResults.forEach((r,i) => { const x = padL + (i/(testResults.length-1))*plotW; const y = padT + (Math.min(r.threshold,100)/100)*plotH; ctx.beginPath(); ctx.arc(x,y,5,0,Math.PI*2); ctx.fillStyle='#3370FF'; ctx.fill(); ctx.beginPath(); ctx.arc(x,y,3,0,Math.PI*2); ctx.fillStyle='#fff'; ctx.fill(); }); }