그림판만들기

jini.choi·2022년 5월 18일
0

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>paintJS</title>
    <link rel="stylesheet" href="./styles.css">
</head>
<body>
    <!-- canvas - 픽셀들을 다루는 요소 -->
    <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">
        </div>
        <div class="controls__btns">
            <button id="jsMode">Fill</button>
            <button id="jsSave">Save</button>
            <button id="jsClear">Reset</button>
        </div>
        <div class="controls__colors" id="jsColors">
            <div class="controls__color jsColor" style="background:#2c2c2c"></div>
            <div class="controls__color jsColor" style="background:#ffffff"></div>
            <div class="controls__color jsColor" style="background:#FF3B30"></div>
            <div class="controls__color jsColor" style="background:#ff9500"></div>
            <div class="controls__color jsColor" style="background:#FFCC00"></div>
            <div class="controls__color jsColor" style="background:#4CD963"></div>
            <div class="controls__color jsColor" style="background:#5AC8FA"></div>
            <div class="controls__color jsColor" style="background:#0579FF"></div>
            <div class="controls__color jsColor" style="background:#5856D6"></div>
        </div>
    </div>

    <script src="./app.js"></script>
</body>
</html>

CSS

@import "reset.css";

body{
    background: #f6f9fc;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 50px 0;
}

.canvas{
    width: 700px;
    height: 700px;
    background: #fff;
    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;
    display: flex;
    flex-direction: column;
    align-items: center;
}

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

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

.controls__btns button{
    /* 기본스타일 초기화 */
    all: unset; 
    cursor: pointer;
    background: #fff;
    padding: 5px 0px;
    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: 2px solid rgba(0, 0, 0, .2);
    color: rgba(0, 0, 0, .7);
    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;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
    border-radius: 100% ;
    cursor: pointer;
    margin: 2px;
}

.controls__colors .controls__color:active{
    transform: scale(0.98);
}

JS

const canvas = document.querySelector("#jsCanvas");
const ctx = canvas.getContext('2d');
//Context는 canvas 안에서 픽셀들을 컨트롤하는 역할
const colors = document.getElementsByClassName("jsColor")
const range = document.getElementById("jsRange");
const mode = document.getElementById("jsMode");
const save = document.getElementById("jsSave");
const clear = document.getElementById("jsClear");

const INITIAL_COLOR = "2c2c2c";
const CANVAS_SIZE = 700;

// 픽셀을 다루는 윈도우의 크기를 입력
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;

//처음 기본배경 설정(투명으로 할거면 삭제)
ctx.fillStyle = "#fff";
//처음 기본배경 설정(투명으로 할거면 삭제)
ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE); 

ctx.strokeStyle = INITIAL_COLOR; // 선들의 기본 색을 지정
ctx.fillStyle = INITIAL_COLOR;
ctx.lineWidth = 2.5; //선 너비 지정 px 

let painting = false;
let filling = false;

function stopPainting(){
    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); //path를 만들면 마우스의 xy좌표로 path를 옮김
        /** 마우스를 움직이는 모든 순간에  path를 만드는 것임 
         * 그렇기에 클릭했을때 마우스가 있는 지점부터 생성이 가능한 것
         * (path의 시작점은 내 마우스가 있는 곳) */
    }else{ //이건 마우스를 움직이는 내내 발생한다.
        ctx.lineTo(x, y); //path의 이 전 위치에서 지금 위치까지 선을 만듬
        ctx.stroke();//현재의 sub-path를 현재의 stroke style로 획을 그음
    }
}


function handleColorClick(event){
    const bgColor = event.target.style.background;
    ctx.strokeStyle = bgColor;
    ctx.fillStyle = bgColor;
}

function handleRangeChange(event){
    const brushSize = event.target.value;
    ctx.lineWidth = brushSize;
}

function handleModeClick(){
    if(filling === true){
        filling = false;
        mode.innerText = "Fill"
    } else {
        filling = true;
        mode.innerText = "Paint"
    }
}

function handleCanvasClick(){
    if(filling){
        //width와 height에 의해서 결정된 사이즈로 (x, y)위치에 색칠된 사각형을 그림
        //(x, y, width, height)
        ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE); 
    }
}

function handleResetClick() {
    ctx.clearRect(0,0,canvas.width, canvas.height);
}

function handleRightClick(event){
    event.preventDefault()
}

// event는 애드이벤터에서 함수를 사용할때만 입력

function handleSaveClick(){
    const image = canvas.toDataURL("image/png");//png가 기본이라확장자 안적어도됨
    const link = document.createElement("a");
    link.href = image;
    link.download = "PaintJS[EXPORT]";
    link.click();
}

if(canvas){
    canvas.addEventListener("mousemove", onMouseMove); 
    canvas.addEventListener("mousedown", startPainting);
    canvas.addEventListener("mouseup", stopPainting); //클릭을 때면 painting = false;
    canvas.addEventListener("mouseleave", stopPainting); //cavase에서 마우스가 벗어나면 painting = false;
    canvas.addEventListener("click", handleCanvasClick);//캔버스 색채우기
    canvas.addEventListener("contextmenu", handleRightClick);//우클릭방지
    
}

/**
 * 0. painting = false;
 * 1. 클릭전 mousemove의 if문 실행
 * 2. 클릭(mousedown)하면  startPainting이 실행되면서 
 * 3. painting = true;가 되면서 mousemove의 조건문은 false가 되면서  lineTo, stroke실행되고 선이 그어짐
 * 
 */


    Array.from(colors).forEach(color => 
    color.addEventListener("click",handleColorClick)
);


if(range){
    range.addEventListener("input", handleRangeChange);
}

if(mode){
    mode.addEventListener("click", handleModeClick)
}
if(clear) {
    clear.addEventListener("click", handleResetClick);
}
if(save){
    save.addEventListener("click", handleSaveClick)
}

결과


이 글은 패스트캠퍼스 노마드코더 '바닐라 JS로 그림판 만들기'을 수강하며 정리한 노트입니다.
https://nomadcoders.co/javascript-for-beginners-2/lobby

profile
개발짜🏃‍♀️

0개의 댓글