通过拖动角手柄调整矩形的半径

Adjusting the radius of a rectangle by dragging a corner handle

我正在构建一个设计工具并且无法理解如何构建此功能。我在这里完成了大部分工作 https://codesandbox.io/s/sharp-mendeleev-dsgvx?file=/src/App.tsx 但它有点靠不住。

我要重现的功能可以在Figma或Sketch中找到,这里是录音https://share.getcloudapp.com/yAuDed8k

import { FC, useEffect, useState, useCallback } from "react";
import { RadiusControlWrap, RadiusControlStyle, CornerKnob } from "./Styles";

interface IRadiusControlProps {
  size: any;
  radius: number;
  onRadiusChange: (radius: number) => void;
}

const RadiusControl: FC<IRadiusControlProps> = ({
  size,
  radius,
  onRadiusChange
}) => {
  const [mouseDown, setMouseDown] = useState<any>(false);

  const handleMouseMove = useCallback(
    (e: any) => {
      if (mouseDown && e.target && e.target.parentElement) {
        const rect = e.target.parentElement.getBoundingClientRect();
        const x = Math.round(Number(e.clientX - rect.left));
        const y = Math.round(Number(e.clientY - rect.top));
        const val = Math.round(Math.atan2(y, x) * 100);

        // TODO: Understand how to set the correct value
        onRadiusChange(val);
      }
    },
    [mouseDown, onRadiusChange]
  );

  const handleMouseUp = (e: any) => {
    setMouseDown(false);
  };

  const handleMouseDown = (e: any) => {
    setMouseDown(true);
  };

  useEffect(() => {
    if (mouseDown) {
      document.addEventListener("mouseup", handleMouseUp);
      document.addEventListener("mousemove", handleMouseMove);
    } else {
      document.removeEventListener("mousemove", handleMouseMove);
    }

    return () => {
      document.removeEventListener("mouseup", handleMouseUp);
      document.removeEventListener("mousemove", handleMouseMove);
    };
  }, [handleMouseMove, mouseDown]);

  // TODO: how to scale the knobs like Figma or Sketch
  return (
    <RadiusControlWrap>
      <RadiusControlStyle
        style={{
          width: size.width - 30 - radius,
          height: size.height - 30 - radius
        }}
      >
        <CornerKnob corner="tl" onMouseDown={handleMouseDown} />
        <CornerKnob corner="bl" />
        <CornerKnob corner="tr" />
        <CornerKnob corner="br" />
      </RadiusControlStyle>
    </RadiusControlWrap>
  );
};

export default RadiusControl;

我最纠结的数学在里面handleMouseMove。我使用的是 y 和 x 坐标的反正切、舍入和乘以 100——显然是错误的。

正如您在 Figma 屏幕共享中看到的那样,那里的数学与对象的中心完全成比例,半径似乎是根据边缘和中心之间的反正切差计算的,相应地设置圆度。

此外,手柄看起来呈矩形,并按比例缩小,直到它们在中间相交——或者当它们在 x 轴上相交时呈矩形。

希望这是有道理的,并且有一些 JS 和三角学经验的人可以提供帮助。

您可以在这里找到所有代码 https://codesandbox.io/s/sharp-mendeleev-dsgvx?file=/src/RadiusControl.tsx:0-1987 以防您在上面错过了它。

更改以下行...

const val = Math.round(Math.atan2(y, x) * 100);

...到...

const val = Math.sqrt( x * x + y * y );

简而言之,原始代码是获取光标到角的角度(以弧度为单位),因此在抓取控制点后,如果将光标大致向左或向右移动,您将看到一个平滑的角过渡,如下所示您正在平滑地转换光标相对于角的角度。

建议的代码只是测量光标到角的距离。

话虽这么说,但这并不能解决全部问题。当 handleMouseDown 事件发生时,需要捕获当前光标位置和到矩形角的当前距离以及当前矩形角半径。然后,这将允许您计算从原始光标位置到角点的距离变化,以及随后矩形半径的变化...