- Published on
웹 프론트 Pan Zoom 개발기
- Authors

- Name
- Mingyu Kim
서론
지금 다니는 회사에서는 위성영상을 인공지능으로 분석해 사용자에게 정보를 제공하는 제품을 만들고 있습니다. 그중 최근 위성영상을 세그멘테이션하는 툴을 만들게 되었습니다. 위성영상을 확대/축소하고 이동하는 기능이 어려울 것이라 생각했지만, 기존에 비슷한 툴을 한번 만들어 본 적이 있어 코드를 어느 정도 가져오면 되겠다고 생각했습니다. 하지만 마음처럼 되지 않았고, 공식을 정리하고 기록해 둘 필요를 느껴 이 글을 작성하게 되었습니다.
요구사항
먼저 요구사항 중에는 줌과 이동 기능이 있었습니다. 다만 줌을 할 때에는 마우스 포인터를 기준으로 잡고 줌 인 / 아웃을 해야 했습니다(아래 영상 참조). 그리고 이동은 여느 프로그램처럼 드래그로 이동하면 되었습니다.
네이버 지도의 줌 인 / 아웃
ZOOM
먼저 어려울 것 같은 줌부터 해결해 보았습니다. 이걸 어떻게 구현해야 하나 고민하고 있을 때, 마침 회사 동료분이 졸업작품으로 비슷한 것을 구현하신 적이 있었습니다. 그분께 조언을 구하니 동차 변환 행렬이라는 것을 찾아보라고 알려주셨습니다. 찾아보니 잘 정리된 블로그가 있었습니다. 행렬을 대수식으로 표현하면 다음과 같은 식이 나옵니다.
여기에서 는 확대 비율을 의미합니다. 는 기존의 좌표를, 는 기준이 되는 좌표를 의미합니다.
예를 들어 을 를 기준으로 2배 확대하였을 때의 좌표는 다음 계산 결과에 따라 이 됩니다.
이것을 코드로 적용해 보았습니다.
const leftTop = new Point(0, 0)
function zoom(zoomLevel: number) {
const newX = zoomLevel * leftTop.x + relativePoint.x * (1 - zoomLevel)
const newY = zoomLevel * leftTop.y + relativePoint.y * (1 - zoomLevel)
return new Point(newX, newY)
}
PAN
마우스를 잡고 드래그하여 위치를 옮기는 것을 Panning이라고 부릅니다. 패닝된 좌표를 구하는 방법은 굉장히 쉽습니다. 다들 눈치채셨겠지만, 마우스의 무브먼트만큼 기존 좌표에 더해주면 됩니다.
const leftTop = new Point(0, 0)
const panningEventHandler = (e: MouseEvent) => {
return new Point(leftTop.x + e.movementX, leftTop.y + e.movementY)
}
결과물
저는 위 코드와 SVG의 transform matrix를 이용하여 Pan, Zoom 요구사항을 구현하였습니다.
const leftTop = new Point(0, 0)
function zoom(zoomLevel: number) {
const newX = zoomLevel * leftTop.x + relativePoint.x * (1 - zoomLevel)
const newY = zoomLevel * leftTop.y + relativePoint.y * (1 - zoomLevel)
return new Point(newX, newY)
}
const panningEventHandler = (e: MouseEvent) => {
return new Point(leftTop.x + e.movementX, leftTop.y + e.movementY)
}
// matrix의 0, 3번째 자리에 줌값을, 4번째 자리엔 x값을, 5번째 자리엔 y값을 넣어주면 됩니다.
<svg transform="matrix(1, 0, 0, 1, 0, 0)"></svg>