Fun with HTML5 Canvas
Day 8-Fun with HTML5 Canvas
구현 사항: html canvas와 다양한 메소드들을 사용하여 그림 그리기
1) canvas를 선택하여 변수 canvas에 할당
const canvas = document.querySelector("#draw");
2) canvas에 무엇인가를 표시하려면 렌더링 컨텍스트에 접근해야 함 -> 변수 cts에 getContext() 사용하여 그리기 메소드와 속성을 가진 context를 할당하며, 메소드의 인자로 "2d"를 명시
const canvas = document.querySelector("#draw");
const ctx = canvas.getContext("2d");
3) window.innerHeight와 window.innerWidth(window의 viewport의 높이와 너비)를 canvas의 높이와 너비로 할당
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
4) ctx의 strokeStyle (그림 그릴 때의 색상), lineJoin (선이 꺾이는 부분의 style), lineCap (선의 끝 부분의 style), lineWidth (선의 너비)를 설정
ctx.strokeStyle = "green";
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.lineWidth = 100;
5) draw() 함수 선언, mousemove 이벤트 발생 시 실행 -> 마우스가 브라우저 내에서 움직일 시 함수 실행
function draw(e) {}
canvas.addEventListener("mousemove", draw);
6) ctx.beginPath() 메소드를 통해 새로운 경로를 생성 -> moveTo() 메소드는 펜을 x와 y로 지정된 좌표로 옮김
let lastX = 0;
let lastY = 0;
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
}
- 매개변수로 들어갈 lastX와 lastY의 값을 0으로 초기화하여 함수 바깥에 선언한 뒤 매개변수로 삽입
7) lineTo() 메소드는 현재의 드로잉 위치에서 x와 y로 지정된 위치까지 선을 그리도록 함
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
}
- 그 x와 y는 event.offsetX와 event.offsetY가 되어야 함
- event.offsetX와 event.offsetY는 좌표를 출력하도록 하는 이벤트가 걸려있는 Dom Node를 기준으로 좌표를 표시함
- 즉, canvas의 왼쪽 상단을 (0, 0)으로 하며 그 지점을 기준으로 마우스의 현재 위치 좌표를 얻을 수 있는 것
8) stroke() 메소드 사용하여 윤곽선 그리기
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
9) 현재 마우스의 위치로 lastX와 lastY를 변경해야 쭉 이어서 그림을 그릴 수 있음 -> lastX와 lastY에 e.offsetX, e.offsetY를 구조 분해 할당
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
10) 마우스를 클릭한 태로 마우스를 움직여야 그려지고, 마우스를 떼거나 마우스가 canvas를 벗어나면 그리는 것을 멈춰야 함 & 마우스를 클릭한 순간의 위치로부터 그림이 시작되어야 함
let lastX = 0;
let lastY = 0;
let isDrawing = false;
function draw(e) {
if (!isDrawing) return;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("mousedown", (e) => {
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY];
});
- 현재 그림을 그리고 있는 상태인지를 표시하기 위한 변수 isDrawing 선언 후 false를 기본값으로 할당
- mousedown 이벤트 발생 시 isDrawing을 true로 변경하며, lastX와 lastY를 mousedown 이벤트가 일어난 위치의 offsetX와 offsetY로 변경
- 함수 실행 시 가장 먼저 isDrawing을 확인 -> false일 경우 바로 return하여 함수가 더 이상 실행되지 않도록 함
11) 마우스를 떼거나 마우스가 canvas 바깥으로 나가면 isDrawing을 false로 변경
let lastX = 0;
let lastY = 0;
let isDrawing = false;
function draw(e) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("mousedown", (e) => {
isDrawing = true;
[lastX, lastY] = [e.offsetX, e.offsetY];
});
canvas.addEventListener("mouseup", () => (isDrawing = false));
canvas.addEventListener("mouseout", () => (isDrawing = false));
12) mousemove가 일어날 때마다 색상이 변하도록 하기 위해 변수 hue 선언 후 strokeStyle을 hsl(${hue}, 100%, 50%)
로 설정
function draw(e) {
ctx.beginPath();
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
13) hue 변수의 범위는 0부터 360까지 -> 함수가 실행될 때마다 hue를 1씩 증가시키며, 만약 hue가 360 이상이 되면 hue를 0으로 되돌리기
function draw(e) {
ctx.beginPath();
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
hue++;
if (hue >= 360) {
hue = 0;
}
14) lineWidth를 줄어들었다가 늘어나는 것을 반복하도록 만들기 위해 direction 변수 선언 -> direction이 true일 때 lineWidth를 증가시키며, false일 때 감소시킬 것, 또한 lineWidth가 100 이상으로 증가라거나 1 이하로 감소하는 시점에 direction의 T/F를 바꿈
function draw(e) {
ctx.beginPath();
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
hue++;
if (hue >= 360) {
hue = 0;
}
if (direction) {
ctx.lineWidth++;
} else {
ctx.lineWidth--;
}
if (ctx.lineWidth >= 100) {
direction = false;
} else if (ctx.lineWidth <= 1) {
direction = true;
}
내가 추가한 기능: 키보드 클릭 시 draw / erase 모드 변경, draw 모드 시 그림 그리고, erase 모드 시 흰 색으로 그릴 수 있도록 하여 지우개 효과 간접 구현
1) 모드를 저장할 변수 mode 선언 후 기본값으로 true 할당 -> keydown 이벤트 발생 시 mode를 변경하도록 함
<canvas tabindex="1" id="draw" width="800" height="800"></canvas>
(...생략...)
let mode = true;
function draw(e) {
ctx.beginPath();
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
(...생략...)
canvas.addEventListener("mousemove", draw);
canvas.addEventListener("mouseup", () => (isDrawing = false));
canvas.addEventListener("mouseout", () => (isDrawing = false));
canvas.addEventListener("keydown", () => (mode = !mode));
- 이 때, canvas에 keydown 이벤트를 listen 시키기 위해 tabindex="1" 속성을 부여해야 함 (참고)
2) 함수 기능을 분리하기 위해 customDrawing() 함수 선언 -> 매개변수로 event 객체, strokeStyle, lineWidth를 받아서 말 그대로 이 함수를 사용하여 그리기 기능을 커스텀할 수 있도록 함
function customDrawing(e, strokeStyle, lineWidth) {
ctx.beginPath();
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.moveTo(lastX, lastY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
[lastX, lastY] = [e.offsetX, e.offsetY];
}
3) draw() 함수 내에 customDrawing() 함수 삽입하여 이전과 같이 그림을 그릴 수 있도록 함 -> 이 때, mode를 확인하여 mode가 true일 땐 이전과 같이 그림 그려지게, false일 경우 lineWidth가 50이며 white 색상으로 그림이 그려지도록 하여 지우개 기능을 간접적으로 구현
function draw(e) {
if (!isDrawing) return;
if (!mode) {
customDrawing(e, "white", 50);
} else {
customDrawing(e, `hsl(${hue}, 100%, 50%)`, ctx.lineWidth);
hue++;
if (hue >= 360) {
hue = 0;
}
if (direction) {
ctx.lineWidth++;
} else {
ctx.lineWidth--;
}
if (ctx.lineWidth >= 100) {
direction = false;
} else if (ctx.lineWidth <= 1) {
direction = true;
}
}
}
Author And Source
이 문제에 관하여(Fun with HTML5 Canvas), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@carmine/JavaScript-30-Days-Challenge-DAY-8저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)