空格键按下与抬起的时间测量不准确问题求助
空格键按住时长测量异常问题
我正在开发一个空格键按住时长的测量功能,通过记录keydown触发时间与keyup触发时间的差值计算时长,但结果异常偏短,还出现反向关联——按住时间越久,显示的时长反而越短。我试过谷歌搜索、ChatGPT排查,也查阅了相关论坛并套用类似问题的修复方案,但这个bug始终无法解决。
相关代码
HTML
<div class="container"> <div id="page1"> <h2>Enter Details</h2> <label for="numTrials">Number of Trials:</label> <input type="number" id="numTrials" min="1" required> <br> <label for="lowerBound">Lower Bound Time (seconds):</label> <input type="number" id="lowerBound" min="1" required> <br> <label for="upperBound">Upper Bound Time (seconds):</label> <input type="number" id="upperBound" min="1" required> <br> <button id="startButton">Start</button> </div> <div id="page2" style="display: none;"> <h2>Countdown</h2> <div id="countdown">5</div> </div> <div id="page3" style="display: none;"> <p>Trial number: <span id="trial"></span></p> <h2>Number of seconds to hold the spacebar: <span id="random"></span></h2> <p>Press and hold the spacebar to record the duration.</p> </div> <div id="exportContainer" style="display: none;"> <button id="exportBtn">Export Data</button> <br> <button id="resetBtn">Reset</button> </div> <div id="imageContainer" style="display: none;"> <img src="https://t4.ftcdn.net/jpg/03/53/15/25/360_F_353152513_w2u3qD8i3aexYr07t2TIjIOZnftclZ7X.jpg" width="75" height="75"> </div> <div id="nextTrialContainer" style="display: none;"> <button id="nextTrial">Next Trial</button> </div> </div>
CSS
body { font-family: Arial, sans-serif; margin: 0; height: 100vh; display: flex; justify-content: center; align-items: center; } .container { text-align: center; } button { padding: 10px 20px; font-size: 16px; cursor: pointer; margin-top: 10px; } #countdown { font-size: 24px; margin-bottom: 20px; } #imageContainer { display: none; }
JavaScript
let trialsLeft; let lowerBound; let upperBound; let trialNumber = 1; let csvContent = "data:text/csv;charset=utf-8,Trial,Duration,Target Time\n"; let spacebarDownTime; let spacebarUpTime; let randomNumber = 0; function startCountdown() { document.getElementById('page1').style.display = 'none'; document.getElementById('page2').style.display = 'block'; document.getElementById('page3').style.display = 'none'; let countdownElement = document.getElementById('countdown'); let count = 5; countdownElement.textContent = 5; let timer = setInterval(() => { count--; countdownElement.textContent = count; if (count <= 0) { clearInterval(timer); displayRandomNumber(); } }, 1000); } function displayRandomNumber() { document.addEventListener('keydown', handleKeyDown); document.addEventListener('keyup', handleKeyUp); document.getElementById('page1').style.display = 'none'; document.getElementById('page2').style.display = 'none'; document.getElementById('page3').style.display = 'block'; randomNumber = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound; document.getElementById('page3').innerHTML = ` <p>Trial number: ${trialNumber}</p> <h2>Number of seconds to hold the spacebar: ${randomNumber}</h2> <p>Press and hold the spacebar to record the duration.</p> `; } function handleKeyDown(event) { if (event.key === ' ') { spacebarDownTime = new Date().getTime(); document.getElementById('imageContainer').style.display = 'block'; document.getElementById('page3').innerHTML = ` <p>Trial number: ${trialNumber}</p> <h2>Number of seconds to hold the spacebar: ${randomNumber}</h2> <p>Press and hold the spacebar to record the duration.</p> `; } } function handleKeyUp(event) { if (event.key === ' ') { spacebarUpTime = new Date().getTime(); document.getElementById('imageContainer').style.display = 'none'; let duration = spacebarUpTime - spacebarDownTime; if (duration > 0) { saveData(trialNumber, duration); } document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('keyup', handleKeyUp); nextAction(trialNumber); } } function nextAction(trialNumber) { if (trialNumber < trialsLeft) { document.getElementById('nextTrialContainer').style.display = 'block'; } else { displayExportButton(); } } function saveData(trialNumber, duration) { csvContent += `${trialNumber},${duration/1000} seconds,${randomNumber} seconds\n`; } function nextTrial() { document.getElementById('nextTrialContainer').style.display = 'none'; trialNumber++; if (trialNumber <= trialsLeft) { displayCountdown(); } else { displayExportButton(); } } function displayCountdown() { document.getElementById('page1').style.display = 'none'; document.getElementById('page2').style.display = 'block'; document.getElementById('page3').style.display = 'none'; startCountdown(); } function displayExportButton() { document.getElementById('page1').style.display = 'none'; document.getElementById('page2').style.display = 'none'; document.getElementById('page3').style.display = 'none'; document.getElementById('exportContainer').style.display = 'block'; } function exportData() { const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", "key_press_data.csv"); document.body.appendChild(link); link.click(); } function resetPage() { location.reload(); } document.getElementById('startButton').addEventListener('click', function() { trialsLeft = parseInt(document.getElementById('numTrials').value); lowerBound = parseInt(document.getElementById('lowerBound').value); upperBound = parseInt(document.getElementById('upperBound').value); if (isNaN(trialsLeft) || isNaN(lowerBound) || isNaN(upperBound)) { alert('Please enter valid numbers'); return; } if (lowerBound >= upperBound) { alert('Upper bound must be greater than lower bound'); return; } displayCountdown(); }); document.getElementById('exportBtn').addEventListener('click', exportData); document.getElementById('resetBtn').addEventListener('click', resetPage); document.getElementById('nextTrial').addEventListener('click', nextTrial);
问题原因分析
核心bug是浏览器的按键重复触发机制:按住空格键时,浏览器会每隔几十毫秒重复触发keydown事件,而你的代码每次触发keydown都会更新spacebarDownTime为当前时间。这导致最终计算时长时,用的是最后一次keydown的时间(几乎和keyup时间重合),所以结果偏短;按住越久,最后一次keydown离keyup越近,差值就越小,呈现反向关联。
修复方案
修改handleKeyDown函数,仅在第一次按下空格键时记录时间,后续重复触发的keydown不再更新;同时在keyup后重置记录值,确保下一次试验正常运行。
修复后的关键代码
function handleKeyDown(event) { // 仅在未记录过按下时间时执行逻辑 if (event.key === ' ' && !spacebarDownTime) { spacebarDownTime = new Date().getTime(); document.getElementById('imageContainer').style.display = 'block'; document.getElementById('page3').innerHTML = ` <p>Trial number: ${trialNumber}</p> <h2>Number of seconds to hold the spacebar: ${randomNumber}</h2> <p>Press and hold the spacebar to record the duration.</p> `; } } function handleKeyUp(event) { if (event.key === ' ') { spacebarUpTime = new Date().getTime(); document.getElementById('imageContainer').style.display = 'none'; let duration = spacebarUpTime - spacebarDownTime; if (duration > 0) { saveData(trialNumber, duration); } // 重置记录值,避免影响下一次试验 spacebarDownTime = undefined; document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('keyup', handleKeyUp); nextAction(trialNumber); } }
额外优化
在displayRandomNumber函数开头添加spacebarDownTime = undefined;,确保每次试验开始前状态都是干净的,避免上一次试验的残留值干扰。
内容的提问来源于stack exchange,提问作者Cruddyturtle
相关产品推荐
相关产品推荐

