애드블럭 종료 후 보실 수 있습니다.

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Webview를 위한 핀치 줌(pinch zoom) 구현하기
    Tip 2024. 2. 24. 12:53
    728x90
    반응형

    핀치 줌은 모바일 웹 또는 앱에서 자주 사용되는 기능 중 하나입니다.

    사용자는 두 손가락을 화면에 대고 벌리거나 모으는 동작을 통해 화면을 확대하거나 축소할 수 있습니다.

    이러한 인터랙션은 보다 세밀한 화면 조작을 가능하게 하며, 사용자 경험을 크게 향상합니다.

    해당 기능은 일반적인 앱의 경우 구현하기 간단하지만 웹기반 웹뷰에서는 특정 영역(주로 이미지)을 확대 시 다른 부분들도 같이 확대되며 다른 페이지 이동 시 화면 비율이 이상해지는 등의 문제가 발생할 수 있습니다.

    기본 핀치 줌 구현

    핀치 줌 기능을 구현하기 위해서는 먼저 터치 이벤트를 적절히 처리할 수 있어야 합니다.

    기본적인 구현 방법은 다음과 같습니다.

    1. 사용자가 화면을 터치하는 순간부터 터치가 끝나는 순간까지의 이벤트를 감지합니다.
    2. 두 손가락 사이의 거리를 계산하여, 이전 거리와 비교함으로써 확대 또는 축소 여부를 판단합니다.

    이 과정에서, 확대 또는 축소는 화면 전체에 대해 균일하게 적용됩니다. 즉, 사용자가 화면의 특정 부분을 중심으로 확대하고자 해도, 확대의 중심점은 고려되지 않는 한계가 있습니다.

    중심점 기반 확대 구현

    사용자가 원하는 대로 중심점을 기준으로 확대하기 위해서는 몇 가지 추가적인 계산이 필요합니다.

    구현 과정은 다음과 같습니다.

    1. Transform 상태 관리

    먼저, 확대/축소 상태를 관리할 객체를 정의합니다.

    // touch.ts
    const state: TransformState = {
      x: 0,
      y: 0,
      scale: 1,
    };
    
    const setState = ({ x, y, scale }: TransformState) => {
      state.x = x;
      state.y = y;
      state.scale = scale;
      target.style.transform = `translateX(${x}px) translateY(${y}px) scale(${scale})`;
    };

    이 상태 관리 함수를 통해 확대/축소와 함께 위치 변화를 적용할 수 있습니다.

    2. 중심점 기반 확대 공식 적용

    중심점을 기준으로 확대하려면, 확대될 때 x, y 값을 적절히 보정해주어야 합니다.

    // pinchZoom.ts
    target.style.transformOrigin = 'top left';
    
    const handlePinch = ({ zoom, x: centerX, y: centerY }: { zoom: number; x: number; y: number }) => {
      if (zoom === 0) {
        return;
      }
    
      const { x, y, scale } = getState();
    
      const zoomWeight = 0.02;
      const nextScale = scale + (zoom > 0 ? zoomWeight : -zoomWeight);
    
      const biasX = ((centerX - x) * nextScale) / scale - (centerX - x);
      const biasY = ((centerY - y) * nextScale) / scale - (centerY - y);
      const nextX = x - biasX;
      const nextY = y - biasY;
    
      const nextState = {
        x: nextX,
        y: nextY,
        scale: nextScale,
      };
    
      setState(nextState);
    };

    이 공식은 확대/축소 시 중심점이 변하지 않도록 위치를 보정해 줍니다.

    transformOrigin을 top left로 설정하는 것이 중요합니다.

    3. 핀치 중심점 좌표 계산

    마지막으로, 핀치 동작의 중심점을 계산합니다.

    // touchMoveHandler
    ...
    if (prevDiff > 0) {
      const zoom = curDiff - prevDiff;
    
      const x = (evHistory[0].clientX + evHistory[1].clientX) / 2;
      const y = (evHistory[0].clientY + evHistory[1].clientY) / 2;
      const { top, left } = (ev.currentTarget as HTMLElement).getBoundingClientRect();
      onPinch({ zoom, x: x - left, y: y - top });
    }

    여기서 중심점은 사용자가 터치한 두 점의 평균 좌표로 계산되며, 이를 통해 보다 정확한 확대/축소 효과를 구현할 수 있습니다.

     

    use-gesture

    ps. 이곳에서는 직접 구현하는 방법에 대해 설명했지만 use-gesture와 같은 라이브러리를 이용하는 방법도 추천합니다.

    반응형

    댓글

Designed by Tistory.