이미지를 우클릭하면 아래와 같이 저장이 가능하다.
canvas는 pixel을 다루는 거니까 기본적으로 image를 만든다.
그래서 download와 save파트는 이미 내장되어 있다.
하지만 아래와 같이 저장했을 때 버그가 있다.
canvas의 배경색이 transparent(투명)로 저장이 된다.
canvas의 배경색을 설정하지 않아서 실제 pixel manipulator canvas에
background를 설정하지 않았다. ( HTML 의 background만 설정한거다.)
그래서 canvas가 load 되자마자 설정되도록 해야된다.
fillStyle을 black으로 만들기 전에
이렇게 설정을 하면 기본적으로 canvas의 background는 default에 의해서 하얀 배경이 되는거다.
이러한 context menu를 원하지 않으면 addEventListener로 할 수 있다.
이제 우클릭을 해도 context menu가 뜨지 않는다.
이제 SAVE 버튼을 눌렀을 때 저장 할 수 있도록 설정해보자.
우리가 원하는건 그린 것들을 다 넣고, image 안에 이것들을 담아내는거다.
먼저 필요한게 canvas의 데이터를 image 처럼 얻는거다.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
HTMLCanvasElement.toDataURL() 메소드는 기본적으로 PNG로 설정된 type parameter에 의해
지정된 포맷의 이미지 표현을 포함한 data URL을 반환한다.
이건 변경할 수 있다. png가 될 수도 있고, jpeg도 될 수 있고 (image format이라면 뭐든지)
이제 링크를 만들거다.
download 속성은 browser에게 이 링크로 가는 대신 URL을 다운로드 하라고 지시하는거다.
그래서 link는 이미지를 갖게 된다.
이제 클릭을 가짜로 만들면 된다.
하지만 클릭해도 아무런 반응이 없다.
그 이유는 image가 href로 되어야 하고 download는 그 이름을 가져야 된다.
href는 image(URL)가 되어야 하고 download는 이름을 정해주면 된다.
사진을 저정하고 열어보니 JPEG의 퀄리티가 안좋아서 PNG로 바꾸겠다.
저 부분을 지워주면 default가 png니까 png가 될 거다.
png로 해도 픽셀이 조금 깨지는데 그건 700x700 사이즈의 image라 그렇다.
초고화질을 원하면 canvas를 고품질로 만들어야 된다.
<소스코드>
<html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./styles.css" />
<title>PaintJS</title>
</head>
<body>
<canvas id="jsCanvas" class="canvas"></canvas>
<div class="controls">
<div class="controls__range">
<!-- 페인트 브러쉬의 사이즈를 컨트롤 -->
<input
type="range"
id="jsRange"
min="0.1"
max="5.0"
value="2.5"
step="0.1"
/>
<!-- value는 기본값을 말하고 step은 0.1씩 이동 -->
</div>
<div class="controls__btns">
<button id="jsMode">Fill</button>
<button id="jsSave">Save</button>
</div>
<div class="controls__colors" id="jsColors">
<div
class="controls__color jsColor"
style="background-color: white;"
></div>
<div
class="controls__color jsColor"
style="background-color: #2c2c2c;"
></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>
</div>
<script src="./app.js"></script>
</body>
</html>
<css>
@import "reset.css";
body {
background-color: #f6f9fc;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 50px 0px; /* 밑부분의 잉여공간을 줄여줬다. */
}
.canvas {
width: 700px;
height: 700px;
background-color: white;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.controls {
margin-top: 50px; /* canvas와 controls의 거리를 두기위해 */
display: flex;
flex-direction: column;
align-items: center;
}
.controls .controls__btns {
margin-bottom: 30px;
}
.controls__btns button {
all: unset; /* 버튼에 기본적으로 적용된 스타일을 제거 */
cursor: pointer;
background-color: white;
padding: 5px 0px;
width: 80px;
text-align: center;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
border: 2px solid rgba(0, 0, 0, 0.2); /* 버튼에 회색 테두리 */
color: rgba(0, 0, 0, 0.6);
text-transform: uppercase; /* 전체 대문자로 */
font-weight: 800;
font-size: 12px;
}
.controls__btns button:active {
transform: scale(0.98); /* 요소를 확대 또는 축소 */
}
.controls .controls__colors {
display: flex; /* 일렬로 나열 */
}
.controls__colors .controls__color {
/* 각각의 색상 박스들 */
width: 50px;
height: 50px;
border-radius: 25px; /* 원형으로 만드려면 width의 반만큼 border-radius를 가지면 된다. */
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.controls .controls__range {
margin-bottom: 30px;
}
<js>
const canvas = document.getElementById("jsCanvas");
const ctx = canvas.getContext("2d");
const colors = document.getElementsByClassName("jsColor");
const range = document.getElementById("jsRange");
const mode = document.getElementById("jsMode");
const saveBtn = document.getElementById("jsSave");
const INITIAL_COLOR = "#2c2c2c";
const CANVAS_SIZE = 700;
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE; // css에서 설정한 캔버스 사이즈와 동일해야된다.
ctx.fillStyle = "white";
ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
ctx.strokeStyle = "INITIAL_COLOR";
ctx.fillStyle = "INITIAL_COLOR";
ctx.lineWidth = 2.5;
let painting = false; /* 기본값으로 false로 주고 클릭했을 때 true가 될거다. */
let filling = false; /* filling을 하고 있으면 그걸 나에게 말해줄 variable이 필요하다. */
function startPainting() {
painting = true;
}
function stopPainting() {
painting = false;
}
function onMouseMove(event) {
const x = event.offsetX;
const y = event.offsetY;
if (!painting) {
ctx.beginPath();
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
ctx.stroke();
}
}
function handleColorClick(event) {
const color = event.target.style.backgroundColor;
ctx.strokeStyle = color;
ctx.fillStyle = color;
}
function handleRangeChange(event) {
const size = event.target.value;
ctx.lineWidth = size;
}
function handleModeClick(event) {
if (filling === true) {
filling = false;
mode.innerText = "Fill";
} else {
filling = true;
mode.innerText = "Paint";
}
}
function handleCanvasClick() {
/* 코너에서부터 시작(0, 0)하고 캔버스보다 커야된다.*/
if (filling) {
ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
}
}
function handleCM(event) {
event.preventDefault();
}
function handleSaveClick() {
const image = canvas.toDataURL();
const link = document.createElement("a");
link.href = image;
link.download = "PaintJs[🎨]";
link.click();
}
if (canvas) {
canvas.addEventListener("mousemove", onMouseMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", stopPainting);
canvas.addEventListener("mouseleave", stopPainting);
canvas.addEventListener("click", handleCanvasClick);
canvas.addEventListener("contextmenu", handleCM);
}
Array.from(colors).forEach((color) =>
color.addEventListener("click", handleColorClick)
);
if (range) {
range.addEventListener("input", handleRangeChange);
}
if (mode) {
mode.addEventListener("click", handleModeClick);
}
if (saveBtn) {
saveBtn.addEventListener("click", handleSaveClick);
}
<실행화면>
※ 본 포스팅은 개인 공부 기록을 목적으로 남긴 글이며 본 사실과 다른 부분이 있다면 과감하게 지적 부탁드립니다.
'Javascript > Paint JS' 카테고리의 다른 글
[Vanilla-JS] 6. Filling Mode (0) | 2020.08.20 |
---|---|
[Vanilla-JS] 5. Changing Color / Brush Size (0) | 2020.08.17 |
[Vanilla-JS] 4. 2D Context (0) | 2020.08.17 |
[Vanilla-JS] 3. Canvas Events (0) | 2020.08.13 |
[Vanilla-JS] 2. Styles part (0) | 2020.08.10 |
댓글