본문 바로가기
  • 컴공생의 공부 일기
  • 공부보단 일기에 가까운 것 같은
  • 블로그
🖥️ Frontend/클론코딩하기

👩🏽‍🦱니꼬쌤 클론코딩 :🍨바닐라 JS로 그림 앱 만들기🍨

by 정람지 2023. 7. 27.

🍦바닐라 JS로 그림 앱 만들기🍦

 

바닐라 JS로 그림 앱 만들기 – 노마드 코더 Nomad Coders

HTML, CSS, JS

nomadcoders.co


🍦완강🍦


🔳사각형 그리기 🔳


- 사각형 선 그리기: ctx.rect(x, y, w, h);
(선의 색이 적용되지 않아서 보이지 않음)


-> 다음 줄에 ctx.stroke()  : 테두리 그리기

-> ctx.fill()  : 채우기

fillrect


- 끊어가기를 원하는 곳 맨 앞에 ctx.beginPath(); 추가해 새 경로 만들기


- moveTo(x, y); -> 브러쉬의 좌표를 움직여줌
- lineTo(x, y) -> 라인을 그려줌


🍦코드🍦

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-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: #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">🩸 Fill</button>
       <button id="destroy-btn">💣 Destroy</button>
       <button id="eraser-btn">❌ Erase</button>
       <label for="file">
         💅🏻 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="js/app.js"></script>
   </body>
 </html>

app.js

const canvas = document.querySelector("canvas");
const lineWidth = document.getElementById("line-width");
const color = document.getElementById("color");
const colorOptions = Array.from( //팔레트
    document.getElementsByClassName("color-option")
  );
const modeBtn = document.getElementById("mode-btn");
const DestroyBtn = document.getElementById("destroy-btn");
const EraseBtn = document.getElementById("eraser-btn");
const fileInput = document.getElementById("file");
const TextInput = document.getElementById("text");
const saveBtn = document.getElementById("save");

//캔버스에 쓰기 위해 필요
const ctx = canvas.getContext("2d")
canvas.width = 800;
canvas.height = 800;
ctx.lineCap = "round";
ctx.lineWidth = lineWidth.value;
let isPainting = false; // 선 그리기용 변수
let isFilling = false; // 채우기 모드용 변수

//캔버스상수
const CANVAS_W = 800;
const CANVAS_H = 800;

//선 그리기
function onMove(event){
    if (isPainting){
        ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
    }
    ctx.moveTo(event.offsetX,event.offsetY);
}

//마우스 누르는 동안 선이 그려지게 함
function startPainting(){
    isPainting = true;
}

// 마우스가 떼어지면 선이 안 그려지게 함
function cancelPainting(){
    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;
    ctx.strokeStyle = colorValue;
    ctx.fillStyle = colorValue;
    color.value = colorValue;
}
// 채우기 모드로 바꾸기(버튼)
function onModeClick(){
    if (isFilling){
        isFilling = false
        modeBtn.innerText = "🩸 Fill"
    }else{
        isFilling = true
        modeBtn.innerText = "Draw"
    }
}
// 캔버스 채우기
function onCanvasClick() {
    if (isFilling){
        ctx.fillRect(0,0,CANVAS_W,CANVAS_H);
    }
}
// 캠버스 초기화하기 (하얀색칠하기)
function DestroyClick() {
    ctx.save(); //현재 ctx의 모든 정보 저장
    ctx.fillStyle = "white";
    ctx.fillRect(0,0,CANVAS_W,CANVAS_H);
    ctx.restore();
}
//지우개 (하얀 선)
function EraseClick(){
    ctx.strokeStyle = "white";
    isFilling = false; // 채우기 모드일 때 오류 방지
    modeBtn.innerText = "Fill";
}
// 밈 메이커
function onFileChange(event){
    const file = event.target.files[0];
    const url = URL.createObjectURL(file);
    const image = new Image();
    image.src = url;
    image.onload = function(){
        ctx.drawImage(image,0,0,CANVAS_W,CANVAS_H );
        fileInput.value = null;
    }
}
function onDoubleClick(event){
    const text = TextInput.value;
    console.log(text);
    if (text !== ""){
        ctx.save(); //현재 ctx의 모든 정보 저장
        ctx.lineWidth = 1;
        ctx.font = "68px serif";
        ctx.fillText(text,event.offsetX, event.offsetY);
        ctx.restore(); //저장했던 ctx로 다시 돌아감
    }
}

function onSaveClick() {
    const url = canvas.toDataURL();
    const a = document.createElement("a");
    a.href = url;
    a.download = "myDrawing.png";
    a.click();
}

canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);
canvas.addEventListener("mouseleave", cancelPainting);
canvas.addEventListener("click", onCanvasClick);
canvas.addEventListener("dblclick", onDoubleClick); // 글자

lineWidth.addEventListener("change", onLineWidthChange);

color.addEventListener("change", oncolorChange);
colorOptions.forEach((color) => color.addEventListener("click", onColorClick));

modeBtn.addEventListener("click", onModeClick);

DestroyBtn.addEventListener("click", DestroyClick);
EraseBtn.addEventListener("click", EraseClick);

fileInput.addEventListener("change", onFileChange);

saveBtn.addEventListener("click", onSaveClick);

reset.css

 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;
 }

styles.css

@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;
     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);
 }

자꾸 제대로 안 됐었는데

저기 링크로 이동 했더니 app.js가 없다고 떠서 새로 만들기 했더니

이렇게 오른쪽이 새로 만들어졌고 저기 코드는 제대로 작동되었다.

왜그러지??????

 

으악 바보야

경로 문제엿다

js 파일 밑에다 넣어놓고 경로 설정을 저렇게 안 했었다 으악

 

역시 언제나 컴퓨터가 맞고 내가 틀리다