Canvas - Canvas JS Study

 

1. Canvas JS 공부


1) Canvas 기본 개념 공부


  • main.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>Document</title>
    <script src="./app.js"></script>
    
    <!-- 문서가 다 읽혀지고 읽어지는 방법 : html, js에서 해결 가능(document.addEventListener 이용) -->
    
</head>
<body>
      <!-- 주의사항 : css를 키우면 배율이 커진다. 그래서 속성을 키워야 한다. 즉, canvas를 키워야 한다.
           보통의 경우 속성을 키우고 css로 줄이는 경우는 이미지가 깨지는것을 방지해준다.  -->

      <canvas class="game-canvas" width="500" height="400"></canvas>
</body>
</html>


  • app.js
window.addEventListener("load", function(){

 	// 이것을 붙여줘야지 canvas 자동완성 한다.
    /** @type {CanvasRenderingContext2D} */ 
   

    var canvas = document.querySelector(".game-canvas");
    // css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.    
    
    var ctx = canvas.getContext('2d');          // 처음에 왜 안 읽혀졌지?
    
    // ctx.fillRect(10,10,50,50);               // 사각형 색깔 채우기
    // ctx.strokeStyle(20,10,150,200);          // 사각형 틀 그리기
    
    ctx.drawImage(img,100,100);


});


2) 이미지 표시하기

window.addEventListener("load", function(){

    // 이것을 붙여줘야지 canvas의 메서드가 자동완성 한다.
    /** @type {CanvasRenderingContext2D} */ 
    
    var canvas = document.querySelector(".game-canvas");
    // document.querySelector는 css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.    
   
    var ctx = canvas.getContext('2d');          // 처음에 왜 안 읽혀졌지?
       
    //var img = document.createElement('image');	// 이런 방식도 가능하다!!
    var img = new Image();			// image 객체 만들기
    img.src = './image/boy.jpg';		// image를 해당 경로로 가져오기 

    img.addEventListener("load", function(){
        ctx.drawImage(img,300,300);			// 바로 실행하기!
    });


});
<!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>Document</title>
    <script src="./app.js"></script>

      
</head>
<body>
      <!-- 주의사항 : css를 키우면 배율이 커진다. 그래서 속성을 키워야 한다. 즉, canvas를 키워야 한다. -->

      <canvas class="game-canvas" width="500" height="400"></canvas>
</body>
</html>
  • 현재 이 상태는 일부분이 잘리므로 전체이미지를 일부분만 따서 가져와야한다. 따라서, image 자르는 API를 배우자!!


3) 이미지 크롭하여 이미지의 일부분만 표시하기

window.addEventListener("load", function(){

    // 이것을 붙여줘야지 canvas의 메서드가 자동완성 한다.
    /** @type {CanvasRenderingContext2D} */ 
    
    var canvas = document.querySelector(".game-canvas");
    // css에서 style을 부여할 때, 이름을 명시해줄때, id명을 주거나 태그를 줄 수 있다.    

    var ctx = canvas.getContext('2d');          // 처음에 왜 안 읽혀졌지?
    
    //ctx.fillRect(10,10,50,50);               // 사각형 색깔 채우기
    //ctx.strokeStyle(20,10,150,200);          // 사각형 틀 그리기
    

    //var img = document.createElement('image'); // image 객체만드는 방식은 이런 방식도 가능하다!!
    var img = new Image();                       // image 객체 1개 만들면 메모리에 image 객체가 만들어진다.
    img.src = './image/boy.jpg';                 // image 속성을 해당 경로에 넣어주기 

    img.addEventListener("load", function(){             // 캔버스에 그릴려면, load가 다 끝난 다음에 그린다.
        ctx.drawImage(img,200,200);                      // 그래서, 바로 그리는 것이 아니라 이 image가 load가 되는 것을 기다렸다가 끝나면 그린다.  
        ctx.drawImage(img,50,50,70,120);                 // load가 끝나면, 실행될 이벤트 핸들러 함수를 만들어 주자!
        ctx.drawImage(img,100,100,200,200,0,0,100,100);  // 소스 이미지로서 전체 이미지를 크롭할 때 사용
    });                                        			// (앞 4개 숫자는 원본 소스를 크롭하는 구간, 뒤 4개 숫자는 어디에 이미지를 출력할지 위치와 크기를 각각 표시)

                                                         // **그렇기 때문에 게임 시작할 때, 처음에 이미지를 모두 load시키고 시작한다.

});

이미지


4) 애니메이션 효과로 점을 이동시켜보기




5) game Test

-main.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">

    <style>
        canvas { background: #eee;}
    </style>

    <title>Document</title>
    
    <script src="./keyboardtest.js"></script>
</head>

<body>
    <canvas class="myCanvas" width="480" height="320"></canvas>

</body>
</html>


-keyboardtest.js

window.addEventListener("load", function(){

    /** @type {CanvasRenderingContext2D} */ 
                                                                                                          
    var canvas = document.querySelector(".myCanvas");               // .이 굉장히 중요하다!!(이것 때문에 에러발생!!)
    var ctx = canvas.getContext("2d");

    var x = canvas.width/2;                        // 파란색 사각형이 움직이도록 해주는 값 기준과 조정
    var y = canvas.height-30;
    var dx = 2;
    var dy = -2;

    var paddleHeight = 20;                          // 이동하는 물체 크기                                         
    var paddleWidth = 20;

    var paddleX = (canvas.width-paddleWidth)/2;     // 이미지 보정하여 파란색 사각형이 키보드로 이동할 때, 사용
    var paddleY = (canvas.height-paddleHeight)/2;

    var rightPressed = false;   // 키보드 입력 초기 값
    var leftPressed = false;
    var upPressed = false;
    var downPressed = false;
    
    var itemRowCount = 2;       // 아이템의 배열 index
    var itemColumnCount = 2;

    var itemWidth = 15;         // 아이템 크기
    var itemHeight = 15;
    var itemPadding = 250;      // 아이템들 사이의  간격

    var itemOffsetTop = 20;     // 아이템들의 중간 좌표 조정
    var itemOffsetLeft = 20;    // 아이템들의 중간 좌표 조정

    var items = [];             // 아이템을 배열로 선언

    var exitHeight = 15;
    var exitWidth = 15;
    var exit = {x:0, y:0, status: 1};   // Exit 문 status 설정과 가로 세로 길이의 초기 설정
    

    for (let c = 0; c < itemColumnCount; c++) {         // 아이템에 status 초기 설정(나중에 충돌 컨트롤을 위해서)
        items[c] = [];
        for (let r = 0; r < itemRowCount; r++) {
            items[c][r] = { x: 0, y: 0, status: 1 };
        }
    }

    
    
    document.addEventListener("keydown", keyDownHandler, false);    // 이벤트 위임
    document.addEventListener("keyup", keyUpHandler, false);

    function keyDownHandler(e) {                                    // 키코드로 방향키 누를 때, 조건 설정
        if(e.key == "Right" || e.key == "ArrowRight") {
            rightPressed = true;
        }
        else if(e.key == "Left" || e.key == "ArrowLeft") {
            leftPressed = true;
        }
        else if(e.key == "Up" || e.key == "ArrowUp") {
            upPressed = true;
            //console.log(upPressed);
        }
        else if(e.key == "Down" || e.key == "ArrowDown") {
            downPressed = true;
            //console.log(downPressed);
            
        }
    }

    function keyUpHandler(e) {                                     // 키보드 안 누를 때, 조건 설정
        if(e.key == "Right" || e.key == "ArrowRight") {
            rightPressed = false;
        }
        else if(e.key == "Left" || e.key == "ArrowLeft") {
            leftPressed = false;
        } 
        else if(e.key == "Up" || e.key == "ArrowUp") {
            upPressed = false;
            //console.log(upPressed);
        }
        else if(e.key == "Down" || e.key == "ArrowDown") {
            downPressed = false;
            //console.log(downPressed);
        }
    }

    function drawPaddle() {
        ctx.beginPath();                                            // beginPath는 매우 중요하다!!!(안쓰면 동기화가 안되어서 한붓 그리기처럼 된다.)
        ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
        ctx.fillStyle = "#0095DD";
        ctx.fill();
        ctx.closePath();
    }

    function drawItem() {
        // ctx.beginPath();                                         // beginPath는 매우 중요하다!!!(안쓰면 동기화가 안되어서 한붓 그리기처럼 된다.)
        // ctx.arc (50, 50, 10, 0, Math.PI * 2, true);              // 1) 일일이 노가다로 아이템 그리기(원)
        // ctx.stroke();
        // ctx.fillStyle = "brown";
        // ctx.fill();
        // ctx.closePath();

        for (var c = 0; c < itemColumnCount; c++) {                                     // 2) 배열로 아이템 그리기 방법(사각형)
            for (var r = 0; r < itemRowCount; r++) {
                if (items[c][r].status == 1) {

                    var itemX = (c * (itemWidth + itemPadding) + itemOffsetLeft);      // 배열 인덱스마다 넘어가면서 item 그리기
                    var itemY = (r * (itemHeight + itemPadding) + itemOffsetTop);

                    items[c][r].x = itemX;                                             // 나중에 충돌 검색을 위해서 사용함.
                    items[c][r].y = itemY;

                    ctx.beginPath();
                    ctx.rect(itemX, itemY, itemWidth, itemHeight);
                    ctx.fillStyle = "red";
                    ctx.fill();
                    ctx.closePath();
                    //console.log(items[c][r].status);
                }
            }
        }
    }

    function drawExitDoor() {                   // Exit 그리기!(status를 연결시켜서 그려야 한다.)

        if(exit.status == 1) {

            var exitX = 150;
            var exitY = 280;
            
            exit.x = exitX;                     // 나중에 Exit문과 충돌 검사용
            exit.y = exitY;

            ctx.beginPath();
            ctx.rect(exitX, exitY, exitHeight, exitWidth); 
            ctx.fillStyle = "green";
            ctx.fill();
            ctx.closePath();   
        } 
    }

    function drawInventory() {                                      // 인벤토리 그리기 
        ctx.beginPath();                                            
        ctx.rect(canvas.width-150, 0, 150, canvas.height);
        ctx.fillStyle = "black";
        ctx.fill();
        ctx.closePath();    
    }
    
    function detectItem(){                                          // 아이템 줍기 코드
        for (var c = 0; c < itemColumnCount; c++) {
            for (var r = 0; r < itemRowCount; r++) {
                    
                var i = items[c][r];         
                var count = 0;

                if (i.status == 1) {            
                    //console.log(i.y);
                                                             // 범위 나중에 다시 고치기..

                    if (i.x < paddleX  && paddleX < i.x + itemWidth && i.y < paddleY && paddleY < i.y + itemHeight) {       
                        i.status = 0;                                               // 범위(item 좌표로부터 사용자 키보드 거리까지 비교하여 충돌 검색)
                        
                        console.log("collision");
                        ctx.clearRect(i.x, i.y, itemWidth, itemHeight);             // 충돌되면 item 주운 것이므로 item 삭제

                        ctx.beginPath();                                            // 아이템 먹으면, 인벤토리에 그 아이템을 채우기!!(이거 버그 발생)
                        ctx.rect(canvas.width-150, 100*count, 25, 25);
                        ctx.fillStyle = "red";
                        ctx.fill();
                        ctx.closePath();
                        
                    }
                    count++;
                }
            }
        }
    }

    function detectExitDoor() {             // 열쇠 4개 먹으면 탈출 성공!!

        if((items[0][0].status == 0 && items[0][1].status == 0 && items[1][0].status == 0 && items[1][1].status == 0) && exit.status == 1 ){
            if (exit.x < paddleX && paddleX < exit.x + exitHeight && exit.y < paddleY && paddleY < exit.y + exitWidth) {    
                exit.status = 0;
                ctx.clearRect(exit.x, exit.y, exitHeight, exitWidth);
                alert("Exit Room!!");
            }
        }
       
    }

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

        drawPaddle();             
        drawInventory();
        drawItem();
        drawExitDoor();
        detectItem();
        detectExitDoor();

        if(rightPressed) {                                          // 키보드를 누를 때마다 박스의 위치를 변경해준다.
            paddleX += 2;

            // if (paddleX + paddleWidth > canvas.width){           //  예전 코드
            //     paddleX = canvas.width - paddleWidth;
            // }
            
            if(paddleX + paddleWidth > canvas.width-150){           // 인벤토리 추가 후 키보드를 누를 때, 인벤토리 고정을 위한 코드
                paddleX = canvas.width-150 - paddleWidth;   
            }
        }
        else if(leftPressed) {
            paddleX -= 2;
            if (paddleX < 0){       // 키보드를 누를 때, canvas 화면 고정을 위한 코드
                paddleX = 0;
            }    
        }
        else if(upPressed) {
            paddleY -= 2;
            if (paddleY < 0){       // 키보드를 누를 때, canvas 화면 고정을 위한 코드
                paddleY = 0;
            }                 
        }
        else if(downPressed) {
            paddleY += 2;
            if (paddleY + paddleHeight > canvas.height){            // 인벤토리 추가 후 키보드를 누를 때, 인벤토리 고정을 위한 코드
                paddleY = canvas.height - paddleHeight;
            }       
        }       
        x += dx;
        y += dy;                    // 파란색 사각형이 움직일 때, 미세 값으로 프레임 조정
    }

    setInterval(draw, 10);

});