참고: https://dev.to/javascriptacademy/create-a-drawing-app-using-javascript-and-canvas-2an1
이 튜토리얼에서는 브라우저에서 간단히 그림을 그릴 수 있도록 하는 스크립트를 만들 것입니다. 이를 위해 기본 javascript와 canvas API를 사용할 것입니다. 이 튜토리얼을 마치고 나면 canvas API와 javascript의 이벤트 처리에 대한 대략적인 흐름을 알게 될 것입니다.
HTML markup
전체 앱을 section으로 래핑하고 class 이름을 container로 해서 시작하겠습니다. 이는 툴바와 드로잉 보드를 정렬하는데 사용합니다.
이 안쪽에 툴바를 위치시킬 div를 넣고 javascript 작업을 수월하게 하기위해 이 element의 id는 toolbar로 설정 합니다.
툴바 안에는 두개의 input 필드를 넣을 건데 하나는 색상 지정을 위해, 다른 하나는 선의 굵기를 지정하도록 할 것입니다. 색상 input은 선의 색을 지정하는 것 이므로 id를 stroke로 하고, 두번째 input에는 숫자 값을 받을 것이고 id는 lineWidth라고 지정합니다. 잊지말고 각각의 input에 해당하는 레이블을 달아줍니다. 마지막으로 id가 clear인 버튼을 추가할 것인데 이것은 드로잉 보드를 깨끗이 지우는 기능을 넣을 것입니다.
다음으로 우리 앱의 실질적인 그림을 그릴 부분인 드로잉 보드를 추가합니다. 이는 canvas element 이고 레이아웃을 잡기위해 div로 래핑합니다.
마지막으로 body 마지막 부분에 script 태그를 추가해야합니다.
<section class="container">
<div id="toolbar">
<label for="stroke">Stroke</label>
<input id="stroke" name='stroke' type="color">
<label for="lineWidth">Line Width</label>
<input id="lineWidth" name='lineWidth' type="number" value="5">
<button id="clear">Clear</button>
</div>
<div class="drawing-board">
<canvas id="drawing-board"></canvas>
</div>
</section>
<script src="./index.js"></script>
CSS 스타일 추가
먼저 브라우저 기본 스타일로 정의된 padding과 margin을 제거하고 시작하겠습니다. 또한 body의 높이는 100%로 설정하고 overflow: hidden으로 스크롤바를 안보이게 만듭니다.
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
color: white;
}
container도 높이를 100%로 설정하고 flexbox를 사용하도록 설정 합니다.
.container {
height: 100%;
display: flex;
}
툴바도 flexbox를 쓰지만 세로정렬을 하게 합니다. 툴바의 가로길이는 그냥 100px로 잡고 다른 부분과 차이나 보이도록 배경을 검정으로 설정합니다. 또 각 툴바 항목에 기본적인 스타일링을 적용해줍니다.
#toolbar {
display: flex;
flex-direction: column;
width: 100px;
background-color: black;
}
#toolbar * {
margin-bottom: 5px;
}
#toolbar input {
width: 100%;
}
javascript 구현
먼저 툴바와 드로잉 보드(canvas)를 참조할 변수를 지정합니다.
const canvas = document.getElementById('drawing-board');
const toolbar = document.getElementById('toolbar');
다음으로 canvas의 context를 가져와야 하는데 이 context를 사용해 canvas에 그림을 그릴 것입니다. 이를 위해 canvas의 getContext 메소드를 호출해 context를 얻고 그냥 평면에 그림그릴 것이기 때문에 매개변수로 2D를 넘겨줍니다.
const ctx = canvas.getContext('2d');
다음 단계에서는 오프셋(canvas의 왼쪽 위 좌표와 브라우저 화면의 왼쪽 위 사이의 거리)을 가져올 것입니다. 이 예제의 경우 canvas가 높이 100% 이기 때문에 top 오프셋은 0px이 되고 left 오프셋은 화면상에 툴바 옆에 있기 때문에 100px가 됩니다. 다음으로 canvas의 가로, 세로 길이를 계산해 설정합니다. 계산 방법은 브라우저 화면의 가로, 세로 길이에서 canvas의 오프셋을 빼면 얻을 수 있습니다.
const canvasOffsetX = canvas.offsetLeft;
const canvasOffsetY = canvas.offsetTop;
canvas.width = window.innerWidth - canvasOffsetX;
canvas.height = window.innerHeight - canvasOffsetY;
이제 전역 변수를 설정해야합니다. isPainting 변수는 현재 사용자가 그림을 그리고 있는 상태인지 아닌지에 대한 정보를 가지게 하고 기본 선의 굵기는 5px로 지정합니다.
let isPainting = false;
let lineWidth = 5;
이제 이벤트 리스너를 추가할 시간입니다. 먼저 툴바에 클릭 이벤트 리스너를 추가해야합니다. 만약 이벤트의 event.target.id가 clear이면(사용자가 clear 버튼을 클릭한 경우) clearRect 매소드에 매개변수로 canvas의 가로, 세로 길이를 넘겨서 canvas를 깨끗이 지우도록 작성하겠습니다. 이 메소드의 기본적인 기능은 매개변수로 받은 가로, 세로 크기에 해당하는 canvas의 모든 픽셀을 지우는 것입니다. 이 튜토리얼의 경우 canvas의 모든 영역이 됩니다.
toolbar.addEventListener('click', (event) => {
if (event.target.id === 'clear') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
다음으로 색상과 선굵기 input의 값이 변할 때를 제어해야합니다. 여기서는 각각의 input에 이벤트 리스너를 추가하는게 아닌 통합으로 작성해보겠습니다. 이를 위해 부모 element에 이벤트 리스너를 달아 이벤트를 제어하도록 할 것입니다. 각각의 input 필드를 구분하기위해 event.target을 체크하도록 하겠습니다. 만약 색상이 바뀐다면 canvas context의 strokeStyle을 설정하고 선의 굵기가 바뀐다면 전역변수 lineWidth에 새 값을 설정할 것입니다.
toolbar.addEventListener('change', (event) => {
if (event.target.id === 'stroke') {
ctx.strokeStyle = event.target.value;
}
if (event.target.id === 'lineWidth') {
lineWidth = event.target.value;
}
});
다음으로 드로잉 컨트롤을 구현하겠습니다. mousedown 이벤트가 발생하면(사용자가 마우스 버튼을 클릭 후 손가락을 떼지 않고 잡고있을 때) isPainting 변수를 true로 설정하고 context의 beginPath() 메소드를 사용하여 선그리기 시작 설정을 하고 moveTo() 메소드로 선을 그릴 시작점 설정을 합니다. 이 상태에서 사용자가 마우스 버튼을 놓으면 isPainting을 false로 바꿔줍니다.
canvas.addEventListener('mousedown', (event) => {
isPainting = true;
ctx.beginPath();
ctx.moveTo(event.clientX - canvasOffsetX, event.clientY);
});
canvas.addEventListener('mouseup', (event) => {
isPainting = false;
});
canvas에 마우스 포인트가 이동할 때 이벤트를 제어하기 위해 mousemove 이벤트 리스너를 추가해줍니다. 여기서는 먼저 isPainting 상태인지 아닌지 판단먼저 합니다. 그리는 상태가 아닌데 canvas위의 포인트가 바뀐다면 간단히 return으로 그림그리기 처리를 안하도록 합니다. 이제 그리기 상태라면 선의 굵기를 먼저 설정합니다. 이 값은 전역변수에 저장되도록 해놨으므로 이 값을 사용하여 선의 굵기를 설정하고 lineCap은 둥굴게 설정합니다. 이 다음 lineTo() 메소드를 이용해 그림을 그릴 현재 마우스 좌표를 입력합니다. 여기서 주의할 점은 왼쪽에 툴바가 있으므로 마우스 좌표에서 오프셋을 뺀 값을 캔버스에 그리도록 해야한다는 점입니다. 마지막으로 stroke() 메소드를 호출해 지정된 색과 굵기로 canvas에 선을 그립니다.
canvas.addEventListener('mousemove', (event) => {
if (!isPainting) {
return;
}
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.lineTo(event.clientX - canvasOffsetX, event.clientY);
ctx.stroke();
});
이로서 간단한 그림판 앱이 완성됐습니다. 아래에 전체 코드를 첨부합니다.
index.html
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Drawing app</title>
</head>
<body>
<section class="container">
<div id="toolbar">
<label for="stroke">Stroke</label>
<input id="stroke" name='stroke' type="color">
<label for="lineWidth">Line Width</label>
<input id="lineWidth" name='lineWidth' type="number" value="5">
<button id="clear">Clear</button>
</div>
<div class="drawing-board">
<canvas id="drawing-board"></canvas>
</div>
</section>
<script src="./index.js"></script>
</body>
</html>
styles.css
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
color: white;
}
.container {
height: 100%;
display: flex;
}
#toolbar {
display: flex;
flex-direction: column;
width: 100px;
background-color: black;
}
#toolbar * {
margin-bottom: 5px;
}
#toolbar input {
width: 100%;
}
index.js
const canvas = document.getElementById('drawing-board');
const toolbar = document.getElementById('toolbar');
const ctx = canvas.getContext('2d');
const canvasOffsetX = canvas.offsetLeft;
const canvasOffsetY = canvas.offsetTop;
canvas.width = window.innerWidth - canvasOffsetX;
canvas.height = window.innerHeight - canvasOffsetY;
let isPainting = false;
let lineWidth = 5;
toolbar.addEventListener('click', (event) => {
if (event.target.id === 'clear') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
toolbar.addEventListener('change', (event) => {
if (event.target.id === 'stroke') {
ctx.strokeStyle = event.target.value;
}
if (event.target.id === 'lineWidth') {
lineWidth = event.target.value;
}
});
canvas.addEventListener('mousedown', (event) => {
isPainting = true;
ctx.beginPath();
ctx.moveTo(event.clientX - canvasOffsetX, event.clientY);
});
canvas.addEventListener('mouseup', (event) => {
isPainting = false;
});
canvas.addEventListener('mousemove', (event) => {
if (!isPainting) {
return;
}
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.lineTo(event.clientX - canvasOffsetX, event.clientY);
ctx.stroke();
});
관련 글
'dev > javascript' 카테고리의 다른 글
[javascript] 자바스크립트 classList 사용 방법 (1) | 2022.04.27 |
---|---|
[javascript] 자바스크립트 Promise 사용 방법 (0) | 2022.04.26 |
[javascript] 자바스크립트 createElement 사용방법 (0) | 2022.04.21 |
[javascript] 자바스크립트 setInterval 사용방법 (0) | 2022.04.21 |
[javascript] 자바스크립트 setTimeout 사용방법 (0) | 2022.04.20 |
댓글