강의를 보며 HTML + CSS + JavaScript로 그림판을 만들어 보았다.
1) Canvas
API
<canvas>
엘리먼트를 통해 그래픽을 그리기위한 수단을 제공한다. 주로 2D그래픽에 중점을 두고 있으며, 애니메이션, 게임 그래픽, 데이터 시각화, 사진 조작 및 실시간 비디오 처리를 위해 사용된다.2) box-shadow
속성
,
로 구분해서 여러 그림자 효과를 입힐 수 있다. 박스 그림자는 요소에서의 수평수직 거리(오프셋), 흐릿함과 확산 정도, 색상으로 이루어진다./* offset-x | offset-y | blur-radius | spread-radius | color */
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
3) all: unset
4) range
input
태그의 type
속성값 중 하나이다.<input type=“range”>
는 슬라이드 바를 조정하여 범위 안의 숫자를 선택할 수 있는 입력 필드를 정의한다.range
비교적 정확하지 않은 값을 제어할 때 사용한다. (스크롤)<input>
요소의 최댓값을 명시한다.<input>
요소의 최솟값을 명시한다.<input>
요소에 입력할 수 있는 숫자들 사이의 간격을 명시한다.<input>
요소의 초깃값(기본값)을 명시한다.1) getElementById()
메서드
2) 마우스 이벤트
click
: element를 클릭했을 때(버튼을 눌렀다가 떼었을 때) 발생 한다.
mousedown
: element에서 마우스 버튼을 눌렀을 때 발생한다.
mouseup
: element에서 눌렀던 마우스 버튼을 떼었을 때 발생한다.
mousemove
: element에서 마우스를 움직였을 때 발생한다.
mouseover
: element 바깥에서 안으로 옮겼을 때 발생한다.
mouseout
: element 안에서 바깥으로 옮겼을 때 발생한다.
3) getContext
<canvas>
는 context
를 갖고 있는 HTML의 요소이고, context
는 이 요소 안에서 우리가 픽셀에 접근할 수 있는 방법이다. (내부의 픽셀들)context
가 가지고 있는 기능들 MDN_Contextfunction onMouseMove(event){
const x = event.offsetX;
const y = event.offsetY;
if(painting===false){ // if(!painting)
ctx.beginPath(); //경로 생성
ctx.moveTo(x, y); //선 시작 좌표
}
else{
ctx.lineTo(x, y); //선 끝 좌표
ctx.stroke(); //선 그리기
}
}
4) JS에서 cavas 사이즈 가져오기
// JS에서 픽셀을 다룰 캔버스사이즈 가져오기
// ex.1 ) JS에 명시한다.
canvas.width = 700;
canvas.height = 700;
// ex.2 ) JS에서 가져온다.
canvas.width = document.getElementsByClassName("canvas")[0].offsetWidth;
canvas.height = document.getElementsByClassName("canvas")[0].offsetHeight;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// ex.3 ) HTML에 지정한다.
<canvas id="jsCanvas" width="700" height="700">
5) 컬러변환
5-1 ) color 오버라이딩
const colors = document.getElementsByClassName("jsColor")
function handleColorClick(event){
const color = event.target.style.backgroundColor;
ctx.strokeStyle = color;
}
Array.from(colors).forEach(color => color.addEventListener("click", handleColorClick))
5-2 ) Array.from
메서드
Array.from()
메서드는 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운Array 객체를 만든다.5-3 ) Array.prototype.forEach()
메서드
forEach()
메서드는 주어진 함수를 배열 요소 각각에 대해 실행한다.5-4 ) 콜백함수 인자 명명
Array.from(colors).forEach
(color => color.addEventListener("click", handleColorClick))
여기서 color
는 다른이름으로 지정해도 상관없다. array안에 있는 각각의 아이템들을 대표하는 것이라고 보면 된다. (마치 div의 의미같이. div
: 특정 영역이나 구획을 정의할 때 사용)
📌 Point !
콜백함수로 전달되어지는 인자는 최대한 이해하기 쉽도록 쓰는것이 중요하다.
콜백함수에서 전달되어지는 인자를 value , item, element 와 같이 이름지어서 전달한다면, 협업을 하거나 긴 코드를 작성할 때 이러한 이름이 많아지면 가독성이 떨어질 수 있다. 때문에 'student, color'와 같이 의미있는 이름을 짓는 것이 중요하다! 😃
배열 메서드(2)
5-5 ) getElementsByClassName 와 querySelector
document.getElementsByClassName("jsColor");
Element의 메소드 getElementsByClassName()
는 주어진 클래스를 가진 모든 자식 엘리먼트의 실시간 HTMLCollection 을 반환한다.
Document의 메소드 getElementsByClassName()
는 도큐먼트 루트로부터 도큐먼트 전체를 탐색한다는 점을 제외하고는 위와 동일하게 작동한다.
querySelector
수 많은 컬러 div중 오직 제일 처음 div만 선택해서 결과적으로 color는 변하지 않는다. 이때 querySelector All
을 통해서 getElementsByClassName
와 같은 효과를 볼 수 있다.
이 부분은 프로젝트를 벗어나 다시 따로 모아서 정리를 해야겠다. 🤔 DOM요소선택하기
6) HTML의 <input type="range">
를 JS에서 활용하기
const range = document.getElementById("jsRange");
// input은 반응할 이벤트 유형
if(range){
range.addEventListener("input", handleRangeChange);
}
function handleRangeChange(event){
const size = event.target.value;
ctx.lineWidth = size;
}
input
) 의 이벤트가 발생했을 때, 콜백되는 함수를 정의한다.event.target.value
값을 변수size
에 할당한다.ctx.lineWidth = size;
를 통해 상위 디폴트 값을 오버라이딩 한다.lineWidth
는 선의 너비를 제어한다. (CanvasRenderingContext2D.lineWidth
)8) mode.innerText = "Fill or Paint"
// html▼
<button id="jsMode">Fill</button>
// Javascript▼
const mode = document.getElementById("jsMode");
if(mode){
mode.addEventListener("click", handleModeClick);
}
function handleModeClick(){
if(filling === true){
filling = false;
mode.innerText = "Fill"
} else {
filling = true;
mode.innerText = "paint"
}
}
element.innerText;
: 이 속성은 element 안의 text 값들만을 가져온다.
element.innerHTML;
: innerText와는 달리 innerHTML은 element 안의 HTML이나 XML을 가져온다. 참고
9) Event.preventDefault()
메서드
canvas.addEventListener("contextmenu", handleCM);
// (canvas에서 자체적으로 지원하는 우클릭 저장 방지 )
function handleCM(event){
event.preventDefault()
}
10) 세이브 만들기
toDataURL()
: HTML5, Canvas 에는 그려진 내용을 URL 문자열로 반환해 주는 함수가 제공된다. Canvas 객체의 toDataURL() 함수를 통해 캔버스에 그린 그림을 문자열 형태로 변환할 수 있다. 참고
✍️ Canvas 로 그린 내용을 문자열로 변환하여 img 요소의 src 로 사용하는 원리인 것 같다.
createElement
: 지정한 tagName의 HTML 요소를 만들어 반환한다.
download()
API
: URL 등의 파일을 다운로드한다.
// html▼
<button id="jsSave">Save</button>
// Javascript▼
const saveBtn = document.getElementById("jsSave");
if(saveBtn){
saveBtn.addEventListener("click", handleSaveClick)
}
function handleSaveClick(){
const image = canvas.toDataURL();
// jpeg를 원한다면 이렇게 설정한다. canvas.toDataURL("image/jpeg");
const link = document.createElement("a");
link.href = image;
link.download = "Hello😎";
link.click();
}
📌 Tip
+) num && console.log(num);
&&
연산자는 앞이 true 여야 뒤가 실행이된다. 앞이 false라면 뒷문장이 아예 실행이 되지 않는다. num에 값이 '있다면' num을 활용한 뒷문장을 사용할 수 있을 때 쓴다.
const num = 9;
if (num) {
console.log('true!');
}
// 9
num && console.log(num);
// 9
//두개가 같은 의미이다.
✍️ 시행착오&변경
color 가져오기)
color를 css 스타일에서 각각 background로 부여하고 JS에서 컬러 동작 함수를 정의하고 싶었는데 getElementsByClassName을 통해서 가져오면 style이 가져와 지지 않았다. 그래서 queryselectorall로도 정의해봤지만 처음에 nodelist에 빈 배열이 담겨서 당황했고.. 알고 봤더니 HTML요소 명 앞에 .
을 붙이지 않아서였다. 😓 그런데 Nodelist를 확인해봤더니 여기에도 배경 속성이 오지 않았다. 결국 구글링을해서 getComputedStyle
키워드, 그리고 관련된 Element.style
키워드를 얻었다. 컬러를 각각 개별적으로 (위치등) 관리하기 위해서 시도해본 것이었는데, 사실 css에서 margin 속성으로 충분히 해결가능한 부분이다. 그리고 키워드에 대해서 알아보았다.Element_Style 가져오기
filling을 적용할 때 )
1. 클릭을 한 상태로 드래그를 하면 페인팅 선이보이고, 마우스를 up 하고 나서야 filling이 적용 되는 문제를 해결해야 했다. 먼저, mousedown인 상태가 filling 모드에서 작동하지 않게 하고 싶었다. mousedown인 상태는 즉 startpainting 함수가 호출된다는 것이고, 이 함수는 painting이 true(마우스 동작 관련 함수)로 이루어진다. 결국 filling 모드가 true 일때는 이것을 비활성화 시키면 되었다.
2. 그런데 위와 같이 하면 작동이 잘 되는 듯 보이지만, 클릭을 하고 있는 상태(드래그) 에서는 스트로크가 보이지 않을 뿐, filling모드가 아닌 painting모드였다. 그래서 두개의 작동 스위치는 결국 mousedown에 있으므로, canvas.addEventListener("mousedown", handleCanvasClick);
으로 수정 했다. 😃
Reset 버튼 추가하기)
같은 방식으로 리셋버튼을 추가해서 JS로 버튼을 작동시켰다. 사용한 기능은 CanvasRenderingContext2D.clearRect()
메소드이다. 기존에 정의한 캔버스 사이즈만 매개변수로 전달했는데 작동이 안되어서, x축과 y축 좌표값 0을 같이 지정했더니 잘 작동되었다 :)
+) 컬러버튼 동작효과 추가하기
버튼을 눌렀을 때 트랜스폼 스케일+트랜지션을 통해 선택효과를 시각적으로도 전달할 수 있도록 추가하였다.
이쁘게 잘 만드셨네요ㅎㅎ 잘 보고 갑니다