🍠 바닐라JS로 그림 앱 만들기 : 노마드 코더 클론 코딩 🍠
2023. 7. 27. 00:32ㆍ개발/🍠 바닐라JS 노마드 코더 클론코딩
const saveBtn = document.getElementById("save"); //id:save인 버튼 불러오기
const textInput = document.getElementById("text"); //id:text인 요소(text input) 불러오기
const fileInput = document.getElementById("file"); //id:file인 요소(file input) 불러오기
const eraseBtn = document.getElementById("eraser-btn"); //id:eraser-btn인 버튼 불러오기
const destroyBtn = document.getElementById("destroy-btn");id:destroy-btn인 버튼 불러오기
const modeBtn = document.getElementById("mode-btn");id:mode-btn인 버튼 불러오기
//색 옵션(class : color-option) 배열로 받기
const colorOptions = Array.from(
document.getElementsByClassName("color-option")
);
const color = document.getElementById("color"); //id:color인 요소(색 input) 불러오기
const lineWidth = document.getElementById("line-width"); //id:line-width인 요소(선 굵기 input) 불러오기
const canvas = document.querySelector("canvas"); //html에서 canvas 불러오기
const ctx = canvas.getContext("2d"); //canvas상의 드로잉 컨텍스트(type:2d로 지정) 불러오기
//canvas 크기 상수
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;
//canvas 크기 지정
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
ctx.lineWidth = lineWidth.value; //ctx의 디폴트 선 굵기 지정
ctx.lineCap = "round"; //ctx의 선 끝 둥글게 만들기
let isPainting = false; //그림을 그리고 있는지
let isFilling = false; //색 채우기 모드인지
//마우스 따라서 선 그리기
function onMove(event){
#지금 그림을 그리고 있다면
if(isPainting){
ctx.lineTo(event.offsetX, event.offsetY); //마우스의 위치 따라서 line 따라가기
ctx.stroke(); //따라간 line의 색 채우기
return;
}
#지금 그림을 그리고 있지 않다면 -> 붓의 위치만 이동시키기
ctx.moveTo(event.offsetX, event.offsetY);
}
#그림 그린다!
function startPainting(event){
isPainting = true;
}
#그림 안 그린다!
function cancelPainting(event){
isPainting = false;
ctx.beginPath();
}
#선 굵기 변경
function onLineWidthChange(event){
ctx.lineWidth = event.target.value; #굵기 변경 슬라이더의 값 따라서 선 굵기 지정
}
#선 색깔 바꾸기
function onColorChange(event){
#선택한 값 따라서 선 색깔 지정
ctx.strokeStyle = event.target.value; #선 색
ctx.fillStyle = event.target.value; #배경 색
}
#색 옵션 바꾸기
function onColorClick(event){
const colorValue = event.target.dataset.color; #클릭한 옵션의 data값(색 넘버) 저장
ctx.strokeStyle = colorValue; #선 색
ctx.fillStyle = colorValue; #배경 색
}
#배경 채우기 / 선 그리기 모드 선택
function onModeClick(event){
#버튼 눌렀을 때
#현재 isFilling : true 상태면
if(isFilling){
isFilling = false; #isFilling : false로 바꾸고
modeBtn.innerText = "Fill"; #버튼 텍스트 Fill로 바꾸기
}
#현재 isFilling : false 상태면
else{
isFilling = true; #isFilling : true로 바꾸고
modeBtn.innerText = "Draw"; #버튼 텍스트 Draw로 바꾸기
}
}
#배경 색 바꾸기
function onCanvasClick(event){
if(isFilling){
ctx.fillRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT); #배경 크기 맞춰서 색 바꾸기
}
}
#초기화
function onDestroyClick(event){
ctx.fillStyle = "white"; #배경 색 white로 지정
ctx.fillRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT); #지정된 색(white)으로 배경 채우기
}
#지우개
function onEraserClick(event){
ctx.strokeStyle = "white"; #선 색 white로 지정
isFilling = false; #isFilling : false
modeBtn.innerText = "Fill"; #모드 변경 버튼 텍스트 : Fill
}
#사진 파일 삽입
function onFileChange(event){
const file = event.target.files[0]; #파일 상수에 저장
const url = URL.createObjectURL(file); #파일 url(브라우저에만 존재) 상수에 저장
const image = new Image(); #image 생성
image.src = url; #image 출처 <= 파일 url
#image onload 하면 함수 발동
image.onload = function(){
ctx.drawImage(image, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); #배경에 사진 삽입
fileInput.value = null;
};
}
#더블클릭으로 텍스트 삽입
function onDoubleClick(event){
const text = textInput.value; #사용자가 input한 텍스트 상수에 저장
if (text !==""){
ctx.save(); #기존 ctx(그림 선)의 정보 저장
ctx.lineWidth = 1; #ctx(현재는 텍스트)의 선 굵기 변경
ctx.font = "68px, serif"; #ctx(현재는 텍스트)의 폰트 변경
ctx.strokeText(text, event.offsetX, event.offsetY); #더블클릭한 위치에 텍스트 삽입
ctx.restore(); #ctx.save() 때 저장한 정보 다시 불러오기
}
}
#그림 파일 컴퓨터에 저장
function onSaveClick(event){
const url = canvas.toDataURL();
const a = document.createElement("a");
a.href = url;
a.download = "myDrawing.png";
a.click();
}
canvas.addEventListener("dblclick", onDoubleClick)
canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown",startPainting);
canvas.addEventListener("mouseup",cancelPainting);
canvas.addEventListener("mouseleave", cancelPainting);
canvas.addEventListener("click", onCanvasClick)
lineWidth.addEventListener("change", onLineWidthChange);
color.addEventListener("change", onColorChange);
colorOptions.forEach((color) => color.addEventListener("click", onColorClick));
modeBtn.addEventListener("click", onModeClick);
destroyBtn.addEventListener("click", onDestroyClick);
eraseBtn.addEventListener("click", onEraserClick);
saveBtn.addEventListener("click", onSaveClick);
fileInput.addEventListener("change", onFileChange);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width= , initial-scale=1.0">
<title>Meme Maker</title>
<link rel = "stylesheet" href="styles.css" />
</head>
<body>
<div class="color-options">
<input type="color" id="color" />
<div
class="color-option"
style="background-color: #1abc9c;"
data-color="#1abc9c"></div>
<div
class="color-option"
style="background-color: #3498db;"
data-color="#3498db"></div>
<div
class="color-option"
style="background-color: #34495e;"
data-color="#34495e"></div>
<div
class="color-option"
style="background-color: #27ae60;"
data-color="#27ae60"></div>
<div
class="color-option"
style="background-color: #8e44ad;"
data-color="#8e44ad"></div>
<div
class="color-option"
style="background-color: #f1c40f;"
data-color="#f1c40f"></div>
<div
class="color-option"
style="background-color: #e74c3c;"
data-color="#e74c3c"></div>
<div
class="color-option"
style="background-color: #95a5a6;"
data-color="#95a5a6"></div>
<div
class="color-option"
style="background-color: #d35400;"
data-color="#d35400"></div>
<div
class="color-option"
style="background-color: #bdc3c7;"
data-color="#bdc3c7"></div>
<div
class="color-option"
style="background-color: #2ecc71;"
data-color="#2ecc71"></div>
<div
class="color-option"
style="background-color: #e67e22;"
data-color="#e67e22"></div>
</div>
<canvas></canvas>
<div class = "btns">
<input
id="line-width"
type="range"
min="1"
max="10"
value="5"
step="0.1"
/>
<button id ="mode-btn">🩸</button>
<button id ="destroy-btn">💣</button>
<button id ="eraser-btn">❌</button>
<label for="file">
</label>
💅🏻 Add Photo
<input type="file" accept="image/*" id="file"
/></label>
<input type="text" id="text" placeholder="Add text here... :)" />
<button id="save">🖼 Save image</button>
</div>
<script src="app.js"></script>
</body>
</html>
@import "reset.css";
body {
display: flex;
gap:20px;
justify-content: space-between;
align-items: flex-start;
background-color: #f6f9fc;
padding:20px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
canvas{
width: 800px;
height: 800px;
border: 5px solid black;
border:5px solid black;
background-color: white;
border-radius:10px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
body {
display: flex;
justify-content: center;
align-items: center;
}
.btns {
display: flex;
flex-direction: column;
gap:20px;
}
.color-options {
display: flex;
flex-direction: column;
gap:20px;
align-items: center;
}
.color-option {
width:50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
border:2px solid white;
transition:transform ease-in-out .1s;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.color-option:hover {
transform: scale(1.2);
}
input#color {
background-color: white;
}
button,
label {
all:unset;
padding:10px 0px;
text-align: center;
background-color:royalblue;
color:white;
font-weight: 500;
cursor: pointer;
border-radius: 10px;
transition: opacity linear .1s;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
button:hover {
opacity: 0.85;
}
input#file {
display: none;
}
input#text {
all:unset;
padding:10px 0px;
border-radius: 10px;
font-weight: 500;
text-align: center;
background-color:white;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
Code Challenge는 패스....
다음에 해커톤할 때 잘 응용해봐야겠다!!
'개발 > 🍠 바닐라JS 노마드 코더 클론코딩' 카테고리의 다른 글
🍠 바닐라 JS로 크롬 앱 만들기 : 노마드 코더 클론 코딩 강의 🍠 (0) | 2023.07.10 |
---|