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 ππ
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