为什么这个程序中的运动仍然伴随着keydown事件的发射?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么这个程序中的运动仍然伴随着keydown事件的发射?相关的知识,希望对你有一定的参考价值。
将红色方块添加为DOM节点并连接到键盘上的箭头键。
我自己的键盘处理逻辑试图规避与keydown
事件发射相关的延迟和缓和。按下箭头键时,方块应立即全速移动。
但广场的运动仍然遵循keydown事件的发射。
为什么是这样?
这段代码的工作方式是:
- 一个
keyPresses
对象,它维护每个箭头键码的按键列表及其持续时间 - 这些列表的长度通常为零或一,因为,
- 列表将重置每个动画帧
- 我依靠
requestAnimationFrame
希望能够停止依赖keydown事件 37
,38
,39
,40
魔术数字是箭头键的键码
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事件的发射?的主要内容,如果未能解决你的问题,请参考以下文章