我正在尝试使用 React-Konva 库实现一个操纵杆,可以用来控制 React.JS 中的机器人之类的东西。到目前为止,我已经成功地通过在较大的圆圈内绘制一个较小的圆圈并让较小的圆圈在鼠标按下时跟踪鼠标相对于舞台的位置来获得某种效果。它的问题是,一旦鼠标离开舞台,我就会停止获取 onMouseMove 事件,并且圆圈会卡在其最后位置,直到鼠标返回舞台。理想情况下,我希望能够让圆圈保持跟踪鼠标的方向,即使它移动到舞台之外,但显然限制了圆圈实际上可以从原点移动到保持在舞台内的距离。
这是我到目前为止的代码
import React, { useState, useContext } from "react";
import { Stage, Layer, Circle } from "react-konva";
export default function Joystick(props) {
const { size } = props;
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const [down, setDown] = useState(false);
const joyX = down ? x : size / 2;
const joyY = down ? y : size / 2;
return (
<Stage
width={size}
height={size}
onMouseMove={(ev) => {
setX(ev.evt.layerX);
setY(ev.evt.layerY);
}}
onMouseDown={(ev) => setDown(true)}
onMouseUp={(ev) => setDown(false)}
>
<Layer>
<Circle x={size / 2} y={size / 2} radius={size / 2} fill="black" />
<Circle x={joyX} y={joyY} radius={size / 4} fill="white" />
</Layer>
</Stage>
);
}
所以我想知道什么是最简单、最干净的方法,我可以扩展它以保持跟踪鼠标,即使它超出了舞台?
最佳答案
根据 @VanquishedWombat 的建议,从 fire the div mousemove while on document 获取灵感我想出了以下代码
function offset(el) {
var rect = el.getBoundingClientRect(),
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return {
top: rect.top + scrollTop,
left: rect.left + scrollLeft,
};
}
export default class Joystick extends React.Component {
constructor(props) {
super(props);
this.state = {
down: 0,
x: 0,
y: 0,
offset: { top: 0, left: 0 },
};
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
}
updatePosition(ev, o) {
const { size } = this.props;
const offset = o || this.state.offset;
let x = ev.clientX - offset.left;
let y = ev.clientY - offset.top;
let right = (x / size - 0.5) * 2;
let up = (y / size - 0.5) * -2;
const mag = Math.sqrt(right * right + up * up);
const newMag = Math.min(mag, 1);
right = (right / mag) * newMag;
up = (up / mag) * newMag;
x = (1 + right) * (size / 2);
y = (1 - up) * (size / 2);
this.setState({ x, y });
}
handleMouseMove(ev) {
this.updatePosition(ev);
}
handleMouseUp(ev) {
document.removeEventListener("mousemove", this.handleMouseMove);
document.removeEventListener("mouseup", this.handleMouseUp);
this.setState({ down: false });
}
render() {
const { x, y, down } = this.state;
const { size } = this.props;
const joyX = down ? x : size / 2;
const joyY = down ? y : size / 2;
return (
<div
onMouseDown={(ev) => {
const o = offset(ev.currentTarget);
this.setState({ offset: o, down: true });
this.updatePosition(ev, o);
document.addEventListener("mousemove", this.handleMouseMove);
document.addEventListener("mouseup", this.handleMouseUp);
}}
style={{ width: size, height: size }}
>
<Stage width={size} height={size}>
<Layer
clipFunc={(ctx) =>
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2)
}
>
<Circle x={size / 2} y={size / 2} radius={size / 2} fill="black" />
<Circle x={joyX} y={joyY} radius={size / 4} fill="white" />
</Layer>
</Stage>
</div>
);
}
}
这段代码有点令人讨厌,因为它必须计算光标相对于舞台的位置,但我试图使其尽可能简单,而且它似乎工作得很好。舞台需要包裹在相同大小的 div 中才能使用 getBoundingClientRect 函数,该函数允许计算相对鼠标位置。我还必须将我的 React 组件从函数组件更改为类组件,因为我需要常量回调函数引用,以便在释放鼠标后可以正确取消注册它们。
我相信,如果在鼠标按下时包装 div 的位置发生变化(从滚动或其他方式),这仍然会失败,因为它只计算初始 mousedown 事件时的偏移量。这在我的应用程序中不是问题,但如果这可能影响您的应用程序,请注意。
关于javascript - Konva在舞台外继续追踪鼠标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63159928/