【问题标题】:Konva continue to track mouse outside stageKonva 继续在舞台外追踪鼠标
【发布时间】:2020-07-29 18:10:13
【问题描述】:

我正在尝试使用 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>
  );
}

那么我想知道的是什么是最简单和最干净的方法,我可以扩展它以保持跟踪鼠标,即使它超出了舞台?

【问题讨论】:

  • 最终一切都在浏览器文档中,所以您可以移动听众以在顶层收听吗?
  • @VanquiishedWombat 我可以,但是尝试计算鼠标相对于 Konva 元素的位置会变得非常讨厌,或者有没有简单的方法?
  • 不完全相同但非常相似:stackoverflow.com/questions/44801596/…
  • @VanquiishedWombat 谢谢

标签: javascript html reactjs konvajs react-konva


【解决方案1】:

根据@VanquiishedWombat 的建议,从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 事件的偏移量。这在我的应用程序中不是问题,但如果这可能会影响您的应用程序,请注意。

【讨论】:

  • 干得好。出于兴趣,如果画布被缩放会有什么问题吗?这可能不是您的用例,也可能不是问题 - 我总是认为缩放是一个潜在的问题,因为画布阶段可以独立于周围的文档进行缩放。
  • 其他人注意 - 如果您使用此代码并发现它在移植时不起作用,请注意您可能拥有的任何取消气泡样式的代码,这些代码会阻止事件沿树向上传播到文档级别.
  • @VanquiishedWombat 缩放可能会导致一些痛苦,因为屏幕坐标被假定与画布中的坐标具有相同的比例。它很可能需要进行一些修改以支持缩放。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 2014-07-01
  • 1970-01-01
  • 2019-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多