2021. 12. 1. 00:40ㆍNomadCoders/바닐라 JS로 그림판 만들기
# 학습 전 나의 상태
11월 마지막 날 오랜만에 블로그 작성 중이다.
노마드 코더에서 2개의 챌린지를 진행하느라 약간 정신이 없어 블로깅을 하지 못했다.
코코아 클론 챌린지는.. 실패했다... 중간에 과제 1번 미제출.. 마지막 졸업 작품.. gitgub pages 를 만들지 못해.. 미제출..ㅜㅜ
그렇지만 바닐라 JS는 성공했다. 허접하지만.. 나름 열심히 했다.. (흑..ㅠㅠ)
오늘은 바닐라 JS와 Canvas를 공부해보고 싶어 그림판 만들기 강의를 보기로 했다.
# 오늘의 학습 내용
1. Canvas Event
- mousemove 이벤트를 적용하고 함수를 만들어 Canvas 안에서 event가 발생했을 시 console.log를 찍어보았다.
- client 값은 윈도우 전체의 범위 내에서 마우스 위치값을 나타내는데
- 나는 여기서 Canvas 내 좌표가 있는 offset 값이 필요하다.
- offset을 적용한 코드이다.
const canvas = document.getElementById('jsCanvas'); const onMouseMove = (e) => { const x = e.offsetX; const y = e.offsetY; console.log(x, y); } if (canvas) { canvas.addEventListener("mousemove", onMouseMove) }
- console.log(x, y)를 한 결과는 아래와 같다.
- Canvas Event 코드
const canvas = document.getElementById('jsCanvas'); // 기본적으로 painting은 false 이다. let painting = false; const stopPainting = () => { painting = false; } const onMouseMove = (e) => { const x = e.offsetX; const y = e.offsetY; } // 마우스를 클릭했을 때 painting은 true가 된다. const onMouseDown = (e) => { painting = true; } // 마우스 클릭을 땠을 때 다시 false가 된다. const onMouseUp = (e) => { stopPainting() } if (canvas) { canvas.addEventListener("mousemove", onMouseMove) canvas.addEventListener("mousedown", onMouseDown) canvas.addEventListener("mouseup", onMouseUp) canvas.addEventListener("mouseleave", stopPainting) }
2. Context
- canvas는 HTML에 의한 요소인데 다른 점은 context를 갖는 다는 것이다.
- Context는 canvas 요소 안에 픽셀에 접근할 수 있는 방법
- stokeStyle이란?
- 도형 주위의 선에 사용할 색상 또는 스타일
- lineWidth는 input:range 즉, 라인의 굵기
- context의 paths는 기본 선, 선의 시작점을 의미한다.
- context 코드
const canvas = document.getElementById('jsCanvas'); const ctx = canvas.getContext('2d'); // canvas에게 width와 height를 알려준다. canvas.width = 500; canvas.height = 400; // 선의 색 ctx.strokeStyle = "#2c2c2c"; // 선의 너비 ctx.lineWidth = 2.5; // 기본적으로 painting은 false 이다. let painting = false; const stopPainting = () => { painting = false; } const startPainting = () => { painting = true; } const onMouseMove = (e) => { const x = e.offsetX; const y = e.offsetY; if (!painting) { ctx.beginPath(); // 움직는 동안 path가 만들어진다. ctx.moveTo(x, y); } else { // path의 이전 위치에서 지금 현재 위치까지 선을 이어준다. ctx.lineTo(x, y); // 현재의 sub-path를 현재의 stroke style로 획을 그음 ctx.stroke(); } } if (canvas) { canvas.addEventListener("mousemove", onMouseMove) canvas.addEventListener("mousedown", startPainting) canvas.addEventListener("mouseup", stopPainting) canvas.addEventListener("mouseleave", stopPainting) }
3. changing color
- index.html 에 있는 컬러들의 클래스네임에 jsColor 추가
<div class="controls__colors" id="jsColors"> <div class="controls__color jsColor" style="background-color: #000"></div> <div class="controls__color jsColor" style="background-color: #fff"></div> <div class="controls__color jsColor" style="background-color: #ff3b30"></div> <div class="controls__color jsColor" style="background-color: #FF9500"></div> <div class="controls__color jsColor" style="background-color: #FFCC00"></div> <div class="controls__color jsColor" style="background-color: #4CD963"></div> <div class="controls__color jsColor" style="background-color: #5AC8FA"></div> <div class="controls__color jsColor" style="background-color: #0579ff"></div> <div class="controls__color jsColor" style="background-color: #5856D6"></div> </div>
- app.js에서 jsColor를 불러오고, console.log(colors) 찍어보기
- 이것을 Array로 담아보자
- Array를 forEach로 color를 돌린다. 그리고 addEvevtListner("click", hadleColorClick) 함수를 호출한다.
- hadleColorClick 함수에서 event 발생시 style.backgroundColor를 console로 확인해보니 rgb 값들이 나온다.
- 이 값을 color라는 변수에 담아주고 윗줄에 ctx.strokeStyle에 color를 할당해주면 색이 클릭했을 때 해당 색으로 변경된다.
const colors = document.getElementsByClassName('jsColor'); const handleColorClick = (e) => { const color = e.target.style.backgroundColor; ctx.strokeStyle = color; } // Array로 만들고 forEach로 color를 돌려서 addEventListener("click", handleColorClick)를 호출 Array.from(colors).forEach(color => color.addEventListener("click", handleColorClick));
4. Range와 Mode
- 먼저 아래 사진의 range가 늘어나고 줄어듬에 따라 브로쉬의 사이즈가 변경이 되어야 한다.
- 이벤트를 호출함 함수를 만들어준다.
- 이벤트 발생시 찾아야 할 값은 event.target 안에 value 값이다.
- 그래서 event.target.value를 변수에 담아주고 ctx.lineWidth에 담아준다.
const range = document.getElementById('jsRange'); const handleRangeChange = (e) => { console.log(e) const size = e.target.value; ctx.lineWidth = size; } if (range) { range.addEventListener("input", handleRangeChange); }
- Mode도 역시 먼저 이벤트 함수 호출을 해준다.
- 그리고 전역에 let filling = false; 라는 변수를 만들어준다.
- mode를 변경하는 함수에 filling이 false이면 false와 버튼에 FILL이 보이도록하고
- filling이 true면 버튼에 PAINT가 나오도록 해준다.
const mode = document.getElementById('jsMode'); let filling = false; const handleModeClick = () => { if (filling === true) { filling = false; mode.innerText = "Fill" } else { filling = true; mode.innerText = "Paint" } } if (mode) { mode.addEventListener('click', handleModeClick); }
- 이제 FILL버튼을 누르고 색상을 선택하여 canvas에 클릭하면 전체 색상이 변경 되도록 해보자
- fillRect()를 사용해 전체 width, height 값만큼 색을 입힐 수 있다.
- fillstyle()은 색상을 변경해준다.
const INITIAL_COLOR = "#2c2c2c"; canvas.width = 500; canvas.height = 400; ctx.fillStyle = INITIAL_COLOR; const handleCanvasClick = () => { if (filling) { ctx.fillRect(0, 0, canvas.width, canvas.height); } }
5. 이미지로 저장하기
- Canvas는 pixel을 다루고 기본적으로 image를 만든다.
- 그래서 다운로드나 저장은 이미 내장되어 있다.
- 이미지로 저장하기 위해 canvas.toDataURL('image/png'); 를 입력한다.
- document.createElement로 a 태그를 만들어주고 클릭시 다운로드 되도록 한다.
const save = document.getElementById('jsSave'); // 우클릭으로 저장하기 방지 const handleCM = (e) => { e.preventDefault(); } const handleSaveClick = () => { const image = canvas.toDataURL('image/png'); const link = document.createElement('a'); link.href = image; link.download = 'PaintJS'; link.click(); } if (canvas) { canvas.addEventListener("contextmenu", handleCM); // 우클릭 방지 } if (save) { save.addEventListener('click', handleSaveClick); }
참고자료
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
CanvasRenderingContext2D - Web APIs | MDN
The CanvasRenderingContext2D interface, part of the Canvas API, provides the 2D rendering context for the drawing surface of a <canvas> element. It is used for drawing shapes, text, images, and other objects.
developer.mozilla.org
https://developer.mozilla.org/ko/docs/Web/API/Canvas_API
Canvas API - Web API | MDN
Canvas API는 JavaScript와 HTML <canvas> 엘리먼트를 통해 그래픽을 그리기위한 수단을 제공합니다. 무엇보다도 애니메이션, 게임 그래픽, 데이터 시각화, 사진 조작 및 실시간 비디오 처리를 위해 사용
developer.mozilla.org
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
HTMLCanvasElement.toDataURL() - Web APIs | MDN
The HTMLCanvasElement.toDataURL() method returns a data URI containing a representation of the image in the format specified by the type parameter (defaults to PNG). The returned image is in a resolution of 96 dpi.
developer.mozilla.org
# 학습을 마치며
평소 Canvas가 궁금했다. 왜냐하면 파이널 프로젝트때 그림일기 만드는 것에서 동기분이 canvas를 활용했기 때문이다.
그런데 이렇게 좋은 강의가 있어서 canvas 기본에 대해 배울 수 있었던 것 같다.
이걸 어떻게 써먹을지.. 고민 좀 해봐야겠다.
그리고 내일부터는 React를 다시 공부하려고 한다.
한동안.. 바닐라JS만 했더니.. React가 가물..ㅜ 다시 학습하고 담주부터 챌린지에 참여할 것이다.
마지막이라는 각오로.. React와 JS를 정복하고 싶다..