본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

Canvas 자유 그리기 · 직선 그리기 완전 정리

by 왕진 2026. 7. 16.
반응형

 

 

HTML Canvas

Canvas 자유 그리기 · 직선 그리기 완전 정리

mousemove 자유선과 mousedown/mouseup 직선 그리기를 이벤트 상태 관리 중심으로 정리합니다.

0 오늘 분석할 코드
<canvas id="myCanvas" width="500" height="250"></canvas>
<input type="number" id="widthInput" value="2" onchange="context.lineWidth=this.value">
<input type="color" id="colorInput" value="#00ff00" onchange="context.strokeStyle=this.value">
<script>
let canvas, context;
let prevX = 0, prevY = 0;
let inbound = false;
let startX = 0, startY = 0;
let dragging = false;

window.onload = init;

function init() {
  canvas = document.getElementById("myCanvas");
  context = canvas.getContext("2d");
  canvas.addEventListener("mousemove", move, false);
  canvas.addEventListener("mouseenter", over, false);
  canvas.addEventListener("mouseleave", out, false);
  canvas.addEventListener("mousedown", down, false);
  canvas.addEventListener("mouseup", up, false);
}
</script>
mousemove마우스가 움직일 때마다 발생하는 이벤트입니다.
mousedown마우스 버튼을 누르는 순간 발생하는 이벤트입니다.
mouseup마우스 버튼을 떼는 순간 발생하는 이벤트입니다.
mouseenter마우스가 요소 안으로 들어올 때 발생합니다.
mouseleave마우스가 요소 밖으로 나갈 때 발생합니다.
offsetX이벤트 대상 내부 기준 x좌표입니다.
offsetY이벤트 대상 내부 기준 y좌표입니다.
clearRect()캔버스의 지정 영역을 지웁니다.

1 두 가지 그리기 방식

원문에는 두 가지 마우스 그리기 방식이 있습니다. 첫 번째는 mousemove마다 이전 좌표와 현재 좌표를 연결해 자유롭게 그리는 방식이고, 두 번째는 mousedown에서 시작점을 잡고 mouseup에서 최종 직선을 확정하는 방식입니다.

  • 자유 그리기는 prevX, prevY를 계속 갱신합니다.
  • 직선 그리기는 startX, startY를 고정해 둡니다.
  • mousemove는 미리보기 또는 연속 그리기에 사용합니다.
  • mouseup은 드래그 종료 시점을 처리합니다.

2 mousemove 자유 그리기 흐름
  1. mouseenter에서 현재 좌표를 prevX, prevY에 저장합니다.
  2. mousemove가 발생하면 현재 좌표 curX, curY를 읽습니다.
  3. prev 좌표에서 cur 좌표까지 선을 긋습니다.
  4. 그린 뒤 cur 좌표를 prev 좌표로 다시 저장합니다.
  5. mouseleave에서는 inbound를 false로 바꿔 다음 진입 때 선이 튀지 않게 합니다.

3 mousedown과 mouseup 직선 그리기

직선 그리기 예제는 누른 지점과 뗀 지점을 연결합니다. 이동 중에는 clearRect로 이전 미리보기를 지우고 새 직선을 다시 그립니다.

이벤트 역할 상태 변화
mousedown 시작 좌표 저장 dragging = true
mousemove 현재 위치까지 미리보기 dragging일 때만 실행
mouseup 최종 선 그리기 dragging = false
mouseleave 캔버스 이탈 처리 필요하면 drawing 종료

 

반응형

 

4 offsetX와 offsetY

offsetX와 offsetY는 이벤트가 발생한 요소 내부 기준 좌표입니다. 캔버스 위에서 마우스 위치를 계산할 때 간단히 사용할 수 있습니다.

  • 캔버스 내부 왼쪽 위가 0,0입니다.
  • 마우스가 오른쪽으로 갈수록 offsetX가 커집니다.
  • 마우스가 아래쪽으로 갈수록 offsetY가 커집니다.
  • CSS transform이나 복잡한 배치에서는 getBoundingClientRect 방식도 고려합니다.

5 inbound 상태값

자유 그리기 예제의 inbound는 마우스가 캔버스 안에 들어온 뒤 첫 좌표를 초기화했는지 나타냅니다. 이 값이 없으면 이전에 나간 좌표와 새로 들어온 좌표가 긴 선으로 연결될 수 있습니다.

캔버스 밖으로 나갔다가 다시 들어올 때 선이 갑자기 이어지면 mouseleave에서 상태를 끊는 처리를 확인합니다.

6 dragging 상태값

dragging은 마우스 버튼을 누르고 있는지 나타냅니다. move 함수 초반에 if(!dragging) return을 두면 버튼을 누르지 않은 이동은 무시됩니다.

function move(e) {
  if (!dragging) return;
  context.clearRect(0, 0, canvas.width, canvas.height);
  draw(e.offsetX, e.offsetY);
}

7 선 굵기와 색 변경

원문 예제는 input의 onchange에서 context.lineWidth와 context.strokeStyle을 직접 바꿉니다. 사용자가 값을 바꾼 뒤 그리는 선부터 새 스타일이 적용됩니다.

입력 연결 속성 설명
number lineWidth 선 굵기를 바꿉니다.
color strokeStyle 선 색을 바꿉니다.
range lineWidth 슬라이더 UI로 확장할 수 있습니다.
button clearRect 지우기 기능을 붙일 수 있습니다.

8 clearRect 미리보기의 의미

직선 예제에서 mousemove마다 clearRect를 호출하는 이유는 이전 미리보기 선을 지우기 위해서입니다. 그렇지 않으면 마우스를 움직인 모든 중간 선이 남습니다.

  1. 드래그 시작점을 저장합니다.
  2. 마우스가 움직일 때 캔버스 전체를 지웁니다.
  3. 시작점에서 현재 위치까지 선을 다시 그립니다.
  4. mouseup에서 마지막 선을 한 번 더 그립니다.

9 이벤트 등록 방식

addEventListener는 같은 이벤트에 여러 함수를 붙일 수 있고, HTML 속성보다 관리가 쉽습니다. 원문처럼 익명 함수로 감싸도 되고, 함수 이름을 바로 전달해도 됩니다.

canvas.addEventListener("mousemove", move, false);
canvas.addEventListener("mousedown", down, false);
canvas.addEventListener("mouseup", up, false);

10 실전 개선 포인트
개선 이유
mouseup 대신 window mouseup 추가 캔버스 밖에서 버튼을 떼도 상태를 종료할 수 있습니다.
touch 이벤트 추가 모바일에서도 그릴 수 있습니다.
지우기 버튼 추가 clearRect 기능을 사용자에게 제공합니다.
좌표 함수 분리 마우스와 터치 좌표를 같은 방식으로 처리할 수 있습니다.
상태 객체 사용 prevX, dragging 같은 값을 한곳에서 관리합니다.

11 디버깅 체크리스트
  • canvas id가 myCanvas인지 확인합니다.
  • context가 null이 아닌지 확인합니다.
  • mousemove가 너무 자주 호출되는 것은 정상입니다.
  • 선이 보이지 않으면 strokeStyle과 lineWidth를 확인합니다.
  • 선이 튀면 prevX, prevY 초기화 시점을 확인합니다.
  • 직선이 계속 남으면 clearRect 위치를 확인합니다.

12 연습 과제
과제 목표
브러시 굵기 슬라이더 input range로 lineWidth를 조절합니다.
색상 팔레트 여러 버튼으로 strokeStyle을 바꿉니다.
직선 저장 mouseup 때 선 정보를 배열에 저장합니다.
전체 지우기 버튼 클릭 시 clearRect를 실행합니다.
모바일 대응 touchstart와 touchmove를 추가합니다.

M 자주 하는 실수
실수 문제 해결
mouseleave 처리를 빠뜨림 밖으로 나갔다 들어올 때 긴 선이 생깁니다. inbound나 drawing 상태를 false로 바꿉니다.
dragging 검사 없이 move에서 그림 버튼을 누르지 않아도 선이 그려집니다. if(!dragging) return을 둡니다.
prev 좌표를 갱신하지 않음 모든 선이 한 점에서 시작합니다. stroke 후 prevX, prevY를 현재 좌표로 바꿉니다.
clearRect 위치가 잘못됨 미리보기 선이 누적되거나 최종 선이 사라집니다. 직선 미리보기 흐름에 맞게 지웁니다.
lineWidth 값을 문자열로 방치 대부분 동작하지만 계산 확장 시 헷갈립니다. 필요하면 Number로 변환합니다.

핵심 한 줄 요약

free자유 그리기는 이전 좌표와 현재 좌표를 계속 이어 선을 만듭니다.
line직선 그리기는 시작점과 현재점만 연결하고 미리보기는 clearRect로 갱신합니다.
stateinbound와 dragging 같은 상태값이 마우스 이벤트 예제의 핵심입니다.

Tags

#Canvas#JavaScript#mousemove#mousedown#mouseup#offsetX#offsetY#clearRect#그리기#티스토리
▼ 티스토리 태그 입력란 복사용
Canvas, JavaScript, mousemove, mousedown, mouseup, offsetX, offsetY, clearRect, 그리기, 티스토리
반응형

댓글