Canvas 자유 그리기 · 직선 그리기 완전 정리
mousemove 자유선과 mousedown/mouseup 직선 그리기를 이벤트 상태 관리 중심으로 정리합니다.
<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에서 최종 직선을 확정하는 방식입니다.
- 자유 그리기는 prevX, prevY를 계속 갱신합니다.
- 직선 그리기는 startX, startY를 고정해 둡니다.
- mousemove는 미리보기 또는 연속 그리기에 사용합니다.
- mouseup은 드래그 종료 시점을 처리합니다.
- mouseenter에서 현재 좌표를 prevX, prevY에 저장합니다.
- mousemove가 발생하면 현재 좌표 curX, curY를 읽습니다.
- prev 좌표에서 cur 좌표까지 선을 긋습니다.
- 그린 뒤 cur 좌표를 prev 좌표로 다시 저장합니다.
- mouseleave에서는 inbound를 false로 바꿔 다음 진입 때 선이 튀지 않게 합니다.
직선 그리기 예제는 누른 지점과 뗀 지점을 연결합니다. 이동 중에는 clearRect로 이전 미리보기를 지우고 새 직선을 다시 그립니다.
| 이벤트 | 역할 | 상태 변화 |
|---|---|---|
| mousedown | 시작 좌표 저장 | dragging = true |
| mousemove | 현재 위치까지 미리보기 | dragging일 때만 실행 |
| mouseup | 최종 선 그리기 | dragging = false |
| mouseleave | 캔버스 이탈 처리 | 필요하면 drawing 종료 |
offsetX와 offsetY는 이벤트가 발생한 요소 내부 기준 좌표입니다. 캔버스 위에서 마우스 위치를 계산할 때 간단히 사용할 수 있습니다.
- 캔버스 내부 왼쪽 위가 0,0입니다.
- 마우스가 오른쪽으로 갈수록 offsetX가 커집니다.
- 마우스가 아래쪽으로 갈수록 offsetY가 커집니다.
- CSS transform이나 복잡한 배치에서는 getBoundingClientRect 방식도 고려합니다.
자유 그리기 예제의 inbound는 마우스가 캔버스 안에 들어온 뒤 첫 좌표를 초기화했는지 나타냅니다. 이 값이 없으면 이전에 나간 좌표와 새로 들어온 좌표가 긴 선으로 연결될 수 있습니다.
dragging은 마우스 버튼을 누르고 있는지 나타냅니다. move 함수 초반에 if(!dragging) return을 두면 버튼을 누르지 않은 이동은 무시됩니다.
function move(e) {
if (!dragging) return;
context.clearRect(0, 0, canvas.width, canvas.height);
draw(e.offsetX, e.offsetY);
}
원문 예제는 input의 onchange에서 context.lineWidth와 context.strokeStyle을 직접 바꿉니다. 사용자가 값을 바꾼 뒤 그리는 선부터 새 스타일이 적용됩니다.
| 입력 | 연결 속성 | 설명 |
|---|---|---|
| number | lineWidth | 선 굵기를 바꿉니다. |
| color | strokeStyle | 선 색을 바꿉니다. |
| range | lineWidth | 슬라이더 UI로 확장할 수 있습니다. |
| button | clearRect | 지우기 기능을 붙일 수 있습니다. |
직선 예제에서 mousemove마다 clearRect를 호출하는 이유는 이전 미리보기 선을 지우기 위해서입니다. 그렇지 않으면 마우스를 움직인 모든 중간 선이 남습니다.
- 드래그 시작점을 저장합니다.
- 마우스가 움직일 때 캔버스 전체를 지웁니다.
- 시작점에서 현재 위치까지 선을 다시 그립니다.
- mouseup에서 마지막 선을 한 번 더 그립니다.
addEventListener는 같은 이벤트에 여러 함수를 붙일 수 있고, HTML 속성보다 관리가 쉽습니다. 원문처럼 익명 함수로 감싸도 되고, 함수 이름을 바로 전달해도 됩니다.
canvas.addEventListener("mousemove", move, false);
canvas.addEventListener("mousedown", down, false);
canvas.addEventListener("mouseup", up, false);
| 개선 | 이유 |
|---|---|
| mouseup 대신 window mouseup 추가 | 캔버스 밖에서 버튼을 떼도 상태를 종료할 수 있습니다. |
| touch 이벤트 추가 | 모바일에서도 그릴 수 있습니다. |
| 지우기 버튼 추가 | clearRect 기능을 사용자에게 제공합니다. |
| 좌표 함수 분리 | 마우스와 터치 좌표를 같은 방식으로 처리할 수 있습니다. |
| 상태 객체 사용 | prevX, dragging 같은 값을 한곳에서 관리합니다. |
- canvas id가 myCanvas인지 확인합니다.
- context가 null이 아닌지 확인합니다.
- mousemove가 너무 자주 호출되는 것은 정상입니다.
- 선이 보이지 않으면 strokeStyle과 lineWidth를 확인합니다.
- 선이 튀면 prevX, prevY 초기화 시점을 확인합니다.
- 직선이 계속 남으면 clearRect 위치를 확인합니다.
| 과제 | 목표 |
|---|---|
| 브러시 굵기 슬라이더 | input range로 lineWidth를 조절합니다. |
| 색상 팔레트 | 여러 버튼으로 strokeStyle을 바꿉니다. |
| 직선 저장 | mouseup 때 선 정보를 배열에 저장합니다. |
| 전체 지우기 | 버튼 클릭 시 clearRect를 실행합니다. |
| 모바일 대응 | touchstart와 touchmove를 추가합니다. |
| 실수 | 문제 | 해결 |
|---|---|---|
| mouseleave 처리를 빠뜨림 | 밖으로 나갔다 들어올 때 긴 선이 생깁니다. | inbound나 drawing 상태를 false로 바꿉니다. |
| dragging 검사 없이 move에서 그림 | 버튼을 누르지 않아도 선이 그려집니다. | if(!dragging) return을 둡니다. |
| prev 좌표를 갱신하지 않음 | 모든 선이 한 점에서 시작합니다. | stroke 후 prevX, prevY를 현재 좌표로 바꿉니다. |
| clearRect 위치가 잘못됨 | 미리보기 선이 누적되거나 최종 선이 사라집니다. | 직선 미리보기 흐름에 맞게 지웁니다. |
| lineWidth 값을 문자열로 방치 | 대부분 동작하지만 계산 확장 시 헷갈립니다. | 필요하면 Number로 변환합니다. |
핵심 한 줄 요약
Tags
'php' 카테고리의 다른 글
| 웹 스토리지 · 오디오/비디오 완전 정리 (0) | 2026.07.16 |
|---|---|
| JavaScript 쿠키 · Canvas 폼 원호 완전 정리 (0) | 2026.07.16 |
| Canvas 텍스트 · 폼 그리기 완전 정리 (0) | 2026.07.16 |
| Canvas 도형 함수 완전 정리 (0) | 2026.07.15 |
| Canvas 이미지·텍스트 완전 정리 (0) | 2026.07.15 |
왕진 블로그

댓글