#javascript #reactjs #trigonometry
Вопрос:
Я создаю инструмент проектирования и застрял в понимании того, как создать эту функциональность. У меня есть хороший кусок этого здесь https://codesandbox.io/s/sharp-mendeleev-dsgvx?file=/src/App.tsx но это немного шатко.
Функцию, которую я пытаюсь воссоздать, можно найти в Figma или эскизе, вот запись 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 amp;amp; e.target amp;amp; 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 на случай, если вы пропустили это выше.
Ответ №1:
Измените следующую строку…
const val = Math.round(Math.atan2(y, x) * 100);
…чтобы…
const val = Math.sqrt( x * x y * y );
Короче говоря, исходный код получает угол (в радианах) курсора к углу, поэтому после захвата контрольной точки, если вы обычно перемещаете курсор влево или вправо, вы увидите плавный угловой переход, поскольку вы плавно перемещаете угол курсора относительно угла.
Предлагаемый код просто измеряет расстояние от курсора до угла.
Тем не менее, это не решает всей проблемы. Когда handleMouseDown
происходит событие, необходимо зафиксировать текущее местоположение курсора, текущее расстояние до угла прямоугольника И текущий радиус угла прямоугольника. Затем это позволит вам рассчитать изменение расстояния до угла от исходного местоположения курсора, а затем изменение радиуса прямоугольника…