如何检测何时释放所有键

How to detect when all keys are released

下面的代码应该测量一个键(或多个键)被按下的时间。

var output = document.getElementById('output'),
    pressed = {};

window.onkeydown = function(e) {
    if ( pressed[e] ) return;
    pressed[e] = e.timeStamp;
};

window.onkeyup = function(e) {
    if ( !pressed[e] ) return;   
    var duration = ( e.timeStamp - pressed[e] ) / 1000;
    var today = new Date();
    var date = today.getDate()+'-'+(today.getMonth()+1)+'-'+today.getFullYear();
    var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    var dateTime = date+' '+time; 
    output.innerHTML += dateTime + ' - Key(s) pressed for ' + duration + ' seconds</p>';
    pressed[e] = 0;
}
<div id="output"></div>

有什么办法可以改变window.onkeyup函数,让它只在释放所有键时输出日志吗?

示例:

10:00:00 - 我按住 A

10:00:05 - 我按住 B

10:00:20 - 我按住 C

10:00:22 - 我放A

10:00:24 - 我释放 B

10:00:30 - 我释放 C

当前代码输出:

26-2-2021 10:00:22 - 按键持续 22 秒

26-2-2021 10:00:24 - 按键持续 2 秒

26-2-2021 10:00:30 - 按键持续 6 秒

预期输出:

26-2-2021 10:00:30 - 按键持续 3​​0 秒

感谢您的帮助。

您可以用另一个值改进您的 pressed 地图,即 is_down 并在最后检查它。

window.onkeydown = function(e) {
    if ( pressed[e.key] ) return;
    pressed[e.key] = {time:e.timeStamp, is_down:true}
};

然后在发布时检查是否有人 is_down:true

window.onkeyup = function(e) {
    if ( !pressed?.is_down ) return;   
    var duration = ( e.timeStamp - pressed[e.key].time ) / 1000;
    var is_all_released = Object.keys(pressed).filter(key => pressed[key].is_down).length > 0
    if (is_all_released) {
        //do your printouts
    }

首先,你应该做 pressed[e.code],而不是 pressed[e],因为这个 e 对象值将被字符串化为 [object KeyboardEvent],所以你只分配给一个和相同的属性,在每个键盘事件上。

要仅在所有键都弹起时报告,请跟踪计数和按下第一个键的时间:

var output = document.getElementById('output'),
    pressed = {},
    keyDownCount = 0,
    keyDownTime = 0;

window.onkeydown = function(e) {
    if ( pressed[e.code] ) return;
    pressed[e.code] = true;
    if (!keyDownCount) keyDownTime = e.timeStamp;
    keyDownCount++;
};
    
window.onkeyup = function(e) {
    if ( !pressed[e.code] ) return;
    pressed[e.code] = false;
    keyDownCount--;
    if (keyDownCount) return;
    var duration = ( e.timeStamp - keyDownTime ) / 1000;
    var today = new Date();
    var date = today.getDate()+'-'+(today.getMonth()+1)+'-'+today.getFullYear();
    var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    var dateTime = (date+' '+time).replace(/\b\d\b/g, "0$&"); 
    output.innerHTML += dateTime + ' - Key(s) pressed for ' + duration + ' seconds</p>';
}
<div id="output"></div>

请注意,这并非完全防弹。如果有人关注另一个 window 并更改返回前按下的键,这些更改将不会触发事件。

要使它正常工作,您需要进行一些更改。 首先,当您在释放所有键后重置缓存的时间戳时,您应该删除条目而不是仅仅将时间戳设置为 0 - 这样您将在下次按下多个键时再次从一个空对象开始。

您可以为此使用 delete 关键字。

此外,据我了解,您实际上只想比较第一个时间戳和最后一个时间戳,以获得您按下按键的总时间。所以我建议你的解决方案是(我也重构了一点):

const output = document.getElementById('output');
let pressed = {};

window.onkeydown = function (e) {
  if (Object.values(pressed).length <= 1) {
    pressed.start = new Date();
  }
  pressed[e.code] = e.timeStamp;
};

window.onkeyup = function (e) {
  delete pressed[e.code];
  const { start, ...rest } = pressed;
  if (rest.length <= 1) {
    const currentTime = new Date();
    const duration = (currentTime - start) / 1000;
    const date = `${currentTime.getDate()}-${currentTime.getMonth() + 1}-${currentTime.getFullYear()}`;
    const time = `${currentTime.getHours()}:${currentTime.getMinutes() + 1}:${currentTime.getSeconds()}`;
    output.innerHTML += `${date} ${time} - Key(s) pressed for ${duration} seconds</p>`;
    delete pressed.start;
  }
};

您可以使用 Set 然后在从集合中删除最后一个键时执行一些代码(如下面的 onRelease 方法)。要知道持续时间是多少,您还必须跟踪按下第一个键的时间。

下面的示例将逻辑和回调函数移动到 pressed 对象中,但您可以通过多种方式进行设置。

const output = document.getElementById("output");

function log(text) {
  const p = document.createElement("p");
  p.append(text);
  output.append(p);
}

const pressed = {
  keys: new Set(),
  timestamp: null,
  get isEmpty() { return !this.keys.size },
  add(key) {
    // save the current timestamp when adding the first key
    if (this.isEmpty) this.timestamp = Date.now();
    return this.keys.add(key);
  },
  delete(key) {
    const wasPresent = this.keys.delete(key);
    // trigger onRelease when deleting the last key
    if (wasPresent && this.isEmpty) this.onRelease();
    return wasPresent;
  },
  onRelease() {
    const now = new Date();
    const duration = now.getTime() - this.timestamp;
    log(`${now.toLocaleString()} - Key(s) pressed for ${duration}ms`);
  }
};

document.addEventListener("keydown", ({ code }) => pressed.add(code));
document.addEventListener("keyup", ({ code }) => pressed.delete(code));
<div id="output"></div>

与其他答案类似,“keydown”和“keyup”只有在当前选项卡中的页面处于活动状态时才会触发。当键是 pressed/released 而在另一个 tab/window 上时,它们将不会注册到 JavaScript。