
https://tech.kakaoent.com/front-end/2023/230310-webview-pinch-zoom/
다시한번 압도적 감사...

우리가 지도앱을 쓸때 많이 하는 행위로,
손가락 두개가 멀어지거나 가까워지는 제스처를 Pinch Zoom 이라고 한다.
const handleTouchMove = useCallback(
(e: React.TouchEvent<HTMLCanvasElement>) => {
e.preventDefault();
const touches = e.touches;
const rect = interactionCanvasRef.current!.getBoundingClientRect();
if (touches.length === 2) {
const dx = touches[0].clientX - touches[1].clientX;
const dy = touches[0].clientY - touches[1].clientY;
const newDistance = Math.sqrt(dx * dx + dy * dy);
const oldDistance = pinchDistanceRef.current;
if (oldDistance > 0) {
const scaleFactor = newDistance / oldDistance;
const newScale = Math.max(
MIN_SCALE,
Math.min(MAX_SCALE, scaleRef.current * scaleFactor)
);
const centerX =
(touches[0].clientX + touches[1].clientX) / 2 - rect.left;
const centerY =
(touches[0].clientY + touches[1].clientY) / 2 - rect.top;
const xs = (centerX - viewPosRef.current.x) / scaleRef.current;
const ys = (centerY - viewPosRef.current.y) / scaleRef.current;
viewPosRef.current.x = centerX - xs * newScale;
viewPosRef.current.y = centerY - ys * newScale;
scaleRef.current = newScale;
draw();
updateOverlay(centerX, centerY);
}
pinchDistanceRef.current = newDistance;
return;
}
touches.length === 2, 화면에 닿은 손가락이 2개일 때만 확대/축소 로직을 실행한다.
dx, dy로 두 손가락의 화면상의 x 좌표와, y좌표를 계산한다.
첫 두손가락의 거리 사이가 oldDistance에 저장된다.
사용자가 Pinch Zoom 을 수행하면 두 손가락 사이의 거리가 바뀐다.
이때 피타고라스 정리를 활용하여 계산한 결과가 newDistance.
const newDistance = Math.sqrt(dx * dx + dy * dy);
이 두 값을 비교하여 확대/축소 배율인 scaleFactor를 계산하고,
기존 배율인 scaleRef.current에 이를 곱하여 확대/축소를 진행한다.
이때 확대와 축소는 두 손가락의 중심점을 기준으로 진행하게 된다.
이렇게 안하면 사용자가 좌측 상단에 중심점을 두든,
우측 하단에 중심점을 두든 중앙에서 확대/축소가 된다.
const xs = (centerX - viewPosRef.current.x) / scaleRef.current;
const ys = (centerY - viewPosRef.current.y) / scaleRef.current;
이 좌표가 다시 화면의 CenterX, CenterY 값으로 적용되어,
사용자가 손가락으로 가리키는 지점을 중심으로 확대/축소되는 동작을 구현하였다.
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
구글링했을때, 분명히 "initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" 하면 브라우저 기본 확대 동작을 막을 수 있다고 했다.
iOS 버전이 업데이트 되면서 safari에서는
viewport를 통해 브라우저를 확대하지 못하게 하는 방법이 불가능하게 되었다.
그래서 중간 발표때 모바일에서 도저히 프로덕트가 동작하지 않게 되었다.
처음엔 touch 이벤트에 preventDefault를 달아서 막아보고자 했는데,
최상위 파일인 Main.tsx에 아래와같이 추가하였더니 기본동작을 막을 수 있었다.
document.addEventListener(
'touchmove',
(event) => {
event.preventDefault();
},
{ passive: false }
);