HTML5 Canvas 键盘事件与 Konva
Konva 中没有内置的键盘事件,比如 keydown
或 keyup
。
但是如何在画布上监听 keydown 或 keyup 事件?
你可以通过两种方式轻松添加它们:
- 监听
window
对象上的全局事件 - 或者通过
tabIndex
属性使舞台容器可聚焦,并在其上监听事件。
说明:点击舞台以聚焦它,使用箭头键移动形状
- Vanilla
- React
- Vue
import Konva from 'konva'; const stage = new Konva.Stage({ container: 'container', width: window.innerWidth, height: window.innerHeight, }); const layer = new Konva.Layer(); stage.add(layer); const circle = new Konva.Circle({ x: stage.width() / 2, y: stage.height() / 2, radius: 50, fill: 'red', stroke: 'black', strokeWidth: 4, }); layer.add(circle); // 使舞台容器可聚焦 stage.container().tabIndex = 1; // 聚焦它 // 点击舞台时也会聚焦 stage.container().focus(); const DELTA = 4; // 添加键盘事件 stage.container().addEventListener('keydown', (e) => { if (e.keyCode === 37) { circle.x(circle.x() - DELTA); } else if (e.keyCode === 38) { circle.y(circle.y() - DELTA); } else if (e.keyCode === 39) { circle.x(circle.x() + DELTA); } else if (e.keyCode === 40) { circle.y(circle.y() + DELTA); } else { return; } e.preventDefault(); });
import { Stage, Layer, Circle } from 'react-konva'; import { useRef, useEffect, useState } from 'react'; const App = () => { const stageRef = useRef(); const containerRef = useRef(); const [position, setPosition] = useState({ x: window.innerWidth / 2, y: window.innerHeight / 2, }); useEffect(() => { // focus the div on mount containerRef.current.focus(); }, []); const handleKeyDown = (e) => { const DELTA = 4; switch (e.keyCode) { case 37: // 左 setPosition(pos => ({ ...pos, x: pos.x - DELTA })); break; case 38: // 上 setPosition(pos => ({ ...pos, y: pos.y - DELTA })); break; case 39: // 右 setPosition(pos => ({ ...pos, x: pos.x + DELTA })); break; case 40: // 下 setPosition(pos => ({ ...pos, y: pos.y + DELTA })); break; default: return; } e.preventDefault(); }; return ( <div ref={containerRef} tabIndex={0} onKeyDown={handleKeyDown} > <Stage width={window.innerWidth} height={window.innerHeight} ref={stageRef} > <Layer> <Circle x={position.x} y={position.y} radius={50} fill="red" stroke="black" strokeWidth={4} /> </Layer> </Stage> </div> ); }; export default App;
<template> <div ref="containerRef" tabindex="0" @keydown="handleKeyDown" > <v-stage :config="stageSize" ref="stageRef"> <v-layer> <v-circle :config="circleConfig" /> </v-layer> </v-stage> </div> </template> <script setup> import { ref, onMounted, computed } from 'vue'; const stageRef = ref(null); const containerRef = ref(null); const position = ref({ x: window.innerWidth / 2, y: window.innerHeight / 2, }); const stageSize = { width: window.innerWidth, height: window.innerHeight }; const circleConfig = computed(() => ({ x: position.value.x, y: position.value.y, radius: 50, fill: 'red', stroke: 'black', strokeWidth: 4 })); onMounted(() => { // focus the div on mount containerRef.value.focus(); }); const focusDiv = () => { containerRef.value.focus(); }; const handleKeyDown = (e) => { const DELTA = 4; switch (e.keyCode) { case 37: // 左 position.value.x -= DELTA; break; case 38: // 上 position.value.y -= DELTA; break; case 39: // 右 position.value.x += DELTA; break; case 40: // 下 position.value.y += DELTA; break; default: return; } e.preventDefault(); }; </script>