본문 바로가기

비개발자의 개발 일지

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

시리즈 보기
php

Canvas 드래그 그리기 완전 정리

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

 

 

Web Development · Canvas

Canvas 드래그 그리기

마우스 이벤트와 GraphicEditor 클래스로 자유 그리기 흐름을 정리합니다.
시작 오늘 분석할 코드

이 장은 Canvas에 이미지와 텍스트를 그리는 방법, 그리고 마우스 드래그 이벤트로 선을 이어 그리는 방법을 다룹니다.

let startX = 0, startY = 0; let drawing = false; function move(e) { if (!drawing) return; let curX = e.offsetX, curY = e.offsetY; draw(curX, curY); startX = curX; startY = curY; } class GraphicEditor { constructor(canvas, context) { this.canvas = canvas; this.context = context; } }
mousedown마우스를 누른 순간입니다.
mousemove마우스가 움직이는 동안 발생합니다.
mouseup마우스를 뗀 순간입니다.
offsetXcanvas 내부 x 좌표입니다.
drawing현재 그리는 중인지 나타내는 상태값입니다.
class상태와 메서드를 객체로 묶습니다.

1 드래그 그리기의 상태

마우스 드래그 그리기는 마지막 좌표와 현재 좌표를 계속 선으로 연결하는 구조입니다. drawing 변수는 마우스가 눌린 상태인지 알려주는 스위치입니다.

상태 의미 변경 시점
startX/startY 마지막 좌표 mousedown, mousemove 후
drawing 그리는 중 여부 down true, up/out false
curX/curY 현재 좌표 mousemove
context 그리기 도구 init에서 생성

2 이벤트 등록

첫 번째 버전은 canvas.addEventListener로 mousemove, mousedown, mouseup, mouseout 리스너를 등록합니다.

mousedown
drawing true
mousemove draw
mouseup false

3 offsetX와 offsetY

마우스 좌표는 e.offsetX, e.offsetY로 읽습니다. 이 값은 이벤트 대상인 canvas의 왼쪽 위를 기준으로 한 좌표라 그리기에 적합합니다.

좌표 기준 용도
offsetX canvas 내부 x 그리기 x 좌표
offsetY canvas 내부 y 그리기 y 좌표
clientX 브라우저 viewport 화면 위치
pageX 문서 전체 스크롤 포함 위치

 

반응형

 

4 draw 함수

draw 함수는 startX/startY에서 curX/curY까지 선을 긋습니다. 그린 뒤 현재 좌표를 다시 시작 좌표로 저장해야 연속 선이 됩니다.

1
beginPath새 선 경로를 시작합니다.
2
moveTo마지막 좌표로 이동합니다.
3
lineTo현재 좌표까지 선을 추가합니다.
4
stroke선을 화면에 그립니다.

5 클래스 버전 구조

두 번째 버전은 GraphicEditor 클래스로 canvas, context, startX, startY, drawing 상태를 객체 안에 묶습니다.

프로퍼티 역할 초기값
canvas 그리기 대상 DOM myCanvas
context 2D context getContext 결과
startX/startY 마지막 좌표 0
drawing 그리는 중 여부 false

6 ge 전역 변수 이유

원문 주석처럼 이벤트 리스너 안의 this는 GraphicEditor 객체가 아니라 canvas를 가리킬 수 있습니다. 그래서 전역 변수 ge를 통해 ge.down(e)처럼 명시적으로 호출합니다.

this.down(e)
이벤트 실행 시 this가 canvas라면 down 메서드가 없어 오류가 납니다.
ge.down(e)
전역으로 저장한 GraphicEditor 객체의 메서드를 확실히 호출합니다.

7 addEventListener와 onmousedown

첫 번째 버전은 addEventListener를 사용하고 두 번째 버전은 onmousedown 속성에 함수를 대입합니다. 둘 다 가능하지만 addEventListener는 여러 리스너를 붙이기 쉽습니다.

방식 장점 주의
addEventListener 여러 리스너 등록 가능 코드가 조금 길어짐
onmousedown 대입 간단함 기존 핸들러를 덮을 수 있음
클래스 메서드 상태를 객체로 묶음 this 바인딩 주의
전역 함수 입문 단계에서 이해 쉬움 전역 변수 증가

8 자주 하는 실수
실수 증상 해결
drawing 검사 누락 마우스만 움직여도 그려집니다. if(!drawing) return을 둡니다.
start 좌표 갱신 누락 선이 한 점에서 계속 뻗습니다. move 후 startX/startY를 현재 좌표로 바꿉니다.
mouseout 처리 없음 캔버스 밖에서도 이어질 수 있습니다. out에서 drawing false 처리합니다.
this 바인딩 착각 클래스 메서드 호출 오류가 납니다. ge 또는 bind/arrow 함수를 사용합니다.
offset/client 혼동 좌표가 어긋납니다. canvas 내부 좌표는 offsetX/Y를 씁니다.

9 복습 체크리스트
체크 통과 기준
mousedown 그리기 시작 좌표를 저장합니다.
mousemove drawing 상태일 때만 선을 긋습니다.
mouseup 그리기 상태를 false로 바꿉니다.
mouseout 캔버스 밖으로 나가면 그리기를 멈춥니다.
class 상태와 메서드를 GraphicEditor에 묶습니다.
this 이벤트 핸들러의 this 주의점을 설명합니다.

10 추가 테스트
테스트 확인 내용
클릭 없이 이동 선이 그려지지 않아야 합니다.
드래그 연속 선이 따라와야 합니다.
마우스 밖으로 이동 그리기가 멈춰야 합니다.
선 색 변경 strokeStyle을 바꿔 색 차이를 봅니다.
클래스 버전 ge 메서드 호출 흐름을 확인합니다.

11 드래그 상태 디버깅

드래그 그리기에서 선이 끊기거나 엉뚱하게 그려지면 drawing 상태와 좌표 갱신을 먼저 확인합니다.

문제 가능한 원인 확인
클릭 없이 선이 그려짐 drawing 검사 누락 if(!drawing) return
선이 한 점에서 뻗음 start 좌표 갱신 누락 startX = curX
캔버스 밖에서 이어짐 mouseout 처리 없음 out에서 false
좌표가 어긋남 clientX 사용 offsetX/Y 사용
클래스 버전 오류 this 바인딩 문제 ge.down(e) 호출
A
down 확인mousedown에서 drawing이 true가 되는지 봅니다.
B
move 확인mousemove에서 curX/curY가 계속 바뀌는지 봅니다.
C
draw 확인moveTo와 lineTo 좌표가 이어지는지 봅니다.
D
up/out 확인그리기 상태가 false로 돌아오는지 봅니다.

12 클래스 설계 개선

GraphicEditor 클래스는 캔버스 그리기에 필요한 상태를 하나로 묶습니다. 더 확장하려면 색상, 굵기, 지우기, 초기화 기능도 프로퍼티와 메서드로 넣을 수 있습니다.

기능 프로퍼티/메서드 역할
선 색 this.color strokeStyle에 적용
선 굵기 this.lineWidth context.lineWidth에 적용
지우기 clear() clearRect로 전체 삭제
상태 변경 setStyle() 색과 굵기 변경
이벤트 연결 bindEvents() 리스너 등록 분리

13 추가 연습
연습 목표
선 굵기 변경 input range 값으로 lineWidth를 바꿉니다.
선 색 변경 input color 값으로 strokeStyle을 바꿉니다.
지우기 버튼 clearRect(0,0,width,height)를 호출합니다.
터치 이벤트 touchstart/touchmove를 추가합니다.
저장 기능 canvas.toDataURL로 이미지 저장을 실험합니다.

14 실전 점검 순서
  1. 이벤트 연결 확인: mousedown, mousemove, mouseup, mouseout이 모두 등록되어 있는지 봅니다.
  2. 상태값 확인: drawing 값이 눌렀을 때 true, 뗐을 때 false로 바뀌는지 확인합니다.
  3. 좌표 확인: offsetX와 offsetY가 캔버스 내부 기준 좌표인지 콘솔로 찍어봅니다.
  4. 그리기 순서 확인: beginPath, moveTo, lineTo, stroke 순서를 지킵니다.
  5. 마우스 이탈 처리: 캔버스 밖으로 나갔을 때도 drawing을 false로 바꿉니다.
드래그 그리기에서 가장 흔한 문제는 좌표보다 상태값입니다. 선이 계속 이어지면 mouseup이나 mouseout 처리를 먼저 확인합니다.

15 이벤트별 역할표
이벤트 주요 역할 실패 증상
mousedown 그리기 시작, 시작 좌표 저장 눌러도 선이 시작되지 않습니다.
mousemove 현재 좌표까지 선을 그림 마우스를 움직여도 화면이 변하지 않습니다.
mouseup 그리기 종료 버튼을 떼도 선이 계속 이어집니다.
mouseout 캔버스 이탈 시 종료 밖으로 나갔다 들어오면 선이 튑니다.
click 버튼 기능 실행 초기화나 저장 버튼이 동작하지 않습니다.

16 완성도 올리는 체크리스트
  • 선 굵기와 색상은 전역 변수보다 객체의 속성으로 관리하면 확장하기 쉽습니다.
  • 모바일까지 고려한다면 touchstart, touchmove, touchend를 별도로 연결합니다.
  • 캔버스 위치가 바뀌는 레이아웃에서는 getBoundingClientRect 기반 좌표 계산이 안전합니다.
  • 지우기 기능은 clearRect만 호출하지 말고 배경색을 다시 칠할지 결정합니다.
  • 저장 기능을 붙일 때는 toDataURL 결과가 현재 캔버스 상태를 그대로 담는지 확인합니다.

핵심 한 줄 요약

statedrawing과 startX/startY가 드래그 그리기의 핵심 상태입니다.
eventmousedown, mousemove, mouseup, mouseout이 함께 동작합니다.
classGraphicEditor는 canvas와 context, 좌표 상태를 하나로 묶습니다.

Tags

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

댓글