Coding Archive

๋…ธ๋งˆ๋“œ ์ฝ”๋” - ๋ฐ”๋‹๋ผ JS๋กœ ๊ทธ๋ฆผํŒ ๋งŒ๋“ค๊ธฐ ๋ณธ๋ฌธ

๐Ÿ–ฅ๏ธ Clone Coding

๋…ธ๋งˆ๋“œ ์ฝ”๋” - ๋ฐ”๋‹๋ผ JS๋กœ ๊ทธ๋ฆผํŒ ๋งŒ๋“ค๊ธฐ

์ฝ”๋“ฑ์–ด 2022. 7. 22. 01:54

#1 SETUP + STYLES

 

0. Project Setup

github ๊ณ„์ •์— ๋“ค์–ด๊ฐ€์„œ ์ƒˆ๋กœ์šด repository๋ฅผ ๋งŒ๋“ ๋‹ค.

 

์ด๋•Œ initialize this repository with์—์„œ "Add a README file"์„ ์ฒดํฌ, gitignore์€ "node"๋„ฃ์–ด์ค€๋‹ค.

(Add .gitignore์—์„œ .gitignore template: node๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.)

 

์ €์žฅํ•  ๊ณต๊ฐ„์„ ํ„ฐ๋ฏธ๋„์—์„œ ์—ด์–ด์„œ git clone https://github.com/seoye0ng/paint-js(์ƒ์„ฑ๋œ repository ์ฃผ์†Œ)๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๊นƒ ํด๋ก ํ•˜๋ฉด ๋œ๋‹ค. → ๋‚ด๊ฐ€ ์ €์žฅํ•  ๊ณต๊ฐ„์— paint-jsํŒŒ์ผ์ด ์ƒ์„ฑ๋œ๋‹ค.

 

ํŒŒ์ผ์„ vsc์—์„œ ์—ด๊ณ  html, css, js๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

 

 

 

1. Styles part One

html

(canvas์™€ ํŒ”๋ ˆํŠธ ๋งŒ๋“ค๊ธฐ)

 

 <!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" />
    <link rel="stylesheet" href="styles.css" />
    <title>PaintJS</title>
  </head>
  <body>
    <canvas id="jsCanvas" class="canvas"></canvas>
    <div class="controls">
      <div class="controls__colors" id="jsColors">
        <div class="controls__color" style="background-color: #2c2c2c"></div>
        <div class="controls__color" style="background-color: white"></div>
        <div class="controls__color" style="background-color: #ff3b30"></div>
        <div class="controls__color" style="background-color: #ff9500"></div>
        <div class="controls__color" style="background-color: #ffcc00"></div>
        <div class="controls__color" style="background-color: #4cd963"></div>
        <div class="controls__color" style="background-color: #5ac8fa"></div>
        <div class="controls__color" style="background-color: #0579ff"></div>
        <div class="controls__color" style="background-color: #5856d6"></div>
        <!--๋ณด๊ธฐ์‰ฝ๊ฒŒ html์—์„œ background-color๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค-->
      </div>
    </div>
    <script src="app.js"></script>
  </body>
</html>

 

 

reset.cssํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•ด ๋ธŒ๋ผ์šฐ์ €์˜ ๋””ํดํŠธ ๊ฐ’์œผ๋กœ ์„ค์ •๋œ ๋””์ž์ธ์„ ๋ชจ๋‘ ์—†์• ์ค€๋‹ค.

 

 

css

 

@import "reset.css";

body {
  background-color: #f6f9fc;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 80px;
}

.canvas {
  width: 800px;
  height: 800px;
  background-color: white;
  border-radius: 3%;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}

.controls {
  margin-top: 80px;
}

.controls .controls__colors {
  display: flex;
}

.controls__colors .controls__color {
  width: 50px;
  height: 50px;
  border-radius: 100%;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
  cursor: pointer;
  margin: 0 5px;
}

 

 

 

2. Styles part Two

html

(๋ธŒ๋Ÿฌ์‰ฌ ๊ตต๊ธฐ ์กฐ์ ˆ ๋ฐ”์™€ ์ƒ‰ ์ฑ„์šฐ๊ธฐ, ์ €์žฅํ•˜๊ธฐ ๋ฒ„ํŠผ ๋งŒ๋“ค๊ธฐ)

 

<div class="controls">
      <div class="controls__range">
        <input
          type="range"
          id="jsRange"
          min="0.1"
          max="5"
          value="2.5"
          step="0.1"
        />
        <!--์ตœ์†Œ 0.1๊นŒ์ง€/ ์ตœ๋Œ€ 5๊นŒ์ง€/ ๊ธฐ๋ณธ๊ฐ’์€ 2.5/ ์กฐ์ ˆ์€ 0.1์”ฉ-->
      </div>
      <div class="controls__btns">
        <button id="jsMode">Fill</button>
        <button id="jsSave">Save</button>
      </div>

 

 

css

 

 .controls {
  margin-top: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.controls .controls__range {
  margin-bottom: 30px;
}

.controls__btns {
  margin-bottom: 30px;
}

.controls__btns button {
  all: unset;
  background-color: white;
  padding: 5px 0;
  width: 80px;
  text-align: center;
  border-radius: 5px;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
  border: 1.5px solid rgba(0, 0, 0, 0.2);
  color: rgba(0, 0, 0, 0.7);
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  cursor: pointer;
}

.controls__btns button:active {
  transform: scale(0.96);
}

 

 

 

 

 

 

 

 

 

#2 PAINTJS

 

0. Canvas Events

js

 

const canvas = document.getElementById(jsCanvas);

let painting = false;
//ํด๋ฆญํ•˜๊ธฐ ์ „์—๋Š” ํŽ˜์ธํŒ…๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— false๋กœ ์„ค์ •

function stopPainting() {
  painting = false;
}

function onMouseMove(event) {
  const x = event.offsetX;
  const y = event.offsetY;
}

function onMouseDown(event) {
  painting = true;
}
//onMouseDownํ–ˆ์„ ๊ฒฝ์šฐ์—๋งŒ painting์ด true๊ฐ€ ๋จ

function onMouseUp(event) {
  stopPainting();
}

if (canvas) {
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mousedown", onMouseDown); //mousedown์€ ๋งˆ์šฐ์Šค๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ๋ฅผ ์˜๋ฏธ
  canvas.addEventListener("mouseup", onMouseUp); //mouseup์€ ํด๋ฆญ์„ ๋–ผ์—ˆ์„ ๋•Œ๋ฅผ ์˜๋ฏธ
  canvas.addEventListener("mouseleave", stopPainting); //๋งˆ์šฐ์Šค๊ฐ€ canvas์—์„œ mouseleave๋˜๋ฉด painting์ด false๊ฐ€ ๋˜๋„๋ก ์„ค์ •
}

 

offset์€ ์บ”๋ฒ„์Šค ๋ถ€๋ถ„๊ณผ ๊ด€๋ จ ์žˆ๋Š” ๊ฐ’์ด๋‹ค.

 

clientX, Y๋Š” ์œˆ๋„์šฐ ์ „์ฒด์˜ ๋ฒ”์œ„ ๋‚ด์—์„œ ๋งˆ์šฐ์Šค ์œ„์น˜ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š”๋ฐ,

์—ฌ๊ธฐ์„œ๋Š” ์œˆ๋„์šฐ ์ „์ฒด ๋ฒ”์œ„ ๋‚ด์˜ ์ขŒํ‘œ๋ฅผ ์•Œ๊ณ  ์‹ถ์€ ๊ฒŒ ์•„๋‹ˆ๋ผ ์บ”๋ฒ„์Šค ๋‚ด์—์„œ์˜ ์ขŒํ‘œ๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค.

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— event ์•ˆ์˜ offsetX, Y๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

 

 

1. 2D Context

js

 

const canvas = document.getElementById(jsCanvas);
const ctx = canvas.getContext("2d");

ctx.strokeStyle = "#2c2c2c"; //์ฒซ ๋ฒˆ์งธ์ƒ‰(๊ฒ€์ •์ƒ‰)์œผ๋กœ ์‹œ์ž‘ํ•˜๋„๋ก ์„ค์ •
ctx.lineWidth = 2.5; //๋‘๊ป˜๋ฅผ 2.5๋กœ ์‹œ์ž‘ํ•˜๋„๋ก ์„ค์ •

let painting = false;

function stopPaninting() {
  painting = false;
}

function startPainting() {
  painting = true;
}

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 onMouseDown(event) {
  painting = true;
}

if (canvas) {
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mousedown", startPainting);
  canvas.addEventListener("mouseup", stopPaninting);
  canvas.addEventListener("mouseleave", stopPaninting);
}

canvas๋Š” context๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” html ์š”์†Œ์ด๋ฉฐ, context๋Š” canvas ์•ˆ์—์„œ ํ”ฝ์…€์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด๋‹ค.

 

const ctx = canvas.getContext('2d'); ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” context๋Š” ๋ชจ๋‘ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

 

ex)

ctx.lineWidth → context๋กœ ๋ผ์ธ ๋‘๊ป˜๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์Œ

ctx.strokeRect → ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Œ

ctx.fillRect → ์น ํ•ด์ง„ ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Œ

ctx.moveTo → context๋ฅผ ์–ด๋”˜๊ฐ€ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ์Œ

ctx.lineTo →๋‹ค๋ฅธ ๊ณณ์— ๋ผ์ธ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Œ

 

context๊ฐ€ ๊ฐ€์ง„ ๊ฒƒ ์ค‘์—” Path๋„ ์žˆ๊ณ , Path๋Š” ๊ธฐ๋ณธ์ ์ธ ์„ ์ด๋‹ค.

 

์šฐ๋ฆฌ๋Š” beginPath()๋ฅผ ์‚ฌ์šฉํ•ด Path๋กœ ์‹œ์ž‘ํ•˜๊ณ ,  moveTo()๋กœ Path๋ฅผ ์›€์ง์ด๊ณ , closePath()๋กœ Path๋ฅผ ๋‹ซ์„ ์ˆ˜ ์žˆ๋‹ค.

Comments