🖼 바닐라 JS로 그림 앱 만들기 : canvas, js 기능 구현 강의 기록
서론
노마드코더의 강의 중 하나인 바닐라 JS로 그림 앱 만들기 를 완강했다.
공부 중에 새롭게 알게된 것, 기록해놔야겠다고 생각했던 것들 ... 을 모두 모아 기록해둔다.
canvas는 정말 재미있는 기능이어서 매우 흥미로웠다! 꼭 활용해보고 싶다.
<canvas>
HTML의 요소.
보통 자바스크립트를 사용하여 그림을 그리는 데에 사용한다.
ex) 그래프 그리기, 사진 합성, 애니메이션 만들기 ... 등
canvas 크기 지정
// canvas
const canvas = document.querySelector("canvas");
// canvas width, height 지정
canvas.width = 가로;
canvas.height = 세로;
canvas 렌더링 컨텍스트 지정
// 렌더링 컨텍스트 타입을 지정 (ex. 2d, bitmaprenderer... )
const ctx = canvas.getContext("2d");
+ WebGL (Javascript API) 을 사용하여 3D 그래픽 애플리케이션을 만들 수 있다. 🔗여기
fillRect / strokeRect
// fillRect => 함수가 실행되면 fill(채우기)이 실행된다 (시작x, 시작y, 채울w, 채울h)
// -----> fill, rect를 함께 담은 단축함수shortcut
// strokeRect 는 fill 을 strok(선)으로만 바꾸면 같다.
// rect 만 있는 상태는 색상자체는 "투명"이라고 생각하자, 꼭 색이나 선을 채워줘야 한다
ctx.fillRect(50, 50, 5, 5);
// 는 밑과 같다.
ctx.rect(50, 50, 5, 5);
ctx.fill();
beginPath()
// beginPath()를 사용하면 사용하기 전과 후 경로가 분리된다.
// 새로운 경로가 시작된다는 뜻.
ctx.beginPath();
moveTo / lineTo
// moveTo : 브러쉬를 이동한다. (시작지점)
ctx.moveTo(50,50);
// lineTo : 선을 그린다 (설정한 곳으로 경로가 이어지며 점이 이동한다고 생각해보자)
ctx.lineTo(150, 50);
// 지금까지 이어둔 경로를 입맛에 맞춰 fill 혹은 stroke
ctx.fill();
ctx.stroke();
스타일 지정하기
1. 색상 지정
// 색상 지정
fillStyle = "color";
strokeStyle = "color";
// color에는 색상 이름, rgb, rgba, #코드 등이 들어갈 수 있음
2. 선 두께 설정
// 선 두께 설정
// 설정을 이런식으로 변경하면 그 이후 선은 계속 똑같은 두께가 나오므로 주의한다.
ctx.lineWidth = number;
3. 선 모양 설정
// 기본값은 네모 뾰족하게 나온다 ...
ctx.lineCap = "round" // 끝이 둥글게 표현
// round, butt, square
🤓 기능 구현
0. 마우스를 누르고 있는가?
마우스를 꾹~ 누르고 있는지를 판별한다. (true)
마우스를 누르고 있다면 움직이는대로 그릴 것이고, 그렇지 않다면 계속해서 그리기 시작하는 좌표가 달라진다.
let isPainting = false;
function startPainting() {
isPainting = true;
}
function cancelPainting() {
isPainting = false;
}
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);
mousedown
포인터가 요소 내부에 있을 때, 포인팅 장치 버튼을 눌렀을 때 mousedown 이벤트가 발생한다.
mouseup
버튼을 누른 상태에서 요소 내부에 있는 상태에서 해제되었을 때 이벤트가 발생한다.
💥 주의
요소 외부에서 mouseup 이 되면 어떻게 될까?
결론부터 말하자면, mouseup 이벤트가 일어나지 않는다. 요소 외부로 나갔기 때문이다.
때문에 요소 외부로 나갔을 때의 이벤트, 함수를 설정해주어야 한다.
// 포인터가 요소를 떠났을 때도 mouseup 때 실행되는 함수를 담는다.
canvas.addEventListener("mouseleave", cancelPainting);
mouseleave 는 말그대로 포인터가 요소의 밖으로 이동했을 때를 뜻한다.
1. 브러쉬 이동, 그리기
- 마우스를 누르지 않았을 때 : 브러쉬를 이동시키기만 한다.
- 마우스를 눌렀을 때 : 누른 좌표에서부터 움직이는 쪽으로 선을 긋는다.
function onMove(event) {
// 마우스를 누른 상태
if(isPainting) {
ctx.lineTo(event.offsetX, event.offsetY);
ctx.stroke();
return;
}
// 마우스를 눌렀다 뗐을 때 그 다음에 그릴 선의 경로를 새로 만들어 둔다.
ctx.beginPath();
// 마우스를 누르지 않고 이동만 하는 상태
ctx.moveTo(event.offsetX, event.offsetY);
}
2. 브러쉬 두께 설정
<input type="range" id="line-width" min="1" max="10" value="5" step="0.1" />
range 타입의 input. 최저는 1 최대는 10 기본값은 5. 0.1씩 증가한다. 라는 정보가 담겨있다.
function onLineWidthChange(event) {
ctx.lineWidth = event.target.value;
}
lineWidth.addEventListener("change", onLineWidthChange);
3. 브러쉬 컬러 설정
function onColorClick(event) {
const colorValue = event.target.dataset.color;
ctx.strokeStyle = colorValue;
ctx.fillStyle = colorValue;
color.value = colorValue;
}
4. 채우기
0~1에서 처럼 채우기 버튼을 만들고, 채우기 버튼이 클릭되어 있을 때(true) 캔버스를 클릭하면 캔버스 크기만큼 fillRect 해준다.
5. 캔버스 모두 지우기
두 가지 방법이 있다.
첫 번째, 캔버스크기만큼 white로 채우는 방법
두 번째, celarRect()를 사용하는 방법
첫 번째의 경우 4번을 참고해서 만들면 된다.
두 번째의 경우는 기록해둔다.
function allClear() {
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
6. 지우개
5번의 첫 번째 방법에서 사용했던 것처럼 하얀색으로 덧칠해 지운다고 생각한다.
function onEraserClick() {
ctx.strokeStyle = "white";
}
선의 색상을 하얀색으로 바꿔주기만 하면 된다. 간단!
7. 이미지 가져오기
기본적으로 브라우저는 나의 파일들을 자기 맘대로 볼 수 없다.
내가 파일을 업로드해야만 브라우저의 자바스크립트에게 보이게 된다.
function onFileChange(event) {
const file = event.target.files[0];
const url = URL.createObjectURL(file); // url 요청
const image = new Image(); // = <img src = "" />
image.src = url;
image.onload = function () {
ctx.drawImage(image, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
fileInput.value = null; // 다른 이미지를 추가하고 싶을지도 모르므로!!
};
}
fileInput.addEventListener("change", onFileChange);
8. 텍스트 추가
캔버스를 더블클릭했을 때, 그 좌표에 input에 적었던 텍스트가 찍힌다.
function onDoubleClick(event) {
const text = textInput.value;
if (text !== "") {
ctx.save(); // 현재 상태, 색상, 스타일 등 모든 것 저장
ctx.lineWidth = 1;
ctx.font = "48px serif"; // size, font-family 설정 가능
ctx.fillText(text, event.offsetX, event.offsetY); // 클릭한 좌표
// fillText, strokText 두 가지 버전이 있다.
// 선 굵기 이전으로 되돌리기
ctx.restore(); //* save() ~ restore() 사이에는 어떤 수정이 이루어져도 저장되지 않는다.
}
}
canvas.addEventListener("dblclick", onDoubleClick);
save() ~ restore()
현재 상태, 색상, 스타일 등 모든 것을 저장한다.
이 사이에 어떤 수정이 이루어져도 저장되지 않는다.
9. 이미지 저장하기
캔버스를 이미지로 저장한다.
function onSaveClick() {
const url = canvas.toDataURL(); // 이미지를 텍스트로 인코딩 해준다.
const a = document.createElement("a");
a.href = url;
a.download = "myDrawing.png";
a.click();
}
saveBtn.addEventListener("click", onSaveClick);