Skip to content

Commit b384d6b

Browse files
authored
refactor: 줌 기능에 strategy 패턴 적용 (#14)
* refactor: 줌 기능에 strategy 패턴 적용 * style: 리팩토링 이전 파일 삭제 후 리팩토링 파일로 대체
1 parent 549be86 commit b384d6b

File tree

3 files changed

+69
-83
lines changed

3 files changed

+69
-83
lines changed

packages/frontend/eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default tseslint.config(
3939
"no-shadow": "off",
4040
"import/no-absolute-path": "warn",
4141
"import/no-unresolved": "warn",
42+
"no-unused-vars": "off",
4243
"react-refresh/only-export-components": [
4344
"warn",
4445
{ allowConstantExport: true },

packages/frontend/src/hooks/useZoomSpace.ts

Lines changed: 14 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,10 @@ import React, { useRef } from "react";
33
import Konva from "konva";
44
import { KonvaEventObject } from "konva/lib/Node";
55

6-
const getMousePointTo = (
7-
stage: Konva.Stage,
8-
pointer: { x: number; y: number },
9-
oldScale: number,
10-
) => {
11-
return {
12-
x: (pointer.x - stage.x()) / oldScale,
13-
y: (pointer.y - stage.y()) / oldScale,
14-
};
15-
};
16-
17-
const calculateNewScale = (
18-
oldScale: number,
19-
direction: number,
20-
scaleBy: number,
21-
) => {
22-
return direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
23-
};
24-
25-
const calculateNewPosition = (
26-
pointer: { x: number; y: number },
27-
mousePointTo: { x: number; y: number },
28-
newScale: number,
29-
) => {
30-
return {
31-
x: pointer.x - mousePointTo.x * newScale,
32-
y: pointer.y - mousePointTo.y * newScale,
33-
};
34-
};
6+
import {
7+
ctrlWheelZoomStrategy,
8+
defaultMoveViewStrategy,
9+
} from "@/utils/zoomStrategies";
3510

3611
interface UseZoomSpaceProps {
3712
stageRef: React.RefObject<Konva.Stage>;
@@ -48,68 +23,24 @@ export function useZoomSpace({
4823
}: UseZoomSpaceProps) {
4924
const lastDistRef = useRef<number | null>(null);
5025

51-
const moveView = (event: KonvaEventObject<WheelEvent>) => {
52-
if (stageRef.current !== null) {
53-
const stage = stageRef.current;
54-
const currentScale = stage.scaleX();
55-
56-
stage.position({
57-
x: stage.x() - event.evt.deltaX / currentScale,
58-
y: stage.y() - event.evt.deltaY / currentScale,
59-
});
60-
}
61-
};
62-
6326
const zoomSpace = (event: KonvaEventObject<WheelEvent>) => {
6427
if (stageRef.current !== null) {
6528
const stage = stageRef.current;
6629

67-
// Ctrl + 휠로 확대/축소
6830
if (event.evt.ctrlKey) {
69-
event.evt.preventDefault();
70-
71-
const oldScale = stage.scaleX();
72-
const pointer = stage.getPointerPosition();
73-
74-
if (!pointer) return;
75-
76-
const mousePointTo = getMousePointTo(stage, pointer, oldScale);
77-
78-
const direction =
79-
event.evt.deltaY > 0
80-
? -1 // Ctrl + 휠: 아래로 휠 → 확대
81-
: 1; // Ctrl + 휠: 위로 휠 → 축소
82-
83-
let newScale = calculateNewScale(oldScale, direction, scaleBy);
84-
85-
newScale = Math.max(minScale, Math.min(maxScale, newScale));
86-
87-
if (newScale === oldScale) {
88-
return;
89-
}
90-
91-
const newPosition = calculateNewPosition(
92-
pointer,
93-
mousePointTo,
94-
newScale,
95-
);
96-
stage.scale({ x: newScale, y: newScale });
97-
stage.position(newPosition);
31+
ctrlWheelZoomStrategy(event, stage, scaleBy, minScale, maxScale);
9832
} else {
99-
// Ctrl 키가 없는 휠 이벤트는 화면 이동 처리
100-
moveView(event);
33+
defaultMoveViewStrategy(event, stage);
10134
}
10235
}
10336
};
10437

105-
// 핀치 줌 로직
10638
const handleTouchMove = (event: KonvaEventObject<TouchEvent>) => {
10739
if (stageRef.current !== null && event.evt.touches.length === 2) {
10840
const stage = stageRef.current;
10941
const touch1 = event.evt.touches[0];
11042
const touch2 = event.evt.touches[1];
11143

112-
// 두 손가락 사이 거리 계산
11344
const dist = Math.sqrt(
11445
(touch1.clientX - touch2.clientX) ** 2 +
11546
(touch1.clientY - touch2.clientY) ** 2,
@@ -121,20 +52,20 @@ export function useZoomSpace({
12152

12253
if (!pointer) return;
12354

124-
const mousePointTo = getMousePointTo(stage, pointer, oldScale);
55+
const mousePointTo = {
56+
x: (pointer.x - stage.x()) / oldScale,
57+
y: (pointer.y - stage.y()) / oldScale,
58+
};
12559

126-
// 확대/축소 비율 계산
12760
const scaleChange = lastDistRef.current / dist;
12861
let newScale = oldScale * scaleChange;
129-
13062
newScale = Math.max(minScale, Math.min(maxScale, newScale));
13163

13264
if (newScale !== oldScale) {
133-
const newPosition = calculateNewPosition(
134-
pointer,
135-
mousePointTo,
136-
newScale,
137-
);
65+
const newPosition = {
66+
x: pointer.x - mousePointTo.x * newScale,
67+
y: pointer.y - mousePointTo.y * newScale,
68+
};
13869
stage.scale({ x: newScale, y: newScale });
13970
stage.position(newPosition);
14071
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import Konva from "konva";
2+
import { KonvaEventObject } from "konva/lib/Node";
3+
4+
export type ZoomStrategy = (
5+
event: KonvaEventObject<WheelEvent>,
6+
stage: Konva.Stage,
7+
scaleBy: number,
8+
minScale: number,
9+
maxScale: number,
10+
) => void;
11+
12+
export const ctrlWheelZoomStrategy: ZoomStrategy = (
13+
event,
14+
stage,
15+
scaleBy,
16+
minScale,
17+
maxScale,
18+
) => {
19+
event.evt.preventDefault();
20+
21+
const oldScale = stage.scaleX();
22+
const pointer = stage.getPointerPosition();
23+
24+
if (!pointer) return;
25+
26+
const mousePointTo = {
27+
x: (pointer.x - stage.x()) / oldScale,
28+
y: (pointer.y - stage.y()) / oldScale,
29+
};
30+
31+
const direction = event.evt.deltaY > 0 ? -1 : 1;
32+
let newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
33+
newScale = Math.max(minScale, Math.min(maxScale, newScale));
34+
35+
if (newScale === oldScale) return;
36+
37+
const newPosition = {
38+
x: pointer.x - mousePointTo.x * newScale,
39+
y: pointer.y - mousePointTo.y * newScale,
40+
};
41+
stage.scale({ x: newScale, y: newScale });
42+
stage.position(newPosition);
43+
};
44+
45+
export const defaultMoveViewStrategy = (
46+
event: KonvaEventObject<WheelEvent>,
47+
stage: Konva.Stage,
48+
) => {
49+
const currentScale = stage.scaleX();
50+
stage.position({
51+
x: stage.x() - event.evt.deltaX / currentScale,
52+
y: stage.y() - event.evt.deltaY / currentScale,
53+
});
54+
};

0 commit comments

Comments
 (0)