feat: 마우스 드래그 및 터치 이벤트 처리 개선
- 캔버스 드래그 시 스크롤 방지 로직 추가 - 마우스/터치 이벤트 핸들러 통합 및 개선 - 이벤트 리스너 등록/제거 로직 최적화
This commit is contained in:
parent
ddcf8b463a
commit
d2e83ac9a5
69
dist/index.js
vendored
69
dist/index.js
vendored
@ -407,12 +407,14 @@ var useMouseVelocity = (containerRef) => {
|
|||||||
mouseStateRef.current.isDragging = false;
|
mouseStateRef.current.isDragging = false;
|
||||||
}, []);
|
}, []);
|
||||||
const handleTouchMove = (0, import_react2.useCallback)((e) => {
|
const handleTouchMove = (0, import_react2.useCallback)((e) => {
|
||||||
|
e.preventDefault();
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
updatePosition(touch.clientX, touch.clientY);
|
updatePosition(touch.clientX, touch.clientY);
|
||||||
}
|
}
|
||||||
}, [updatePosition]);
|
}, [updatePosition]);
|
||||||
const handleTouchStart = (0, import_react2.useCallback)((e) => {
|
const handleTouchStart = (0, import_react2.useCallback)((e) => {
|
||||||
|
e.preventDefault();
|
||||||
mouseStateRef.current.isDragging = true;
|
mouseStateRef.current.isDragging = true;
|
||||||
mouseStateRef.current.isHovering = true;
|
mouseStateRef.current.isHovering = true;
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
@ -437,8 +439,8 @@ var useMouseVelocity = (containerRef) => {
|
|||||||
container.addEventListener("mouseleave", handleMouseLeave);
|
container.addEventListener("mouseleave", handleMouseLeave);
|
||||||
container.addEventListener("mousedown", handleMouseDown);
|
container.addEventListener("mousedown", handleMouseDown);
|
||||||
window.addEventListener("mouseup", handleMouseUp);
|
window.addEventListener("mouseup", handleMouseUp);
|
||||||
container.addEventListener("touchmove", handleTouchMove, { passive: true });
|
container.addEventListener("touchmove", handleTouchMove, { passive: false });
|
||||||
container.addEventListener("touchstart", handleTouchStart, { passive: true });
|
container.addEventListener("touchstart", handleTouchStart, { passive: false });
|
||||||
container.addEventListener("touchend", handleTouchEnd);
|
container.addEventListener("touchend", handleTouchEnd);
|
||||||
container.addEventListener("touchcancel", handleTouchEnd);
|
container.addEventListener("touchcancel", handleTouchEnd);
|
||||||
return () => {
|
return () => {
|
||||||
@ -1100,7 +1102,7 @@ var EditorCanvas = ({
|
|||||||
}
|
}
|
||||||
return inside;
|
return inside;
|
||||||
}, []);
|
}, []);
|
||||||
const handleMouseDown = (0, import_react6.useCallback)(
|
const handlePointDown = (0, import_react6.useCallback)(
|
||||||
(pointIndex) => (e) => {
|
(pointIndex) => (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -1108,12 +1110,21 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[onStartDragging]
|
[onStartDragging]
|
||||||
);
|
);
|
||||||
const handleCanvasMouseDown = (0, import_react6.useCallback)(
|
const handleCanvasDown = (0, import_react6.useCallback)(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
let clientX, clientY;
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
if ("touches" in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
const clickPoint = { x, y };
|
const clickPoint = { x, y };
|
||||||
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
|
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
|
||||||
setIsDraggingArea(true);
|
setIsDraggingArea(true);
|
||||||
@ -1123,12 +1134,24 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[showEditor, selectedArea, isPointInPolygon2]
|
[showEditor, selectedArea, isPointInPolygon2]
|
||||||
);
|
);
|
||||||
const handleMouseMove = (0, import_react6.useCallback)(
|
const handleMove = (0, import_react6.useCallback)(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
|
if ("touches" in e && (draggingPointIndex !== null || isDraggingArea)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
let clientX, clientY;
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
if ("touches" in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
const clampedX = Math.max(0, Math.min(1, x));
|
const clampedX = Math.max(0, Math.min(1, x));
|
||||||
const clampedY = Math.max(0, Math.min(1, y));
|
const clampedY = Math.max(0, Math.min(1, y));
|
||||||
@ -1146,7 +1169,7 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
||||||
);
|
);
|
||||||
const handleMouseUp = (0, import_react6.useCallback)(() => {
|
const handleUp = (0, import_react6.useCallback)(() => {
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
onStopDragging();
|
onStopDragging();
|
||||||
}
|
}
|
||||||
@ -1157,10 +1180,16 @@ var EditorCanvas = ({
|
|||||||
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
||||||
(0, import_react6.useEffect)(() => {
|
(0, import_react6.useEffect)(() => {
|
||||||
if (draggingPointIndex !== null || isDraggingArea) {
|
if (draggingPointIndex !== null || isDraggingArea) {
|
||||||
window.addEventListener("mouseup", handleMouseUp);
|
window.addEventListener("mouseup", handleUp);
|
||||||
return () => window.removeEventListener("mouseup", handleMouseUp);
|
window.addEventListener("touchend", handleUp);
|
||||||
|
window.addEventListener("touchcancel", handleUp);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mouseup", handleUp);
|
||||||
|
window.removeEventListener("touchend", handleUp);
|
||||||
|
window.removeEventListener("touchcancel", handleUp);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}, [draggingPointIndex, isDraggingArea, handleMouseUp]);
|
}, [draggingPointIndex, isDraggingArea, handleUp]);
|
||||||
const uvToPixel = (u, v, points, canvasWidth, canvasHeight) => {
|
const uvToPixel = (u, v, points, canvasWidth, canvasHeight) => {
|
||||||
const [p0, p1, p2, p3] = points;
|
const [p0, p1, p2, p3] = points;
|
||||||
const leftX = p0.x * (1 - u) + p1.x * u;
|
const leftX = p0.x * (1 - u) + p1.x * u;
|
||||||
@ -1240,11 +1269,14 @@ var EditorCanvas = ({
|
|||||||
height,
|
height,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
cursor: showEditor ? getCursorStyle() : "default",
|
cursor: showEditor ? getCursorStyle() : "default",
|
||||||
pointerEvents: showEditor ? "auto" : "none"
|
pointerEvents: showEditor ? "auto" : "none",
|
||||||
// 에디터 숨김 시 포인터 이벤트 비활성화
|
touchAction: "none"
|
||||||
|
// 터치 시 모든 브라우저 동작 비활성화 (스크롤, 줌 등)
|
||||||
},
|
},
|
||||||
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
|
onMouseDown: showEditor ? handleCanvasDown : void 0,
|
||||||
onMouseMove: showEditor ? handleMouseMove : void 0,
|
onMouseMove: showEditor ? handleMove : void 0,
|
||||||
|
onTouchStart: showEditor ? handleCanvasDown : void 0,
|
||||||
|
onTouchMove: showEditor ? handleMove : void 0,
|
||||||
children: [
|
children: [
|
||||||
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageDistortion, { imageSrc, areas }),
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageDistortion, { imageSrc, areas }),
|
||||||
showEditor && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
showEditor && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
||||||
@ -1320,7 +1352,8 @@ var EditorCanvas = ({
|
|||||||
pointerEvents: "auto",
|
pointerEvents: "auto",
|
||||||
boxShadow: "0 2px 4px rgba(0,0,0,0.3)"
|
boxShadow: "0 2px 4px rgba(0,0,0,0.3)"
|
||||||
},
|
},
|
||||||
onMouseDown: handleMouseDown(index),
|
onMouseDown: handlePointDown(index),
|
||||||
|
onTouchStart: handlePointDown(index),
|
||||||
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
|
|||||||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
69
dist/index.mjs
vendored
69
dist/index.mjs
vendored
@ -358,12 +358,14 @@ var useMouseVelocity = (containerRef) => {
|
|||||||
mouseStateRef.current.isDragging = false;
|
mouseStateRef.current.isDragging = false;
|
||||||
}, []);
|
}, []);
|
||||||
const handleTouchMove = useCallback((e) => {
|
const handleTouchMove = useCallback((e) => {
|
||||||
|
e.preventDefault();
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
updatePosition(touch.clientX, touch.clientY);
|
updatePosition(touch.clientX, touch.clientY);
|
||||||
}
|
}
|
||||||
}, [updatePosition]);
|
}, [updatePosition]);
|
||||||
const handleTouchStart = useCallback((e) => {
|
const handleTouchStart = useCallback((e) => {
|
||||||
|
e.preventDefault();
|
||||||
mouseStateRef.current.isDragging = true;
|
mouseStateRef.current.isDragging = true;
|
||||||
mouseStateRef.current.isHovering = true;
|
mouseStateRef.current.isHovering = true;
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
@ -388,8 +390,8 @@ var useMouseVelocity = (containerRef) => {
|
|||||||
container.addEventListener("mouseleave", handleMouseLeave);
|
container.addEventListener("mouseleave", handleMouseLeave);
|
||||||
container.addEventListener("mousedown", handleMouseDown);
|
container.addEventListener("mousedown", handleMouseDown);
|
||||||
window.addEventListener("mouseup", handleMouseUp);
|
window.addEventListener("mouseup", handleMouseUp);
|
||||||
container.addEventListener("touchmove", handleTouchMove, { passive: true });
|
container.addEventListener("touchmove", handleTouchMove, { passive: false });
|
||||||
container.addEventListener("touchstart", handleTouchStart, { passive: true });
|
container.addEventListener("touchstart", handleTouchStart, { passive: false });
|
||||||
container.addEventListener("touchend", handleTouchEnd);
|
container.addEventListener("touchend", handleTouchEnd);
|
||||||
container.addEventListener("touchcancel", handleTouchEnd);
|
container.addEventListener("touchcancel", handleTouchEnd);
|
||||||
return () => {
|
return () => {
|
||||||
@ -1051,7 +1053,7 @@ var EditorCanvas = ({
|
|||||||
}
|
}
|
||||||
return inside;
|
return inside;
|
||||||
}, []);
|
}, []);
|
||||||
const handleMouseDown = useCallback5(
|
const handlePointDown = useCallback5(
|
||||||
(pointIndex) => (e) => {
|
(pointIndex) => (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -1059,12 +1061,21 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[onStartDragging]
|
[onStartDragging]
|
||||||
);
|
);
|
||||||
const handleCanvasMouseDown = useCallback5(
|
const handleCanvasDown = useCallback5(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
let clientX, clientY;
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
if ("touches" in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
const clickPoint = { x, y };
|
const clickPoint = { x, y };
|
||||||
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
|
if (isPointInPolygon2(clickPoint, selectedArea.basePoints)) {
|
||||||
setIsDraggingArea(true);
|
setIsDraggingArea(true);
|
||||||
@ -1074,12 +1085,24 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[showEditor, selectedArea, isPointInPolygon2]
|
[showEditor, selectedArea, isPointInPolygon2]
|
||||||
);
|
);
|
||||||
const handleMouseMove = useCallback5(
|
const handleMove = useCallback5(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
|
if ("touches" in e && (draggingPointIndex !== null || isDraggingArea)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
let clientX, clientY;
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
if ("touches" in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
const clampedX = Math.max(0, Math.min(1, x));
|
const clampedX = Math.max(0, Math.min(1, x));
|
||||||
const clampedY = Math.max(0, Math.min(1, y));
|
const clampedY = Math.max(0, Math.min(1, y));
|
||||||
@ -1097,7 +1120,7 @@ var EditorCanvas = ({
|
|||||||
},
|
},
|
||||||
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
||||||
);
|
);
|
||||||
const handleMouseUp = useCallback5(() => {
|
const handleUp = useCallback5(() => {
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
onStopDragging();
|
onStopDragging();
|
||||||
}
|
}
|
||||||
@ -1108,10 +1131,16 @@ var EditorCanvas = ({
|
|||||||
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
||||||
useEffect4(() => {
|
useEffect4(() => {
|
||||||
if (draggingPointIndex !== null || isDraggingArea) {
|
if (draggingPointIndex !== null || isDraggingArea) {
|
||||||
window.addEventListener("mouseup", handleMouseUp);
|
window.addEventListener("mouseup", handleUp);
|
||||||
return () => window.removeEventListener("mouseup", handleMouseUp);
|
window.addEventListener("touchend", handleUp);
|
||||||
|
window.addEventListener("touchcancel", handleUp);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mouseup", handleUp);
|
||||||
|
window.removeEventListener("touchend", handleUp);
|
||||||
|
window.removeEventListener("touchcancel", handleUp);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}, [draggingPointIndex, isDraggingArea, handleMouseUp]);
|
}, [draggingPointIndex, isDraggingArea, handleUp]);
|
||||||
const uvToPixel = (u, v, points, canvasWidth, canvasHeight) => {
|
const uvToPixel = (u, v, points, canvasWidth, canvasHeight) => {
|
||||||
const [p0, p1, p2, p3] = points;
|
const [p0, p1, p2, p3] = points;
|
||||||
const leftX = p0.x * (1 - u) + p1.x * u;
|
const leftX = p0.x * (1 - u) + p1.x * u;
|
||||||
@ -1191,11 +1220,14 @@ var EditorCanvas = ({
|
|||||||
height,
|
height,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
cursor: showEditor ? getCursorStyle() : "default",
|
cursor: showEditor ? getCursorStyle() : "default",
|
||||||
pointerEvents: showEditor ? "auto" : "none"
|
pointerEvents: showEditor ? "auto" : "none",
|
||||||
// 에디터 숨김 시 포인터 이벤트 비활성화
|
touchAction: "none"
|
||||||
|
// 터치 시 모든 브라우저 동작 비활성화 (스크롤, 줌 등)
|
||||||
},
|
},
|
||||||
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
|
onMouseDown: showEditor ? handleCanvasDown : void 0,
|
||||||
onMouseMove: showEditor ? handleMouseMove : void 0,
|
onMouseMove: showEditor ? handleMove : void 0,
|
||||||
|
onTouchStart: showEditor ? handleCanvasDown : void 0,
|
||||||
|
onTouchMove: showEditor ? handleMove : void 0,
|
||||||
children: [
|
children: [
|
||||||
/* @__PURE__ */ jsx2(ImageDistortion, { imageSrc, areas }),
|
/* @__PURE__ */ jsx2(ImageDistortion, { imageSrc, areas }),
|
||||||
showEditor && /* @__PURE__ */ jsx2(
|
showEditor && /* @__PURE__ */ jsx2(
|
||||||
@ -1271,7 +1303,8 @@ var EditorCanvas = ({
|
|||||||
pointerEvents: "auto",
|
pointerEvents: "auto",
|
||||||
boxShadow: "0 2px 4px rgba(0,0,0,0.3)"
|
boxShadow: "0 2px 4px rgba(0,0,0,0.3)"
|
||||||
},
|
},
|
||||||
onMouseDown: handleMouseDown(index),
|
onMouseDown: handlePointDown(index),
|
||||||
|
onTouchStart: handlePointDown(index),
|
||||||
children: /* @__PURE__ */ jsxs(
|
children: /* @__PURE__ */ jsxs(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
|
|||||||
2
dist/index.mjs.map
vendored
2
dist/index.mjs.map
vendored
File diff suppressed because one or more lines are too long
@ -83,9 +83,9 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
return inside;
|
return inside;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 마우스 이벤트 핸들러
|
// 포인트 핸들 클릭/터치 핸들러
|
||||||
const handleMouseDown = useCallback(
|
const handlePointDown = useCallback(
|
||||||
(pointIndex: number) => (e: React.MouseEvent) => {
|
(pointIndex: number) => (e: React.MouseEvent | React.TouchEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onStartDragging(pointIndex);
|
onStartDragging(pointIndex);
|
||||||
@ -93,15 +93,27 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
[onStartDragging]
|
[onStartDragging]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 캔버스 클릭 (사각형 내부 클릭 감지)
|
// 캔버스 다운 (마우스/터치 공통)
|
||||||
const handleCanvasMouseDown = useCallback(
|
const handleCanvasDown = useCallback(
|
||||||
(e: React.MouseEvent) => {
|
(e: React.MouseEvent | React.TouchEvent) => {
|
||||||
// 에디터가 숨겨진 상태면 동작하지 않음
|
// 에디터가 숨겨진 상태면 동작하지 않음
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
|
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
// 마우스 또는 터치 좌표 추출
|
||||||
|
let clientX: number, clientY: number;
|
||||||
|
if ('touches' in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
const clickPoint = { x, y };
|
const clickPoint = { x, y };
|
||||||
|
|
||||||
// 사각형 내부를 클릭했는지 확인
|
// 사각형 내부를 클릭했는지 확인
|
||||||
@ -114,14 +126,32 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
[showEditor, selectedArea, isPointInPolygon]
|
[showEditor, selectedArea, isPointInPolygon]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseMove = useCallback(
|
// 이동 (마우스/터치 공통)
|
||||||
(e: React.MouseEvent) => {
|
const handleMove = useCallback(
|
||||||
|
(e: React.MouseEvent | React.TouchEvent) => {
|
||||||
// 에디터가 숨겨진 상태면 동작하지 않음
|
// 에디터가 숨겨진 상태면 동작하지 않음
|
||||||
if (!showEditor || !selectedArea || !containerRef.current) return;
|
if (!showEditor || !selectedArea || !containerRef.current) return;
|
||||||
|
|
||||||
|
// 터치 이벤트면 스크롤 방지
|
||||||
|
if ('touches' in e && (draggingPointIndex !== null || isDraggingArea)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
const x = (e.clientX - rect.left) / rect.width;
|
|
||||||
const y = (e.clientY - rect.top) / rect.height;
|
// 마우스 또는 터치 좌표 추출
|
||||||
|
let clientX: number, clientY: number;
|
||||||
|
if ('touches' in e) {
|
||||||
|
if (e.touches.length === 0) return;
|
||||||
|
clientX = e.touches[0].clientX;
|
||||||
|
clientY = e.touches[0].clientY;
|
||||||
|
} else {
|
||||||
|
clientX = e.clientX;
|
||||||
|
clientY = e.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = (clientX - rect.left) / rect.width;
|
||||||
|
const y = (clientY - rect.top) / rect.height;
|
||||||
|
|
||||||
// 포인트 드래그 중
|
// 포인트 드래그 중
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
@ -147,7 +177,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseUp = useCallback(() => {
|
// 업 (마우스/터치 공통)
|
||||||
|
const handleUp = useCallback(() => {
|
||||||
if (draggingPointIndex !== null) {
|
if (draggingPointIndex !== null) {
|
||||||
onStopDragging();
|
onStopDragging();
|
||||||
}
|
}
|
||||||
@ -157,13 +188,19 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
}
|
}
|
||||||
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
}, [draggingPointIndex, isDraggingArea, onStopDragging]);
|
||||||
|
|
||||||
// 전역 마우스 업 이벤트
|
// 전역 업 이벤트 (마우스/터치)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (draggingPointIndex !== null || isDraggingArea) {
|
if (draggingPointIndex !== null || isDraggingArea) {
|
||||||
window.addEventListener('mouseup', handleMouseUp);
|
window.addEventListener('mouseup', handleUp);
|
||||||
return () => window.removeEventListener('mouseup', handleMouseUp);
|
window.addEventListener('touchend', handleUp);
|
||||||
|
window.addEventListener('touchcancel', handleUp);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mouseup', handleUp);
|
||||||
|
window.removeEventListener('touchend', handleUp);
|
||||||
|
window.removeEventListener('touchcancel', handleUp);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}, [draggingPointIndex, isDraggingArea, handleMouseUp]);
|
}, [draggingPointIndex, isDraggingArea, handleUp]);
|
||||||
|
|
||||||
// UV 좌표를 픽셀 좌표로 변환 (셰이더와 동일한 bilinear interpolation)
|
// UV 좌표를 픽셀 좌표로 변환 (셰이더와 동일한 bilinear interpolation)
|
||||||
const uvToPixel = (
|
const uvToPixel = (
|
||||||
@ -280,10 +317,13 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
height,
|
height,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
cursor: showEditor ? getCursorStyle() : 'default',
|
cursor: showEditor ? getCursorStyle() : 'default',
|
||||||
pointerEvents: showEditor ? 'auto' : 'none', // 에디터 숨김 시 포인터 이벤트 비활성화
|
pointerEvents: showEditor ? 'auto' : 'none',
|
||||||
|
touchAction: 'none', // 터치 시 모든 브라우저 동작 비활성화 (스크롤, 줌 등)
|
||||||
}}
|
}}
|
||||||
onMouseDown={showEditor ? handleCanvasMouseDown : undefined}
|
onMouseDown={showEditor ? handleCanvasDown : undefined}
|
||||||
onMouseMove={showEditor ? handleMouseMove : undefined}
|
onMouseMove={showEditor ? handleMove : undefined}
|
||||||
|
onTouchStart={showEditor ? handleCanvasDown : undefined}
|
||||||
|
onTouchMove={showEditor ? handleMove : undefined}
|
||||||
>
|
>
|
||||||
{/* ImageDistortion 컴포넌트 */}
|
{/* ImageDistortion 컴포넌트 */}
|
||||||
<ImageDistortion imageSrc={imageSrc} areas={areas}/>
|
<ImageDistortion imageSrc={imageSrc} areas={areas}/>
|
||||||
@ -371,7 +411,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
|
|||||||
pointerEvents: 'auto',
|
pointerEvents: 'auto',
|
||||||
boxShadow: '0 2px 4px rgba(0,0,0,0.3)',
|
boxShadow: '0 2px 4px rgba(0,0,0,0.3)',
|
||||||
}}
|
}}
|
||||||
onMouseDown={handleMouseDown(index)}
|
onMouseDown={handlePointDown(index)}
|
||||||
|
onTouchStart={handlePointDown(index)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -122,6 +122,7 @@ export const useMouseVelocity = (containerRef: React.RefObject<HTMLElement | nul
|
|||||||
* 터치 이동 핸들러
|
* 터치 이동 핸들러
|
||||||
*/
|
*/
|
||||||
const handleTouchMove = useCallback((e: TouchEvent) => {
|
const handleTouchMove = useCallback((e: TouchEvent) => {
|
||||||
|
e.preventDefault(); // 스크롤 방지
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
updatePosition(touch.clientX, touch.clientY);
|
updatePosition(touch.clientX, touch.clientY);
|
||||||
@ -132,6 +133,7 @@ export const useMouseVelocity = (containerRef: React.RefObject<HTMLElement | nul
|
|||||||
* 터치 시작 핸들러
|
* 터치 시작 핸들러
|
||||||
*/
|
*/
|
||||||
const handleTouchStart = useCallback((e: TouchEvent) => {
|
const handleTouchStart = useCallback((e: TouchEvent) => {
|
||||||
|
e.preventDefault(); // 스크롤 방지
|
||||||
mouseStateRef.current.isDragging = true;
|
mouseStateRef.current.isDragging = true;
|
||||||
mouseStateRef.current.isHovering = true;
|
mouseStateRef.current.isHovering = true;
|
||||||
if (e.touches.length > 0) {
|
if (e.touches.length > 0) {
|
||||||
@ -167,9 +169,9 @@ export const useMouseVelocity = (containerRef: React.RefObject<HTMLElement | nul
|
|||||||
container.addEventListener('mousedown', handleMouseDown);
|
container.addEventListener('mousedown', handleMouseDown);
|
||||||
window.addEventListener('mouseup', handleMouseUp);
|
window.addEventListener('mouseup', handleMouseUp);
|
||||||
|
|
||||||
// 터치 이벤트
|
// 터치 이벤트 (passive: false로 스크롤 방지 가능)
|
||||||
container.addEventListener('touchmove', handleTouchMove, { passive: true });
|
container.addEventListener('touchmove', handleTouchMove, { passive: false });
|
||||||
container.addEventListener('touchstart', handleTouchStart, { passive: true });
|
container.addEventListener('touchstart', handleTouchStart, { passive: false });
|
||||||
container.addEventListener('touchend', handleTouchEnd);
|
container.addEventListener('touchend', handleTouchEnd);
|
||||||
container.addEventListener('touchcancel', handleTouchEnd);
|
container.addEventListener('touchcancel', handleTouchEnd);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user