콴다가 태블릿 문제 풀이용 필기 경험을 위해 웹 Canvas API로 프리 드로잉 알고리즘을 외부 라이브러리 없이 밑바닥부터 구현한 과정을 다룬다. 점 잇기에서 시작해 베지어 곡선, 스플라인 접점 교정, 즉각 렌더, 펜 이벤트와 플랫폼별 좌표 보정까지 6단계로 자연스러운 필기를 완성했다.
핵심 포인트- 외부 라이브러리는 커스텀 한계와 데이터 증가 시 성능 이슈, 모듈화 필요 때문에 걷어내고 의존성 없이 직접 구현했다.
- 점을 직선으로 잇는 기본 방식은 각져서, 선형 보간 기반 2차/3차 베지어 곡선(quadraticCurveTo, bezierCurveTo)으로 곡선화했다.
- 곡선 간 접점을 앞뒤 점의 중점에 두면 이전 곡선의 끝기울기와 다음 곡선의 시작기울기가 같아져 스플라인이 매끄럽게 이어진다.
- 점 3개가 모이기 전에도 1개는 원, 2개는 직선으로 즉시 그려 포인터를 즉각 따라가는 느낌을 준다.
- Pen 환경은 같은 좌표에서 pressure/tilt 변화로 중복 Pointer Move가 발생해 동일 좌표 이벤트를 무시하도록 처리했다.
- Safari는 좌표를 정수화해 내려줘 자글거림이 생기므로 인접(거리 sqrt(10) 이내) 점을 생략해 보정했다.
상세 정리- 기본 드로잉: Pointer Move로 들어온 점을 이전 점과 직선으로 잇는 가장 단순한 방식은 선이 각져 부드럽지 않았다.
- 곡선화: 두 점을 1-t와 t로 섞는 선형 보간(lerp)을 0에서 1로 이동시키면 1차 베지어가 되고, 이를 중첩해 2차와 3차 베지어 곡선을 만들었다. 점 3개마다 quadraticCurveTo로 2차 곡선을 그린다.
- 스플라인 문제: 곡선 3개 단위로는 부드러워도 곡선끼리 이음새가 매끄럽지 않았고, 고차 베지어(예: 999차)는 비싸고 이전 획이 현재 점에 영향받는 부작용이 있었다.
- 접점 교정: 접점을 이전 점과 다음 점의 중점에 두면 끝기울기와 시작기울기가 같아진다는 것을 증명해 이음새를 매끄럽게 만들었다.
- 즉각 렌더: 점 3개가 모일 때까지 아무 선도 안 보여 반응이 느려 보이는 문제를, 점 1개는 원, 2개는 직선, 3개는 2차 베지어로 그려 즉시 반응하도록 했다.
- Pen 이벤트: 펜은 동일 좌표라도 pressure와 tilt가 달라 중복 Pointer Move가 발생해 중복 점에 곡선이 그려졌고, currentPoint와 previousPoint 좌표가 같으면 early return으로 걸렀다.
- 정수화 자글거림: Chrome은 정밀 좌표를, Safari는 PC와 모바일 모두 좌표를 정수화해 내려줘 대각선이 반올림되며 자글거림이 발생했다.
- 인접 간소화: 두 점 거리가 sqrt(10)(상하좌우 1에서 3px) 이내면 인접으로 보고 중간 점을 생략했고, 사람이 인지 못 하는 오차로 판단해 자글거림을 없앴다.
- 미시/거시 관점: 인접 간소화 후 남은 점은 서로 인접하지 않으므로 거시적으로 실제 그린 것과 유사해야 자연스럽다고 봤다.
- 과보정 side effect: 빠르고 곡률이 큰 원을 그릴 때 스플라인 접점 교정과 즉각 렌더가 겹쳐 잔상이 남는 과보정이 발생했다.
- 수정: 인접하지 않은 점 구간에선 접점을 옮기는 대신 2번째와 3번째 점의 중점을 접점으로 삼아 2차 베지어를 그려 실제 궤적과 가깝게 보정했다.
- 결과: 6단계 보정을 거쳐 PC와 모바일, 마우스와 펜 어디서든 즉각적이면서 자글거리지 않고 과보정도 덜한 드로잉을 완성했다.
왜 읽나Canvas로 필기/드로잉 기능을 직접 구현하며 베지어 스플라인, 펜 이벤트, 브라우저별 좌표 편차를 다뤄야 하는 프론트엔드 개발자에게.