김 양의 멋따라 개발따기

JavaScript - 핀치줌(Pinch Zoom) 구현하기 본문

Javascript

JavaScript - 핀치줌(Pinch Zoom) 구현하기

개발따라김양 2024. 12. 27. 13:19

1. html 코드

<div id="screen">
     <img  id="target" src="" style="transform: scale(1) translate(0px, 0px);">   
</div>

 

2. JavaScript 코드

 function touchInit(targetElement) {
    zoomStatus = true;
  
    let startX = 0, startY = 0;
    let offsetX = 0, offsetY = 0;
    let scale = 1;  // 초기 스케일
    let isDragging = false;
    let initialDistance = 0;
  
    let touchCenterX = 0, touchCenterY = 0; // 터치 중심점
    const maxScale = 2;  // 최대 확대 배율 설정
  
    // 기준값 설정
    const baseTranslateX = 270;
    const baseTranslateY = 270;
  
    // 요소의 위치 정보
    const rect = targetElement.getBoundingClientRect();
    const elementOffsetX = rect.left; // 요소의 X 위치
    const elementOffsetY = rect.top;  // 요소의 Y 위치
  
    // 값 제한 함수
    function clamp(value, min, max) {
      return Math.max(min, Math.min(value, max));
    }
  
    // 거리 계산 함수
    function getDistance(touches) {
      const [touch1, touch2] = touches;
      const deltaX = touch2.clientX - touch1.clientX;
      const deltaY = touch2.clientY - touch1.clientY;
      return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    }
  
    // 터치 시작
    targetElement.addEventListener('touchstart', (event) => {
      if (event.touches.length === 2) {
        // 핀치 줌 시작
        initialDistance = getDistance(event.touches);
  
        // 중심점 계산 (요소의 위치를 기준으로 보정)
        const touch1 = event.touches[0];
        const touch2 = event.touches[1];
        touchCenterX = ((touch1.clientX + touch2.clientX) / 2) - elementOffsetX;
        touchCenterY = ((touch1.clientY + touch2.clientY) / 2) - elementOffsetY;
      } else if (event.touches.length === 1) {
        // 드래그 시작
        isDragging = true;
        startX = event.touches[0].clientX - offsetX;
        startY = event.touches[0].clientY - offsetY;
      }
    });
  
    // 터치 이동
    targetElement.addEventListener('touchmove', (event) => {
      if (event.touches.length === 2) {
        // 핀치 줌 동작
        const currentDistance = getDistance(event.touches);
        const zoomFactor = currentDistance / initialDistance;
  
        // 스케일 업데이트 및 제한
        scale = clamp(scale * zoomFactor, 1, maxScale); // 확대 범위 제한
        initialDistance = currentDistance;
  
        // 중심점 기준 확대 조정
        offsetX = (scale - 1) * (baseTranslateX - touchCenterX);
        offsetY = (scale - 1) * (baseTranslateY - touchCenterY);
  
        // 이동 경계 제한 추가
        const minOffsetX = -((scale - 1) * baseTranslateX);
        const maxOffsetX = (scale - 1) * baseTranslateX;
        const minOffsetY = -((scale - 1) * baseTranslateY);
        const maxOffsetY = (scale - 1) * baseTranslateY;
  
        offsetX = clamp(offsetX, minOffsetX, maxOffsetX);
        offsetY = clamp(offsetY, minOffsetY, maxOffsetY);
  
        // 변환 적용
        targetElement.style.transform = `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`;
      } else if (event.touches.length === 1 && isDragging) {
        // 드래그 동작
        offsetX = event.touches[0].clientX - startX;
        offsetY = event.touches[0].clientY - startY;
  
        // 이동 경계 제한
        const minOffsetX = -((scale - 1) * baseTranslateX);
        const maxOffsetX = (scale - 1) * baseTranslateX;
        const minOffsetY = -((scale - 1) * baseTranslateY);
        const maxOffsetY = (scale - 1) * baseTranslateY;
  
        offsetX = clamp(offsetX, minOffsetX, maxOffsetX);
        offsetY = clamp(offsetY, minOffsetY, maxOffsetY);
  
        // 변환 적용
        targetElement.style.transform = `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`;
      }
    });
  
    // 터치 종료
    targetElement.addEventListener('touchend', () => {
      isDragging = false;
    });
  }