JavaScript with 노마드 코더 그림판 만들기

woo ki·2023년 1월 13일

JavaScript

목록 보기
1/9

새로운 언어를 배워보자

웹개발(프론트엔드, 백엔드), 앱개발, 데이터 분석, 게임 개발, 보안 등 너무 많은 종류의 개발이 있다. 그리고 또 너무 다양한 언어들이 있다. 파이썬으로 많은 종류의 개발이 가능하지만 그래도 하나의 언어로는 불편한 점이 많다. 그리고 지식은 다다익선이라고 생각한다. 뭔든지 배우면 나쁘지 않다고 생각하기 떄문에 프론트엔드와 백엔드에 관심이 생겨 제일 기초적인 자바스크립트와 css, html을 강좌를 찾다가 지인의 추천으로 무료의 좋은 강의를 추천 받았다. 노마더 코더라는 사이트인데 유튜브도 하면서 개발 강의를 무료와 유로를 나눠서 강의를 한다. 그리고 여기 사이트가 좋은점이 챌린지라는 제도인데 챌린지를 통해서 매일 일정량의 강의를 듣고 미션을 클리어 해야 통과하는 제도라서 습관을 만들어 주는 아주 좋은 제도이다. 그래서 나는 제일 기초적인 자바스크립트로 모두가 알고 있는 그림판을 만드는 강의를 시청했다.

그림판 만들기라 아주 뭐가 쉬울것 같지만 처음은 모든지 어렵다고 생각하여 천천히 강의를 들으면서 만들었다.
이 강의가 좋은게 무작정 클론코딩처럼 따라하지 않고 개념을 설명하고 하나씩 적용하면서 따라가는게 기본적인 자바스크립트의 개념숙지가 되어 좋은 것 같다. 챌린지를 하면 코드챌린지를 해서 더욱 몰입해서 하나의 개념을 알아갈 수 있지만 그림 앱은 너무 기초적인 내용이라 챌린지가 없는것 같다.
JavaScript 거의 모든 웹을 만드는 기본이라고 생각한다. 또 이 친구와 단짝 친구인 html과 css도 같이 공부해야한다.

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>GOSU 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>
      <input type="file" accept="image/*" id="file" />
      <input type="text" id="text" placeholder="텍스트를 입력하세요!"/>
      <button id="save">🔥Save image</button>
    </div>
    <script src="app.js"></script>
  </body>
</html>

css 그림 앱 코드

@import "reset.css";

body {
    display: flex;
    gap: 20px;
    justify-content: space-between;
    align-items: flex-start;
    background-color: gainsboro;
    padding: 20px;
}

canvas {
    width:800px;
    height:800px;
    background-color: white;
    border-radius: 10px;
}
body {
    display: flex;
    justify-content: center;
    align-items: center;
}
.btns {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.color-options{
    display: flex;
    flex-direction: column;
    gap:12px;
}

.color-option {
    width: 45px;
    height: 45px;
    border-radius: 50%;
    cursor: pointer;
    border: 5px solid white;
    transition: transform ease-in-out .3s;
}

.color-option:hover {
    transform: scale(1.5);
}

input#color {
    background-color: white;
    border-radius: 50%;
}

button {
    all:unset;
    padding: 10px 0px;
    text-align: center;
    background-color: burlywood;
    color:white;
    font-weight: 500;
    cursor: pointer;
    border-radius: 10px;
    transition: opacity linear .1s;
}

button:hover {
    opacity: 0.85;
}

input#text {
    all:unset;
    padding: 10px 0px;
    border-radius: 10px;
    font-weight: 500;
    text-align: center;
    background-color: white;
}

js 그림 앱 코드

const saveBtn = document.getElementById("save");
const textInput = document.getElementById("text");
const fileInput = document.getElementById("file");
const eraserBtn = document.getElementById("eraser-btn");
const destroyBtn = document.getElementById("destroy-btn");
const modeBtn = document.getElementById("mode-btn");
const colorOptions = Array.from(
  document.getElementsByClassName("color-option")
);
const color = document.getElementById("color");
const lineWidth = document.getElementById("line-width");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
ctx.lineWidth = lineWidth.value;
ctx.lineCap = "round";
let isPainting = false;
let isFilling = false;
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_WIDTH, CANVAS_HEIGHT);
  }
}
function onDestroyClick() {
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
function onEraserClick() {
  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_WIDTH, CANVAS_HEIGHT);
    fileInput.value = null;
  };
}
function onDoubleClick(event) {
  const text = textInput.value;
  if (text !== "") {
    ctx.save();
    ctx.lineWidth = 1;
    ctx.font = "68px sans-serif";
    ctx.fillText(text, event.offsetX, event.offsetY);
    ctx.restore();
  }
}

function onSaveClick() {
  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);
eraserBtn.addEventListener("click", onEraserClick);
fileInput.addEventListener("change", onFileChange);
saveBtn.addEventListener("click", onSaveClick);

이렇게 세명의 친구가 협동하여 하나의 웹 페이지를 만든다. css친구는 전체적인 물감이라고 폰트나 사이즈를 설정하고 html친구는 우리에게 보여주는 그림같은 친구이다 실체가 있는 바로 눈에 보인다. js는 오장육부같이 인간의 핵심적인 행동을 제어하는 시스템이라고 생각하면 된다.

앱 실행 모습


전체적인 기능은 일단 그림을 그리고 색깔 변경, 전체 색깔 채우기, 지우개 기능, 사진 추가해서 수정하기, 텍스트를 입력하여 더블클릭하면 텍스트의 문자입력, 마지막으로 저장하기까지 구현을 했습니다. 처음으로 자바스크립트를 사용해서 만들었는데 뭐가 남는게 있어 좋은 경험이였습니다. 다음에는 바닐라 JS챌린지를 성공해서 돌아오겠습니다. 인증 수료증으로 마무리하겠습니다.

profile
안녕하세요. 깃허브 주소는 : https://github.com/gosuuk 입니다.

0개의 댓글