곡 νŠ€κΈ°κΈ° πŸ€πŸ

gyomniΒ·2022λ…„ 2μ›” 18일
2

Interactive Web

λͺ©λ‘ 보기
1/1
post-thumbnail

1) 곡의 μ›€μ§μž„μ„ canvas에 λ‚˜νƒ€λ‚΄κΈ°

πŸ“ 쀑심점, λ°˜μ§€λ¦„

곡을 κ·Έλ¦΄λ•ŒλŠ” 항상 쀑심점을 κΈ°μ€€μœΌλ‘œ 곡에 x , y 값을 μž‘λŠ”λ‹€.
=> 쀑심점이 곡의 μ‹€μ œ μœ„μΉ˜ (νšŒμƒ‰ 점)

λ°˜μ§€λ¦„μ΄ ꡉμž₯히 μ€‘μš”!
λ°˜μ§€λ¦„μ„ x, y 값에닀가 + , - ν•΄μ€˜μ•Ό μ‹€μ œλ‘œ 곡이 λΈŒλΌμš°μ €μ— λ‹Ώμ•˜λŠ”μ§€ μ•Œ 수 있음.

κ·Έλž˜μ„œ 곡에 x , y값을 λΉ„κ΅ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, λ°˜μ§€λ¦„μ„ λ”ν•œ κ°’ or λΊ€ 값을 μ •μ˜ν•΄μ„œ
곡이 μ‹€μ œλ‘œ 어디에 λ‹Ώμ•˜λŠ”μ§€λ₯Ό νŒλ‹¨ν•˜κ²Œ 됨.

πŸ“ vx, vy

λΈŒλΌμš°μ € μƒμ—μ„œ 곡에 μ›€μ§μž„μ„ 주게 되면 x, y에 vx와 vyλΌλŠ” μž„μ˜μ˜ 값을 λ”ν•΄μ£Όκ²Œ λœλ‹€.

=> xκ°’κ³Ό y값이 계속 μ¦κ°€ν•˜κ±°λ‚˜ κ°μ†Œν• ν…Œλ‹ˆκΉŒ μ›€μ§μž„μ„ κ°€μ§€λ©΄μ„œ 그릴수 있게 λœλ‹€.
( 그림처럼 곡이 vx와 vyλ₯Ό λ”°λΌμ„œ μ›€μ§μ΄κ²Œ 됨. )

πŸ“ λΈŒλΌμš°μ €μ˜ (0,0) => xκ°€ 0에 λ‹Ώμ•˜λŠ”μ§€ νŒλ‹¨.

λ§Œμ•½μ— λ‹Ώμ•˜λ‹€κ³  ν•œλ‹€λ©΄!
vx(=x)이 λ‹Ώμ•˜μœΌλ‹ˆκΉŒ vx에 -1을 κ³±ν•΄μ€€λ‹€.

ν˜„μž¬ y값은 μ¦κ°€ν•˜κ³  x값은 κ°μ†Œν•˜λŠ” μ›€μ§μž„μ„ 가지고 μžˆλ‹€.
=> x값에 -1을 κ³±ν•΄μ£Όλ©΄ x값이 λ‹€μ‹œ μ¦κ°€ν•˜κ²Œ λ˜λ‹ˆκΉŒ,
xκ°€ μ™Όμͺ½μœΌλ‘œ μ›€μ§μ΄λ˜ 것이 였λ₯Έμͺ½μœΌλ‘œ μ›€μ§μ΄κ²Œ 되고, yλŠ” μœ„λ‘œ κ°€κ³  ...
μ΄λŸ°μ‹μœΌλ‘œ 곡이 κ·Έλ €μ§€κ²Œ λœλ‹€!
κ·Έλž˜μ„œ νŠ•κ²¨μ§€λŠ” λͺ¨μŠ΅ λ³΄μ—¬μ£Όκ²Œ 됨!! ~~⚽ ⚾ ⚽ ⚾

yκ°€ μœ„μ— λ‹Ώμ•˜λ‹€λ©΄ μ¦κ°€ν•˜λ˜ y값을 λ°˜λŒ€λ‘œ κ°μ†Œν•˜κ²Œ λ§Œλ“€μ–΄ 쀌.
그러면 μ•„λž˜ λ°©ν–₯으둜 μ›€μ§μ΄λŠ” λͺ¨μŠ΅μ„ λ³Ό 수 μžˆλ‹€.

νŒ…νŒ…νŒ…~ ν•˜λŠ” μ›€μ§μž„ let's go πŸš€πŸš€


2) μ½”λ“œ 적용

index.html, style.css, app.js, ball.js
4개의 파일 μƒμ„±ν•΄μ„œ μ•„λž˜μ™€ 같은 μ½”λ“œλ₯Ό μž‘μ„±ν•œλ‹€.

  • index.html
    : css 파일과 script λΆˆλŸ¬μ˜€λŠ” μš©λ„λ‘œ 생성해두기.

  • style.css

*{
    user-select: none;
    -ms-user-select:none;
    outline:0;
    margin: 0;
    padding: 0;
    -webkit-tab-highlight-color:rgba(0,0,0,0);
}
html{
    width: 100%;
    height: 100%;

}
body{
    width: 100%;
    height: 100%;
    overflow: hidden;
    background-color: #6465A6;


}
canvas{
    width: 100%;
    height: 100%;
}
  • app.js
// ball.js import
import{
    Ball
} from './ball.js';

class App{
    constructor(){
        this.canvas = document.createElement('canvas');     // μΊ”λ²„μŠ€ 생성
        this.ctx = this.canvas.getContext('2d');            // context κ°€μ Έμ˜€κΈ°

        document.body.appendChild(this.canvas);
        window.addEventListener('resize', this.resize.bind(this), false) // λ¦¬μ‚¬μ΄μ¦ˆ 이벀트 κ±ΈκΈ° -> ν˜„μž¬ λ‚΄κ°€ λ§Œλ“€κ³ μž ν•˜λŠ” μ• λ‹ˆλ©”μ΄μ…˜ 크기λ₯Ό μ•„λŠ” 것이 ꡉμž₯히 μ€‘μš”.
        this.resize();

        // 화면에 μ›€μ§μ΄λŠ” κ±° 확인해보기
        this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 15); // λ°˜μ§€λ¦„ 60, 속도 15둜 μž„μ˜ν…ŒμŠ€νŠΈ 해보기

        window.requestAnimationFrame(this.animate.bind(this)); // requestAnimationFrame κ±Έμ–΄μ€€ λ‹€μŒ -> line28 (μ• λ‹ˆλ©”μ΄μ…˜ ꡬ동 ν•¨μˆ˜ 생성)
    }
    // λ¦¬μ‚¬μ΄μ¦ˆ 이벀트λ₯Ό κ±Έμ–΄μ£Όκ³  슀크린 μ‚¬μ΄μ¦ˆλ₯Ό κ°€μ Έμ™€μ„œ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ •μ˜.

        resize(){
            this.stageWidth = document.body.clientWidth;
            this.stageHeight = document.body.clientHeight;

            // 슀크린 μ‚¬μ΄μ¦ˆλ₯Ό 미리 μ •ν•΄ 놓고 ν•˜λŠ” κ²½μš°κ°€ λ§Žμ€λ° 사싀 λΈŒλΌμš°μ €λŠ” 가변적인것.
            // κ·Έλž˜μ„œ μŠ€ν¬λ¦°μ‚¬μ΄μ¦ˆ κ°€μ Έμ˜€λŠ” ν•¨μˆ˜λ₯Ό λ¨Όμ € μ •μ˜ ν•΄μ£Όκ³  μž‘μ—…μ„ ν•˜λŠ”κ²Œ λ‚˜μ€‘μ„ μœ„ν•΄μ„œλΌλ„ νŽΈν•˜λ‹€κ³  함.

            this.canvas.width = this.stageWidth * 2;
            this.canvas.height = this.stageHeight * 2;
            this.ctx.scale(2,2);
        }


        //μ• λ‹ˆλ©”μ΄μ…˜ μ‹€μ œλ‘œ κ΅¬λ™μ‹œν‚€λŠ” ν•¨μˆ˜ 생성
        animate(t){ 
            window.requestAnimationFrame(this.animate.bind(this));

            this.ball.draw(this.ctx, this.stageWidth, this.stageHeight);
        } 
    }


window.onload = () =>{
    new App();
};
  • ball.js
export class Ball{
    constructor(stageWidth,stageHeight, radius, speed ){
        // μŠ€μΌ€μ΄μ§€ μ‚¬μ΄μ¦ˆλ₯Ό κ°€μ Έμ˜€κ³  λ°˜μ§€λ¦„κ³Ό 속도λ₯Ό 가지고 옴.
        this.radius =radius;

        // vx, vyλŠ” x,y μ’Œν‘œκ°’μ„ μ›€μ§μ΄λŠ” 속도라고 μ •ν•˜κΈ°.
        this.vx = speed;
        this.vy = speed;

        // μŠ€ν…Œμ΄μ§€μ— 랜덀으둜 μœ„μΉ˜ν•  수 있게 ν•¨μˆ˜λ₯Ό μ •μ˜ν•΄μ€Œ.
        const diameter = this.radius * 2;
        this.x = this.radius +(Math.random() * stageWidth - diameter);
        this.y = this.radius +(Math.random() * stageHeight - diameter);
    }

    // drawν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄μ„œ contextλ₯Ό 가지고 였고 μŠ€ν…Œμ΄μ§€ μ‚¬μ΄μ¦ˆλ₯Ό κ°€μ Έμ˜΄.
    // 그러면 canvas context에 그림을 그릴 수 μžˆλŠ” ν•¨μˆ˜κ°€ 완성이 됨.
    draw(ctx, stageWidth,stageHeight){

         // x와 y 값에 vx와 vy값을 λ”ν•΄μ€˜μ„œ 곡이 움직이도둝 λ§Œλ“¦.
        this.x += this.vx;
        this.y += this.vy;

        this.bounceWindow(stageWidth,stageHeight);

        // 곡에 색을 μ •ν•˜κ³  κ·Έλ¦Ό 그리기
        ctx.fillStyle = '#fdf500';
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fill();


    }
    // bounceWindowλΌλŠ” ν•¨μˆ˜ 생성 (μŠ€ν…Œμ΄μ§€ 상에 λ‹Ώμ•˜λŠ”μ§€λ₯Ό νŒλ‹¨ν•˜λŠ” ν•¨μˆ˜)
    bounceWindow(stageWidth,stageHeight){

        // μŠ€ν…Œμ΄μ§€ 넓이와 높이λ₯Ό 가지고 μ™€μ„œ 
        const minX = this.radius;
        const maxX = stageWidth - this.radius;
        const minY = this.radius;
        const maxY = stageHeight - this.radius;

        // μŠ€ν…Œμ΄μ§€ 상에 곡이 λ‹Ώμ•˜λ‹€λ©΄ λ°˜λŒ€λ‘œ νŠ•κΈ°κΈ°
        // 곡이 어디에 λ‹Ώμ•˜λŠ”μ§€ νŒλ‹¨ν•˜κ³  vx와 vy에 -1을 κ³±ν•΄μ€˜μ„œ λ°˜λŒ€λ‘œ μ›€μ§μ΄κ²Œ ν•˜κΈ°
        if(this.x <=minX || this.x >=maxX){
            this.vx *= -1;
            this.x += this.vx;
        } else if(this.y <=minY || this.y >=maxY){
            this.vy *= -1;
            this.t += this.vy;
        }

    }
}

ν˜„μž¬κΉŒμ§€μ˜ μ½”λ“œλ₯Ό 싀행해보면 μ•„λž˜μ™€ 같이 곡의 μ›€μ§μž„μ΄ μ„ μœΌλ‘œ μ΄μ–΄μ§€κ²Œλœλ‹€. ↓↓↓

μ• λ‹ˆλ©”μ΄μ…˜μ€ 계속 무언가λ₯Ό μƒμ„±ν•˜λŠ” 것이기 λ•Œλ¬Έμ— μœ„μ™€ 같은 상황이 λ°œμƒλ¨ !
κ·Έλž˜μ„œ μƒμ„±ν•˜κΈ° 전에 이전 ν”„λ ˆμž„μ„ μ§€μ›Œμ€˜μ•Όν•œλ‹€.

=> app.js의 animate ν•¨μˆ˜μ— μ•„λž˜μ™€ 같이 clearRect() μ‹€ν–‰ν•΄μ£ΌλŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ λœλ‹€!

 this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight)

μ• λ‹ˆλ©”μ΄μ…˜ μ‹€ν–‰ λͺ¨μŠ΅ 🀩
움직인닀 ~~ !! ~~~
μ •ν™•νžˆ λ§ν•˜μžλ©΄ μ›€μ§μ΄λŠ” 것은 μ•„λ‹ˆκ³  이전 κ·Έλ¦Όκ³Ό λ‹€μŒ 그림을 λ²ˆκ°ˆμ•„ κ°€λ©΄μ„œ λ³΄μ—¬μ£ΌλŠ” 것인데,
이전에 그렸던 것을 μ§€μ›Œμ€ŒμœΌλ‘œμ¨ 곡이 μ›€μ§μ΄λŠ” κ²ƒμ²˜λŸΌ λ³΄μ—¬μ§€κ²Œ λœλ‹€.

🧱🧱🧱🧱 이제 λ²½λŒμ„ μƒμ„±ν•΄λ³΄μž 🧱🧱🧱🧱

  • block.js
    : μŠ€ν…Œμ΄μ§€ 상에 벽돌 λ§Œλ“€κΈ° μœ„ν•΄ μΆ”κ°€λ‘œ block.jsλ₯Ό μƒμ„±ν•œλ‹€!
export class Block{
    constructor(width, height, x, y) {
        // λ²½λŒμ€ 넓이, 높이, x, y 값을 가지고 μžˆλ‹€
        this.width = width;
        this.height = height;
        this.x=x;
        this.y=y;

        // 곡을 μΆ”μ ν•˜κΈ°μœ„ν•΄ maximum값도 μ •μ˜ν•΄ μ€€λ‹€.
        this.maxX=width + x;
        this.maxY=height + y;
        
    }
    draw(ctx){
        // drawν•¨μˆ˜μ— μ‹€μ œλ‘œ κ·Έλ €μ§€λŠ” κ±Έ λ§Œλ“€μ–΄ μ€€λ‹€.
        const xGap = 80;
        const yGap = 60;

        ctx.fillStyle = '#ff384e';
        ctx.beginPath();
        ctx.rect(this.x, this.y, this.width, this.height);
        ctx.fill();

        // λ””μžμΈμ„ μœ„ν•œ 그림자 생성
        // κ·Έλ¦Όμ—μ„œ λͺ¨λ“ κ±΄ λ‹€ μ’Œν‘œ. 즉 μ–Όλ§ˆλ‚˜ μ΄λ™μ‹œν‚¬ 것인가λ₯Ό μ’Œν‘œλ‘œ μ„€μ •.
        // x,y μ’Œν‘œ μ„€μ •
        ctx.fillStyle = '#190f3a';
        ctx.beginPath();
        ctx.moveTo(this.maxX, this.maxY);
        ctx.lineTo(this.maxX-xGap, this.maxY+yGap);
        ctx.lineTo(this.x-xGap, this.maxY+yGap);
        ctx.lineTo(this.x, this.maxY);
        ctx.fill();

        // μ˜†λΆ€λΆ„ 그림자
        ctx.fillStyle = '#9d0919';
        ctx.beginPath();
        ctx.moveTo(this.x, this.y);
        ctx.lineTo(this.x, this.maxY);
        ctx.lineTo(this.x-xGap, this.maxY+yGap);
        ctx.lineTo(this.x-xGap, this.maxY+yGap-this.height);
        ctx.fill();

    }

}

벽돌이 생성은 λ˜μ—ˆμ§€λ§Œ, 곡이 벽돌둜 인해 νŠ•κ²¨μ§€λŠ” μ• λ‹ˆλ©”μ΄μ…˜μ€ λ³Ό 수 μ—†λ‹€.
(μœ„μ˜ μ΄λ―Έμ§€λŠ” 벽돌 생성 확인을 μœ„ν•œ 이미지(pngμž„! gif λ…Έλ…Έ)).

λ²½λŒμ— 곡이 λ‹Ώμ•˜μ„ λ•Œ νŠ•κ²¨ λ‚˜κ°€κ²Œ ν•˜κΈ° μœ„ν•΄μ„œλŠ”!

ball의 μ’Œν‘œμ™€ block의 μ’Œν‘œλ₯Ό λΉ„κ΅ν•΄μ„œ μ–΄λŠ 값이 κ°€μž₯ κ·Όμ ‘ν•œ 지λ₯Ό 찾으면 μœ„μΉ˜λ₯Ό μ•Œ 수 μžˆλ‹€.
=> κ·Όμ ‘ν•œ 값을 νŒλ‹¨ν•˜λŠ” ν•¨μˆ˜λ₯Ό ν•˜λ‚˜ λ§Œλ“¦.

    // λ²½λŒμ—μ„œ 곡이 νŠ•κΈ΄ λ°˜μ‚¬κ°’ ν•¨μˆ˜
    
    bounceBlock(block){
        const minX = block.x - this.radius;
        const maxX = block.maxX + this.radius;
        const minY = block.y - this.radius;
        const maxY = block.maxY + this.radius;

        // block에 λ‹Ώμ•˜λŠ”μ§€ νŒλ‹¨, λ‹Ώμ•˜λ‹€λ©΄ vx와 vy에 -1을 κ³±ν•΄μ£ΌλŠ” λ°©μ‹μœΌλ‘œ 곡이 νŠ•κΉ€.
        // 곡이 μΆ©λŒν•  λ•Œ μ–‘ μ˜†μ— μΆ©λŒν•˜λŠ”μ§€ μœ„μ•„λž˜ μΆ©λŒν•˜λŠ”μ§€ νŒλ‹¨ν•˜κΈ° μœ„ν•΄μ„œλŠ” ball의 μ’Œν‘œμ™€ block의 μ’Œν‘œλ₯Ό λΉ„κ΅ν•΄μ„œ μ–΄λŠ 값이 κ°€μž₯ κ·Όμ ‘ν•œ 지λ₯Ό 찾으면 μœ„μΉ˜λ₯Ό μ•Œ 수 있음.
        // κ·Έλž˜μ„œ κ·Όμ ‘ν•œ 값을 νŒλ‹¨ν•˜λŠ” ν•¨μˆ˜λ₯Ό ν•˜λ‚˜ λ§Œλ“¦.
        if(this.x > minX && this.x < maxX && this.y >minY && this.y<maxY) {
            const x1 = Math.abs(minX - this.x);
            const x2 = Math.abs(this.x - maxX);
            const y1 = Math.abs(minY - this.y);
            const y2 = Math.abs(this.y - maxY);
            const min1 = Math.min(x1, x2);
            const min2 = Math.min(y1, y2);
            const min = Math.min(min1, min2);

            // 값이 μ •μ˜λ˜λ©΄ 이제 vxλ‚˜  vy에 -1을 κ³±ν•΄μ€€λ‹€.
            if(min == min1){
                this.vx *= -1;
                this.x +=this.vx;
            } else if(min == min2){
                this.vy *= -1;
                this.y +=this.vy;
            };

            
        }
    }

이둜써 λ²½κ³Ό λΈŒλΌμš°μ €μ˜ 끝단에 νŠ•κ²¨μ§€λŠ” 곡 μ• λ‹ˆλ©”μ΄μ…˜μ„ μ™„μ„±~πŸŽ‰

데λͺ¨ γ…Žγ…Ž---> bounce a ball

ν•™μŠ΅ : μœ νŠœλ²„ Interactive Developer

profile
Front-end developer πŸ‘©β€πŸ’»βœ

0개의 λŒ“κΈ€