티스토리 뷰
포토샵 같은 그림판?
말 그대로다. 별도의 레이어로 관리 될 수 있는 캔버스 그림판을 만들어보자.
일단 포토샵은 도형을 실제로 그리거나 할 때 해당 도형이 실제로 어떻게 나올지 미리 보여주고, 마우스를 놓을 때 실제 도형이 나타나게 된다.
소스코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#canvas {
border: 1px solid #EEE;
}
.options {
display: flex;
align-items: center;
}
</style>
</head>
<body>
<canvas id="canvas" width="800" height="400"></canvas>
<div class="options">
<select id="type">
<option value="stroke">실선</option>
<option value="square">사각형</option>
<option value="eraser">지우개</option>
</select>
<select id="strokeStyle">
<option value="blue">파란색</option>
<option value="red">빨간색</option>
<option value="pink">분홍색</option>
<option value="orange">주황색</option>
</select>
<select id="lineWidth">
<option value="5">5px</option>
<option value="10">10px</option>
<option value="15">15px</option>
<option value="20">20px</option>
</select>
</div>
<script>
let isAbleDraw = false;
const options = {
type: 'stroke',
strokeStyle: 'blue',
lineWidth: 5,
};
const rects = [];
let currentRect = null;
document.getElementById('canvas').addEventListener('mousedown', () => {
isAbleDraw = true;
currentRect = {
type: options.type,
strokeStyle: options.strokeStyle,
lineWidth: options.lineWidth,
coordinates: [],
};
});
document.getElementById('canvas').addEventListener('mousemove', (e) => {
if (isAbleDraw) {
const ctx = e.target.getContext('2d');
const [x, y] = [e.offsetX, e.offsetY];
currentRect.coordinates.push([x, y]);
drawTools.clear();
drawTools.execute(rects);
if (currentRect.type === 'stroke') drawTools.stroke(currentRect.coordinates, 'rgba(255, 255, 0, .3)', currentRect.lineWidth);
if (currentRect.type === 'eraser') drawTools.eraser(currentRect.coordinates, currentRect.lineWidth);
if (currentRect.type === 'square') drawTools.square(currentRect.coordinates, 'rgba(255, 255, 0, .3)');
}
});
document.getElementById('canvas').addEventListener('mouseup', () => {
isAbleDraw = false;
rects.push(currentRect);
drawTools.clear();
currentRect = null;
drawTools.execute(rects);
console.log(rects);
})
const drawTools = {
clear() {
// 캔버스 내용 제거
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
stroke(coordinates, color, lineWidth) {
// 마우스가 이동한 경로를 따라 실선 그리기
if (coordinates.length > 0) {
const ctx = document.getElementById('canvas').getContext('2d');
const firstCoordinate = coordinates[0];
ctx.beginPath();
ctx.moveTo(firstCoordinate[0], firstCoordinate[1]);
for (let i = 1; i < coordinates.length; i += 1) {
ctx.lineTo(coordinates[i][0], coordinates[i][1]);
}
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.stroke();
ctx.closePath();
}
},
eraser(coordinates, lineWidth) {
// 마우스가 이동한 좌표에 따라 하얀색으로 원을 그려서 지우개 기능처럼 동작
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
for (let i = 0; i < coordinates.length; i += 1) {
ctx.beginPath();
const coordinate = coordinates[i];
const [x, y] = coordinate;
ctx.fillStyle = 'white';
ctx.arc(x, y, lineWidth / 2, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
},
execute(rects) {
// rects 배열에 저장 된 도형을 기준으로 다시 캔버스에 그림
for (let i = 0; i < rects.length; i += 1) {
const rect = rects[i];
const { type } = rect;
if (type === 'stroke') this.stroke(rect.coordinates, rect.strokeStyle, rect.lineWidth);
if (type === 'eraser') this.eraser(rect.coordinates, rect.lineWidth);
if (type === 'square') this.square(rect.coordinates, rect.strokeStyle);
}
},
square(coordinates, color) {
// 사각 도형을 그림
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const start = coordinates[0];
const end = coordinates[coordinates.length - 1];
const [startX, startY] = start;
const [endX, endY] = [end[0] - startX, end[1] - startY];
ctx.beginPath();
ctx.fillStyle = color;
ctx.fillRect(startX, startY, endX, endY);
ctx.closePath();
},
};
document.getElementById('type').addEventListener('change', (e) => {
options.type = e.target.value;
});
document.getElementById('strokeStyle').addEventListener('change', (e) => {
options.strokeStyle = e.target.value;
});
document.getElementById('lineWidth').addEventListener('change', (e) => {
options.lineWidth = e.target.value;
});
</script>
</body>
</html>
원리
원리는 간단하다.
캔버스를 누른 뒤, 마우스를 움직이게 되면 마우스가 이동한 경로를 currentRect의 coordinates 배열에 저장한다.
coordinates에 저장 된 좌표 값을 바탕으로 현재 그리는 도형이 무엇인지에 따라 각기 다른 방식으로 캔버스에 그림을 그린다.
그렇게 그려진 도형은 currentRect 변수를 통해 rects 배열에 저장 되며, 추후에 캔버스를 지운 뒤에 다시 그림을 복구 해야 할 일이 있는 경우, rects배열을 통해 다시 복원 시킬 수 있다.
LIST
'프로그래밍 > Javascript' 카테고리의 다른 글
[Javascript] jQuery Ajax 실습 자료 (0) | 2020.05.25 |
---|---|
[Javascript] 가벼운 저장소, LocalStorage (0) | 2019.04.10 |
[Javascript] atan2를 이용한 두 점 사이 각도 구하기 (0) | 2019.04.10 |
[Javascript] PHP의 number_format을 구현하자 (1) | 2019.04.10 |
[Javascript] 초성 검색 간략화 (1) | 2019.04.10 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 정보처리 산업기능요원
- 전국기능경기대회
- 대학생 산업기능요원
- 기능경기대회
- 초성
- React-Native
- React Native
- 산업기능요원
- 검색
- kakaocdn
- 2021년 산업기능요원
- 대학생 현역 산업기능요원
- eslint
- jest
- 현역 산업기능요원
- IT산업기능요원
- 캔버스 그림판 javascript
- NUXT
- 2021년 산업기능요원 재배정
- 초성검색
- 기능대회
- JavaScript
- 2020정보처리산업기사
- 산업기능요원 재배정 확정
- 21년 산업기능요원
- 산업기능요원 현역
- 산업기능요원 폐지
- 정보처리산업기사 요약
- 산업기능요원 인센티브T.O
- 산업기능요원 재배정
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
글 보관함