You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

空格键按下与抬起的时间测量不准确问题求助

空格键按住时长测量异常问题

我正在开发一个空格键按住时长的测量功能,通过记录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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:24:57