Sandcorp샌드코프 · 웹·AI 제품 개발

새 프로젝트 받는 중 · 갱신 2026.11sandcorp.co.krGitHub

← 팁

프론트·UI

touch-action: none을 주면 브라우저 제스처를 직접 만들어야 한다

그리기 캔버스에 touch-action: none을 주면 손가락 스크롤까지 막힌다. 한 손가락은 그리기, 두 손가락은 스크롤로 직접 나눈다.

그리기 캔버스에서 손가락으로 선을 그을 때 페이지가 같이 스크롤되지 않게 touch-action: none을 줬더니, 이번에는 캔버스 위에서 스크롤을 아예 할 수 없다. 캔버스가 화면을 덮는 모바일에서는 페이지에 갇힌다.

touch-action: none은 그 요소 위에서 브라우저의 모든 터치 제스처(스크롤, 핀치 확대)를 끈다. 그리기만 살리고 스크롤만 남기는 선택지는 없다. 끈 제스처가 필요하면 직접 만들어야 한다.

포인터를 pointerId별로 추적한다. 손가락 하나면 그리기, 두 번째 손가락이 닿으면 진행 중인 획을 취소하고 두 점의 가운데가 움직인 만큼 스크롤한다. 펜(pointerType === 'pen')은 하나여도 항상 그리기로 친다.

const pointers = new Map<number, { x: number; y: number }>();
let lastMid: { x: number; y: number } | null = null;

const mid = () => {
  const [a, b] = [...pointers.values()];
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
};

canvas.addEventListener('pointerdown', (e) => {
  pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
  if (e.pointerType === 'pen' || pointers.size === 1) startStroke(e);
  else if (pointers.size === 2) {
    cancelStroke();
    lastMid = mid();
  }
});

canvas.addEventListener('pointermove', (e) => {
  if (!pointers.has(e.pointerId)) return;
  pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
  if (pointers.size === 2 && lastMid) {
    const m = mid();
    scroller.scrollBy(lastMid.x - m.x, lastMid.y - m.y);
    lastMid = m;
  } else {
    extendStroke(e);
  }
});

const release = (e: PointerEvent) => {
  pointers.delete(e.pointerId);
  if (pointers.size < 2) lastMid = null;
  if (pointers.size === 0) endStroke(e);
};
canvas.addEventListener('pointerup', release);
canvas.addEventListener('pointercancel', release);

핀치 확대도 필요하면 같은 방식으로, 두 점 사이 거리의 비율로 배율을 바꾼다.

문의 →