为什么这个程序中的运动仍然伴随着keydown事件的发射?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么这个程序中的运动仍然伴随着keydown事件的发射?相关的知识,希望对你有一定的参考价值。

将红色方块添加为DOM节点并连接到键盘上的箭头键。

我自己的键盘处理逻辑试图规避与keydown事件发射相关的延迟和缓和。按下箭头键时,方块应立即全速移动。

但广场的运动仍然遵循keydown事件的发射。

为什么是这样?

这段代码的工作方式是:

  • 一个keyPresses对象,它维护每个箭头键码的按键列表及其持续时间
  • 这些列表的长度通常为零或一,因为,
  • 列表将重置每个动画帧
  • 我依靠requestAnimationFrame希望能够停止依赖keydown事件
  • 37383940魔术数字是箭头键的键码

const ROOT_NODE = document.getElementById('root');
const PLAYER_NODE = document.createElement('div');
const createKeyPressStore = () => ({ 37: [], 38: [], 39: [], 40: [] });

const plus = (a, b) => a + b;
const minus = (a, b) => a - b;
const isArrow = ({ keyCode }) => [37, 38, 39, 40].includes(keyCode);
const translate = (axis, transform) => ([x, y], translation) =>
  axis === 'x' ? [transform(x, translation), y] : [x, transform(y, translation)];
const normalise = (n, normal) => (n < normal ? normal : n);
const distance = (duration, velocity) => ~~(normalise(duration, 16) / 16 * velocity);
const moveMap = {
  37: translate('x', minus),
  38: translate('y', minus),
  39: translate('x', plus),
  40: translate('y', plus)
};
const calcLeft = ({ style: { left } }, [x]) => +left.substr(0, left.length - 2) + x;
const calcTop = ({ style: { top } }, [_, y]) => +top.substr(0, top.length - 2) + y;
const resetKeyPresses = store => ((store.keyPresses = createKeyPressStore()), store);
const now = () => ~~performance.now();
const createStore = () => ({
  rootNode: ROOT_NODE,
  player: { node: PLAYER_NODE, velocity: 10 },
  keyPresses: createKeyPressStore()
});

const onkeydown = ({ keyPresses }, e) => (
  isArrow(e) && (keyPresses[e.keyCode][0] && !keyPresses[e.keyCode][0].stop && (keyPresses[e.keyCode][0].stop = now())),
  keyPresses[e.keyCode].unshift({ start: now() }),
  false
);

const onkeyup = ({ keyPresses }, e) =>
  isArrow(e) && keyPresses[e.keyCode][0] && (keyPresses[e.keyCode][0].stop = now());

const calcTranslation = (store, result = [0, 0]) => (
  (result = Object.entries(store.keyPresses).reduce(
    (p, [key, list]) =>
      list.reduce(
        (p1, { start, stop }) => (
          (stop = stop || start), moveMap[key](p1, distance(stop - start, store.player.velocity))
        ),
        p
      ),
    result
  )),
  (store.keyPresses = createKeyPressStore()),
  result
);

const draw = (store, translation = calcTranslation(store), node = store.player.node) => (
  (node.style.left = `${calcLeft(node, translation)}px`),
  (node.style.top = `${calcTop(node, translation)}px`),
  resetKeyPresses(store)
);

const listenForEvents = store => (
  ((window.document.onkeydown = e => onkeydown(store, e)), (window.document.onkeyup = e => onkeyup(store, e))), store
);

const initDOM = store => (
  store.player.node.setAttribute('id', 'player'), store.rootNode.appendChild(store.player.node), store
);

const go = store => (draw(store), requestAnimationFrame(() => go(store)));

go(listenForEvents(initDOM(createStore())));
#root { 
  box-shadow: 0 0 0 10px rgba(0,0,0,1) inset;
  width: 100%;
  height: 100%;
  position: absolute;
  margin: 0;
}

#player { 
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body id="root">

  <script src="./script.js"></script>
</body>
</html>
答案

我想我知道答案。问题是每个动画帧都会通过此代码有效地取消正在进行的印刷。

以下代码解决了该问题。

const ROOT_NODE = document.getElementById('root');
const PLAYER_NODE = document.createElement('div');
const createKeyPressStore = () => ({ 37: [], 38: [], 39: [], 40: [] });

const plus = (a, b) => a + b;
const minus = (a, b) => a - b;
const isArrow = ({ keyCode }) => [37, 38, 39, 40].includes(keyCode);
const translate = (axis, transform) => ([x, y], translation) =>
  axis === 'x' ? [transform(x, translation), y] : [x, transform(y, translation)];
const normalise = (n, normal) => (n < normal ? normal : n);
const distance = (duration, velocity) => ~~(normalise(duration, 16) / 16 * velocity);
const moveMap = {
  37: translate('x', minus),
  38: translate('y', minus),
  39: translate('x', plus),
  40: translate('y', plus)
};
const calcLeft = ({ style: { left } }, [x]) => +left.substr(0, left.length - 2) + x;
const calcTop = ({ style: { top } }, [_, y]) => +top.substr(0, top.length - 2) + y;
const resetKeyPresses = store => ((store.keyPresses = createKeyPressStore()), store);
const now = () => ~~performance.now();
const createStore = () => ({
  rootNode: ROOT_NODE,
  player: { node: PLAYER_NODE, velocity: 2 },
  keyPresses: createKeyPressStore()
});

const onkeydown = ({ keyPresses }, e) => (
  isArrow(e) && (keyPresses[e.keyCode][0] && !keyPresses[e.keyCode][0].stop && (keyPresses[e.keyCode][0].stop = now())),
  keyPresses[e.keyCode].unshift({ start: now() }),
  false
);

const onkeyup = ({ keyPresses }, e) =>
  isArrow(e) && keyPresses[e.keyCode][0] && (keyPresses[e.keyCode][0].stop = now());

const calcTranslation = (store, result = [0, 0], inProgress = createKeyPressStore()) => (
  (result = Object.entries(store.keyPresses).reduce(
    (p, [key, list]) =>
      list.reduce(
        (p1, { start, stop }) => (
          !stop && start && inProgress[key].unshift({ start: now() }),
          moveMap[key](p1, distance((stop || start) - start, store.player.velocity))
        ),
        p
      ),
    result
  )),
  (store.keyPresses = inProgress),
  result
);

const draw = (store, translation = calcTranslation(store), node = store.player.node) => (
  (node.style.left = `${calcLeft(node, translation)}px`),
  (node.style.top = `${calcTop(node, translation)}px`)
);

const listenForEvents = store => (
  ((window.document.onkeydown = e => onkeydown(store, e)), (window.document.onkeyup = e => onkeyup(store, e))), store
);

const initDOM = store => (
  store.player.node.setAttribute('id', 'player'), store.rootNode.appendChild(store.player.node), store
);

const go = store => (draw(store), requestAnimationFrame(() => go(store)));

go(listenForEvents(initDOM(createStore())));
#root { 
  box-shadow: 0 0 0 10px rgba(0,0,0,1) inset;
  width: 100%;
  height: 100%;
  position: absolute;
  margin: 0;
}

#player { 
  background: red;
  width: 50px;
  height: 50px;
  position: absolute;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body id="root">

  <script src="./script.js"></script>
</body>
</html>

以上是关于为什么这个程序中的运动仍然伴随着keydown事件的发射?的主要内容,如果未能解决你的问题,请参考以下文章

jquery_api事件

网格中的 Keypress 或 keydown 事件处理

threejs+blender页面滚动伴随着三维动画

KeyDown,KeyPress和KeyUp详解(转)

c# keydown事件问题

jQuery的切换函数(hover,toggle)