목차
canvas에서 이미지 줌, 이동 기능을 구현 코드
import React, { useRef, useEffect } from 'react';
const img = new Image();
const INITIAL_POSITION = { x: 0, y: 0 };
const MIN_SCALE = 0.1;
const MAX_SCALE = 10;
function App() {
const zoomCanvasRef = useRef(null);
const scaleRef = useRef(1);
const panningRef = useRef(false);
const viewPosRef = useRef(INITIAL_POSITION);
const startPosRef = useRef(INITIAL_POSITION);
const setTransform = () => {
const zoomCanvas = zoomCanvasRef.current;
const context = zoomCanvas.getContext('2d');
context.setTransform(
scaleRef.current,
0,
0,
scaleRef.current,
viewPosRef.current.x,
viewPosRef.current.y
);
};
const draw = () => {
const zoomCanvas = zoomCanvasRef.current;
const context = zoomCanvas.getContext('2d');
zoomCanvas.width = zoomCanvas.width;
setTransform();
context.drawImage(img, 0, 0, zoomCanvas.width, zoomCanvas.height);
};
useEffect(() => {
img.src =
'https://firebasestorage.googleapis.com/v0/b/storege-974dc.appspot.com/o/image%2Frabbit.jpeg?alt=media&token=cc501a63-0258-4aa3-809f-c45b743d2735';
// Load image
img.onload = function () {
draw();
};
}, []);
const handleMouseDown = (e) => {
const { offsetX, offsetY } = e.nativeEvent;
e.preventDefault();
startPosRef.current = {
x: offsetX - viewPosRef.current.x,
y: offsetY - viewPosRef.current.y,
};
panningRef.current = true;
};
const handleMouseUp = (e) => {
panningRef.current = false;
};
const handleMouseMove = (e) => {
const { offsetX, offsetY } = e.nativeEvent;
e.preventDefault();
if (!panningRef.current) {
return;
}
viewPosRef.current = {
x: offsetX - startPosRef.current.x,
y: offsetY - startPosRef.current.y,
};
draw();
};
const handleWheel = (e) => {
const { offsetX, offsetY } = e.nativeEvent;
e.preventDefault();
const xs = (offsetX - viewPosRef.current.x) / scaleRef.current;
const ys = (offsetY - viewPosRef.current.y) / scaleRef.current;
const delta = -e.deltaY;
const newScale =
delta > 0 ? scaleRef.current * 1.2 : scaleRef.current / 1.2;
if (newScale >= MIN_SCALE && newScale <= MAX_SCALE) {
scaleRef.current = newScale;
viewPosRef.current = {
x: offsetX - xs * scaleRef.current,
y: offsetY - ys * scaleRef.current,
};
}
draw();
};
return (
<canvas
ref={zoomCanvasRef}
width="500"
height="500"
style={{ border: ' 1px solid' }}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onWheel={handleWheel}
/>
);
}
export default App;
코드 설명
useState 대신 useRef를 사용한 이유는 상태를 변경할 때마다 컴포넌트가 다시 렌더링되는 것을 방지하기 위해서입니다.
마우스 이벤트에서 draw 함수를 호출해 다시 그려주기 때문에 useState 대신 useRef로 관리 하는 것이 낫다고 생각하여 useRef를 사용하였습니다.
const scaleRef : 현재 확대/축소 비율을 저장합니다.
const panningRef : 드래그 중인지 여부를 저장합니다.
const viewPosRef : 현재 이미지 위치를 저장합니다.
const startPosRef : 마우스 드래그 시작 위치를 저장합니다.
setTransform 함수는 캔버스에 현재 확대/축소 비율과 화면 위치를 설정합니다.
handleWheel 함수를 풀어보면 다음과 같습니다.
const xs = (offsetX - viewPosRef.current.x) / scaleRef.current;
마우스 포인터의 X 좌표(offsetX)에서 현재 이미지 위치(viewPosRef.current.x)를 빼고, 이미지의 현재 스케일(scaleRef.current)로 나누어 xs 값을 계산합니다.
const ys = (offsetY - viewPosRef.current.y) / scaleRef.current;
마우스 포인터의 Y 좌표(offsetY)에서 현재 이미지 위치(viewPosRef.current.y)를 빼고, 이미지의 현재 스케일(scaleRef.current)로 나누어 ys 값을 계산합니다.
xs, ys 값은 이미지의 어느 부분이 확대/축소되는지를 결정하는 데 사용됩니다.
const delta = -e.deltaY;
e.deltaY는 마우스 휠 이벤트의 수직 휠 위치 변경 값을 나타냅니다. 이 값을 -1을 곱하여 delta 변수에 저장합니다. 마우스 휠 이벤트에서 deltaY 값이 양수이면 축소, 음수이면 확대로 처리하기 위함입니다.
const newScale = delta > 0 ? scaleRef.current * 1.2 : scaleRef.current / 1.2;
delta 값이 양수일 경우, 이미지를 확대하려면 현재 스케일에 1.2를 곱하고, 음수일 경우 축소하려면 현재 스케일에 1.2로 나눕니다. 이렇게 계산된 결과를 newScale 변수에 저장합니다.
if (newScale >= MIN_SCALE && newScale <= MAX_SCALE) { ... }
newScale 값이 MIN_SCALE과 MAX_SCALE 사이에 있는지 확인합니다. 그렇다면 이미지의 스케일을 갱신하고, 이미지 위치를 업데이트하여 확대/축소된 이미지가 마우스 포인터를 기준으로 정상적으로 보이게 합니다. 이렇게 함으로써 이미지가 설정한 최소 및 최대 스케일 범위를 벗어나지 않도록 제한할 수 있습니다.
Canvas setTransform 함수
이 함수의 주 목적은 canvas의 기본 확대, 경사도, 이동거리를 설정하기 위해 사용
setTransform(a, b, c, d, e, f)
매개변수 설명
- a: 수평 방향 확대 또는 축소 비율
- b: 수직 방향 경사율
- c: 수평 방향 경사율
- d: 수직 방향 확대 또는 축소 비율
- e: 수평 방향 이동 거리
- f: 수직 방향 이동 거리
실행화면
반응형
'Project > bounding-box' 카테고리의 다른 글
React에서 Tooltip 만들기 (0) | 2023.04.15 |
---|