기록하는 개발자

[VanillaJS] 그림판 만들기 본문

Web/Javascript

[VanillaJS] 그림판 만들기

밍맹030 2021. 8. 12. 20:58
728x90

- 주어진 팔레트 내에서 색상을 변경할 수 있다(default : black).

- 주어진 range 내에서 브러쉬 크기를 변경할 수 있다(default : 10). 

- fill 버튼을 누르면 버튼이 paint로 바뀌고, canvas를 클릭하면 배경색이 채워진다.

- paint로 바뀐 버튼을 누르면 다시 text가 fill로 바뀌고, canvas에 선을 그릴 수 있다.

- clear 버튼을 클릭하면 canvas가 초기화 된다.

 

<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">
    <link rel="stylesheet" href="css/style.css"/>
    <title>Let's Paint!🎨</title>
</head>
<body>
    <canvas id="jsCanvas" class="canvas"></canvas>
    <div class="controls">
        <div class="controls_range">
            <input type="range" id="jsRange" min="1" max="20" value="10" steps="1">
        </div>

        <div class="controls_btns">
            <button id="jsMode">Fill</button>
            <button id="jsClear">Clear</button>
        </div>

        <div class="controls_colors" if="jsColors">
            <div class="controls_color jsColor" style="background-color: black;"></div>
            <div class="controls_color jsColor" style="background-color: white;"></div>
            <div class="controls_color jsColor" style="background-color: rgb(219, 42, 42);"></div>
            <div class="controls_color jsColor" style="background-color: rgb(255, 123, 0);"></div>
            <div class="controls_color jsColor" style="background-color: rgb(255, 196, 0);"></div>            <div class="controls_color jsColor" style="background-color: rgb(81, 119, 64);"></div>
            <div class="controls_color jsColor" style="background-color: rgb(41, 56, 121);"></div>
            <div class="controls_color jsColor" style="background-color: rgb(87, 34, 122);"></div>
            <div class="controls_color jsColor" style="background-color: rgb(168, 81, 135);"></div>
        </div>
    </div>

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

<CSS>

@import "reset.css";
body{
    background-color: #f6f9fc;
    display: flex;
    flex-direction: column;
    align-items : center;
    padding-top : 30px;
}

.canvas{
    width : 500px;
    height : 500px;
    background-color: white;
    border-radius: 15px;
    box-shadow : 0 4px 6px rgba(53, 53, 53, 0.11), 0 1px 3px rgba(66, 66, 66, 0.88);
}

.controls_colors .controls_color{
    width : 50px;
    height : 50px;
    border-radius : 25px; 
    box-shadow : 0 4px 6px rgba(50,50,93,0.11), 0 1px 3px rgba(0, 0, 0, 0.88);
    margin-left : 5px;

}

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

.controls .controls_colors{
    display : flex;
}

.controls .controls_btns{
    margin-bottom : 20px;
}

.controls .controls_btns button{
    all :unset;
    cursor: pointer;
    background-color: white;
    padding : 5px 0px;
    width : 80px;
    text-align : center;
    border-radius : 10px;
    box-shadow : 0 4px 6px rgba(66, 66, 66, 0.11), 0 1px 3px rgba(87, 87, 87, 0.88);
    text-transform: uppercase;
    font-size: 12px;
    font-weight: 800;
}
.controls_btns button:active{
    transform : scale(0.98);
}

.controls .controls_range{
    margin-bottom : 20px;
}

<JS>

const canvas = document.getElementById("jsCanvas");
const context = canvas.getContext("2d");
const colors = document.getElementsByClassName("jsColor");
const range = document.getElementById("jsRange");
const mode = document.getElementById("jsMode");
const clear = document.getElementById("jsClear");

const INITIAL_COLOR ="#2C2C2C";
const CANVAS_SIZE = 500;

let painting = false;
let filling = false;

canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;

context.strokeStyle = INITIAL_COLOR;
context.fillStyle =INITIAL_COLOR;
context.lineWidth = 10;

function stopPainting(){
    painting=false;
}

function startPainting(){
    painting=true;
}

function onMouseMove(event){
    const x=event.offsetX;
    const y=event.offsetY;
    if(!painting) { 
        context.beginPath(); 
        context.moveTo(x,y);
    }
    else{ 
        context.lineTo(x,y); 
        context.stroke();
    }
}

function handleColorClick(event){
    const color = event.target.style.backgroundColor;
    context.strokeStyle = color;
    context.fillStyle = color;
}

function handleRangeChange(event){
    const lineSize = event.target.value;
    context.lineWidth = lineSize;
}

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

function clearCanvas(){
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
}

function handleCanvasClick(){
    if(filling)
        context.fillRect(0,0,CANVAS_SIZE,CANVAS_SIZE);
}

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

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

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

if(mode) mode.addEventListener("click", handleModeClick);

if(clear) clear.addEventListener("click", clearCanvas);

 

< 변수 선언 부 >

const canvas = document.getElementById("jsCanvas");
const context = canvas.getContext("2d");
const colors = document.getElementsByClassName("jsColor");
const range = document.getElementById("jsRange");
const mode = document.getElementById("jsMode");
const clear = document.getElementById("jsClear");

const INITIAL_COLOR ="#2C2C2C";
const CANVAS_SIZE = 500;

let painting = false;
let filling = false;

canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;

context.strokeStyle = INITIAL_COLOR;
context.fillStyle =INITIAL_COLOR;
context.lineWidth = 10;

canvas 태그 : html element로 다른 element와 다른 점으로는 context를 갖는다는 점이다.
context : element안에서 우리가 픽셀에 접근할 수 있는 방법

 

* pixel modifier : offsetX, offsetY 사용을 위해 canvas 크기 지정 해야한다.
    → css 에서 지정한 canvas 크기와는 다르다.

 

참고 : https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial

 

캔버스 튜토리얼 - Web API | MDN

는 HTML 요소 중 하나로서, 스크립트(보통은 자바스크립트)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은 복잡할 수도 있는)

developer.mozilla.org

 

< 함수 >

1. stopPainting, startPainting

function stopPainting(){
    painting=false;
}

function startPainting(){
    painting=true;
}

2. onMouseMove

function onMouseMove(event){
    const x=event.offsetX;
    const y=event.offsetY;
    if(!painting) { // 마우스 클릭 안한 경우
        context.beginPath(); 
        context.moveTo(x,y);
    }
    else{ 
        context.lineTo(x,y); 
        context.stroke();
    }
}

 - offsetX, offsetY : canvas 범위
 - clientX, clientY : 전체 screen 범위

 

1. mouse click을 하지 않았을 때

 - beginPath : starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path.(sub-paths list를 비우고 new path를 생성한다.)

 - moveTo : 펜을  x와 y 로 지정된 좌표로 옮긴다.

 

2. mouse가 canvas위에서 클릭 되어있는 경우

 - lineTo : path의 이전 위치부터 현 위치까지 line을 만든다.
 - stroke : 현재의 sub-path를 현재의 stroke style로 line을 긋는다.

 

 

3. handleColorClick

function handleColorClick(event){
    const color = event.target.style.backgroundColor;
    context.strokeStyle = color;
    context.fillStyle = color;
}

 - 배열에 저장된 element들을 클릭할 때 마다

    현재 함수 내의 color 변수에 해당 element의 backgroundColor가 저장된다.

- strokeStyle, fillStyle을 클릭한 color로 초기화한다.

 

 

4. handleRangeChange

function handleRangeChange(event){
    const lineSize = event.target.value;
    context.lineWidth = lineSize;
}

 input range를 변경할 때마다  현재 함수 내 lineSize 변수에 해당 element의 value 저장된다.*/

 

 

5. clearCanvas

function clearCanvas(){
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
}

 

6. handleCanvasClick

function handleCanvasClick(){
    if(filling)
        context.fillRect(0,0,CANVAS_SIZE,CANVAS_SIZE);
}

 

7. handleModeClick

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

 

< 함수 실행 부 >

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

//classname으로 'jsColor'를 가진 element들을 array로 묶고 이벤트를 추가한다.
Array.from(colors).forEach(colorItem => 
    colorItem.addEventListener("click", handleColorClick)
);

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

if(mode) mode.addEventListener("click", handleModeClick);

if(clear) clear.addEventListener("click", clearCanvas);
728x90