我正在测量用户输入一些文本时按键之间的时间,使用
performance.now()
并保存它。现在,我想重播用户输入的内容,可能让他能够与自己比赛,再次输入相同的文本。
但是,如果我使用以下结构:
for (let ev of events) {
await new Promise(resolve => setTimeout(resolve, ev.performanceDelay));
replayKey();
}
我注意到正在重播的文本的输入速度比之前输入的速度慢。例如,我在 40 秒内输入整个文本,但重播运行了 45 秒,使得任何与自己赛跑的可能性都过时了。
我的问题是:我怎样才能让它发挥作用?有办法吗? 我想将一个操作延迟特定的时间,最好精确到微秒(performance.now() 的输出),显然不会挂起主线程或阻止任何其他事件发生。
我不介意深入研究特定浏览器的特定 API,例如 Chrome(因为我主要运行它)。有办法让这个工作吗?
更新
目前我能想到的解决“竞赛”解决方案的唯一方法是以某种方式将按键事件聚合成更大的块并按这些块重播它们,例如至少 20-50 毫秒长度。然而,我也希望将其作为一个分析工具,以便用户事后可以看到他的打字会话的模拟,以便能够发现他速度慢或犯错误等的地方。所以如果能够重播他的确切输入。但我开始认为这是不可能的,因为用户几乎可以立即甚至同时按下按键,但你不能真正在浏览器 API 上创建接近零的延迟(我猜?)。
使用
requestAnimationFrame
和时间戳偏移量,不要依赖于 setTimeout
或 requestAnimationFrame
的精度:
let start = performance.now();
const keys = [...'abcdefgiklmopqrstuwxyz'];
const events = Array.from({length:100}, () => ({key: keys[Math.random()*keys.length|0], timestamp: start += Math.random() * 100}));
(async()=>{
let idx = 0;
let start = performance.now();
let from = events[0].timestamp;
while(idx < events.length){
const now = performance.now();
const ev = events[idx];
if(now - start >= ev.timestamp - from){
$key.textContent = `${idx}: ${ev.key}`;
idx++;
continue; // if several keys are between frames, just issue them together 🤷♂️
}
await new Promise(resolve => requestAnimationFrame(resolve));
}
})();
<div id="$key" style="font-size:32px"></div>