본문 바로가기
인강\개인공부/노마드 코더

바닐라 JS로 그림판 만들기

by 꼬바리 2021. 4. 14.

이번엔 바닐라JS로 그림판 만들기 수업을 들었습니다.

좀더 낯선 api 를 사용했으니

쉽고 재밌는 강의였습니다.

 

only JavaScript로 만든 그림판의 기능은 다음과 같습니다.

 

1. 캔버스에 마우스로 그리기 (8가지 색)

2. paint 뿐 만아니라 Fill 채우기 기능 

3. range로 브러쉬 두께 조절

4. 마우스 오른쪽 방지

5. sava 버튼으로 이미지 저장

 

 

그림판 디폴트 형태입니다.

아래 기능들로 색을 변경하거나 캔버스를 채우거나 브러쉬의 두께를 조절 할수있습니다.

 

 

 

첫번째와 두번째 선의 두께 차이가 있습니다.

 

 

 

 

SAVA버튼 클릭시 내가 그린 그림이 자동 저장 됩니다.

jpg파일보다 png파일이 픽셀이 덜 꺠져서

png파일로 설정했습니다.

 

 

 

FILL 과 PAINT 버튼이 스위치 됩니다.

 


코드 공유 하겠습니다.

 

 

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">
    <link rel="stylesheet" href="styles.css">
    <title>Paint JS</title>
</head>
<body>
    <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>
        </div>
        <div class="controls_colors" id="jsColors">
            <div class="controls_color jsColor" style="background-color: #2c2c2c;"></div>
            <div class="controls_color jsColor" style="background-color: white;"></div>
            <div class="controls_color jsColor" style="background-color: #FF3B30;"></div>
            <div class="controls_color jsColor" style="background-color: #ff9500;"></div>
            <div class="controls_color jsColor" style="background-color: #4cd963;"></div>
            <div class="controls_color jsColor" style="background-color: #5ac8fa;"></div>
            <div class="controls_color jsColor" style="background-color: #0579ff;"></div>
            <div class="controls_color jsColor" style="background-color: #5856d6;"></div>
            
        </div>
    </div>
    <script src="app.js"></script>
</body>
</html>

 

 

 

 

style.css
@import "reset.css";
body{
    background-color: #f6f9fc;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 50px 0px;
}

.canvas{
    width: 600px;
    height: 600px;
    background-color: white;
    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: 80px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

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

.controls_btns button{
    all: unset;
    cursor: pointer;
    background-color: white;
    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, 0.2);
    color: rgba(0, 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;
    border-radius: 25px;
    cursor: pointer;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}

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

 

 

 

 

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

 

 

 

 

 

app.js
const canvas = document.getElementById("jsCanvas");
const ctx = canvas.getContext("2d");
const colors = document.getElementsByClassName("jsColor");
const range = document.getElementById("jsRange");
const mode = document.getElementById("jsMode");
const saveBtn = document.getElementById("jsSave");

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

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

ctx.fillStyle = "white";
ctx.fillRect(0,0,CANVAS_SIZE,CANVAS_SIZE);

ctx.strokeStyle = INITIAL_COLOR;
ctx.fillStyle = INITIAL_COLOR;
ctx.lineWidth = 2.5;

//fill paint 
/*
ctx.fillStyle ="green";
ctx.fillRect(50,20,100,49);
ctx.fillStyle ="purple";
ctx.fillRect(80,100,100,49);
*/

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){
        //console.log("creating path in" , x ,y);
        ctx.beginPath();   //경로 생성
        ctx.moveTo(x, y);   //선 시작 좌표
    }else{
        //console.log("creating line in" , x ,y);
        ctx.lineTo(x, y);   //선 끝 좌표
        ctx.stroke();   //선 그리기
        //ctx.closePath();  //현대미술같은 선들..
    }
}

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

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

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


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

function handleCM(event){
    event.preventDefault();
}

function handleSaveClick(){
    const image = canvas.toDataURL();
    const link = document.createElement("a");
    link.href=image;
    link.download ="PaintJS[🎨]";
    link.click();
}

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

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

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

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

if(saveBtn){
    saveBtn.addEventListener("click",handleSaveClick);
}

 

 

 

강의가 짧고 쉽고 결과가 빨리 나오다보니 재밌는 강의였습니다.

시간 만드는 크롬앱 들으신 후 도전해 보시면 좋을거같아요

 

 

 

728x90
반응형

댓글