์ค๋์ ์ด์ด์ Part2 [Canvas]์ Chapter02 ๋ถ๊ฝ๋์ด ๊ฐ์๋ฅผ ๋ค์์ต๋๋ค.(01~03๊ฐ)
๊ทธ๋ผ ์ค๋ ๊ฐ์์์ ๋ฐฐ์ด ๋ด์ฉ๋ค์ ๊ฐ๋ตํ๊ฒ ์์ฑํด ๋ณผ๊ฒ์!
๐01. ๋ณด์ผ๋ฌ ํ๋ ์ดํธ ์ฝ๋ ์ถ๊ฐ
์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ง๋ ํ๋ก์ ํธ์ ๋ค๋ฅด๊ฒ ํ์ผ์ ๋ถ๋ฆฌํ๋ ๊ฒ๋ถํฐ ์์ํ์ต๋๋ค. ์ฝ๋๊ฐ ๋ณต์กํด์ง์๋ฐ๋ผ ํ ํ์ผ์ ์์ฑํ๋ ๊ฒ์ ์๋ฌด๋๋ ๊ฐ๋ ์ฑ์ ๋ฌด๋ฆฌ๊ฐ ์์ผ๋๊น์.
์ฐ์ init ํจ์๋ฅผ ๋ง๋ค์ด ์บ๋ฒ์ค์ dps ๊ฐ์ ์ ์ฉํ์ฌ ๋ณด๋ค ์ ๋ช ํ ํ๋ฉด์ ๋ง๋ค์ด์ค๋๋ค.
function init() {
canvasWidth = innerWidth;
canvasHeight = innerHeight;
// ์บ๋ฒ์ค ๊ณ ์ ์ฌ์ด์ฆ ์ง์
canvas.width = canvasWidth * dpr;
canvas.height = canvasHeight * dpr;
ctx.scale(dpr, dpr); // ์บ๋ฒ์ค ์์ ์ ์ฒด ์ปจํ
์ธ ๊ฐ ํ๋๋จ
// css๋ก ์บ๋ฒ์ค ๊ณ ์ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ ๋ก css์ ๋ง์ถฐ ๋ณ๊ฒฝ
canvas.style.width = canvasWidth + 'px'; // ํ๋๊ฐ ๋๊ฒ์ ๊ฐ์ ๋ก ์ฌ์ด์ฆ ์กฐ์ ํ์ฌ window innerWidth, innerHeight์ ๋ง์ถฐ ์กฐ์ -> ์ ๋ช
ํ ํ๋ฉด
canvas.style.height = canvasHeight + 'px';
}
render ํจ์๋ฅผ ๋ง๋ค์ด์ fps์ ์๊ด์์ด ๋ชจ๋ ํ๋ฉด์์ ๋์ผํ ์ ๋๋ฉ์ด์ ์ ๋ณผ ์ ์๋๋ก ์ด๊ธฐ์ค์ ์ ํด์ค๋๋ค.
function render() {
requestAnimationFrame(render);
now = Date.now()
delta = now - then;
if (delta < interval) return;
// main code
then = now - (delta%interval);
}
์ด์ ํฌ์คํธ์์๋ ์ธ๊ธํ ๋ถ๋ถ์ด์ง๋ง, ์์๋ก ์กฐ๊ธ ๋ ์ค๋ช ์ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
์๋๋ canvas์ ๋๋น์ ๋์ด๋ฅผ ๊ฐ๊ฐ 400ํฝ์ ๋ก ์ค์ ํ ํ, dpr, scale, ๊ทธ๋ฆฌ๊ณ CSS ์ค์ ์ ์ฐจ๋ก๋ก ์ถ๊ฐํ๋ ๊ณผ์ ์ ๋๋ค.
dpr์ 1์์ 2๋ก ๋๋ฆด ๊ฒฝ์ฐ ์บ๋ฒ์ค์ ํฌ๊ธฐ๊ฐ 2๋ฐฐ๋ก ์ปค์ง ๊ฒ์ ํ์ธํ์ค ์ ์์ต๋๋ค.
์ฌ๊ธฐ์์ canvas.scale(dpr, dpr)๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋ชจ๋ ์ปจํ ์ธ ์ฌ์ด์ฆ๊ฐ ์บ๋ฒ์ค์ ๋๊ฐ์ด ํ๋๊ฐ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ์บ๋ฒ์ค์ ์คํ์ผ๋ก ๋ชฉํ ์ฌ์ด์ฆ๋ฅผ ์กฐ์ ํด์ฃผ๊ฒ ๋๋ฉด ๋ค์ DPR์ด 1์ผ ๋ ๋ชจ์์ผ๋ก ๋ณด์ด๊ฒ ๋ฉ๋๋ค!
init๊ณผ render๊น์ง ์ถ๊ฐํ์์ผ๋ฉด ๋ง์ง๋ง์ผ๋ก addEventListener๋ก ์๋์ฐ ๋ก๋๊ฐ ์๋ฃ๋์ ๋์ ๋ฆฌ์ฌ์ด์ฆ ๋์ ๋ ์คํํ ์ฝ๋ฐฑ์ ์ถ๊ฐํด์ค๋๋ค.
window.addEventListener('load' , () => {
init();
render();
})
window.addEventListener('resize', () => {
init();
})
์ฐธ๊ณ ๋ก ๊ฐ์์์๋ ์ด ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋๋ฅผ ํด๋์ค๋ก ๋ณ๊ฒฝํด์ ์ฌ์ฉํ์ต๋๋ค.
class Canvas extends CanvasOption {
constructor() {
// ๋ถ๋ชจํด๋์ค์์ ๋ง๋ ๋ณ์, ๋ฉ์๋๋ฅผ ์์ ๋ฉ์๋๊ฐ ์ฌ์ฉํ ์ ์์
super();
}
init() {
this.canvasWidth = innerWidth;
this.canvasHeight = innerHeight;
// ์บ๋ฒ์ค ๊ณ ์ ์ฌ์ด์ฆ ์ง์
this.canvas.width = this.canvasWidth * this.dpr;
this.canvas.height = this.canvasHeight * this.dpr;
this.ctx.scale(this.dpr, this.dpr); // ์บ๋ฒ์ค ์์ ์ ์ฒด ์ปจํ
์ธ ๊ฐ ํ๋๋จ
// css๋ก ์บ๋ฒ์ค ๊ณ ์ ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ ๋ก css์ ๋ง์ถฐ ๋ณ๊ฒฝ
this.canvas.style.width = this.canvasWidth + "px"; // ํ๋๊ฐ ๋๊ฒ์ ๊ฐ์ ๋ก ์ฌ์ด์ฆ ์กฐ์ ํ์ฌ window innerWidth, innerHeight์ ๋ง์ถฐ ์กฐ์ -> ์ ๋ช
ํ ํ๋ฉด
this.canvas.style.height = this.canvasHeight + "px";
}
...
}
๐02. Particle Class ์ถ๊ฐ
์๋์ฒ๋ผ ํํฐํด ํด๋์ค๋ฅผ ์ ์ํ๊ณ canvas class๋ก ๋์๊ฐ์ constructor์ particles ๋ฐฐ์ด์ ์ถ๊ฐํด์ค๋๋ค.
import CanvasOption from "./CanvasOption.js";
export default class Particle extends CanvasOption {
constructor(x, y) {
super();
this.x = x;
this.y = y;
}
update() {}
draw() {
// ํธ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์์ํ๋ค๊ณ ์๋ฆผ
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, 10, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
}
๊ทธ๋ฆฌ๊ณ createParticles ํจ์๋ฅผ ์ถ๊ฐ ํ ์๋์ฒ๋ผ ์์ฑํ๊ณ
createParticles() {
const PARTICLE_NUM = 1;
for (let i = 0; i < PARTICLE_NUM; i++) {
const x = 300;
const y = 300;
this.particles.push(new Particle(x, y));
}
}
render ํจ์ ๋ด์ ํํฐํด ๋ฐฐ์ด์ ๋๋ฉฐ update์ draw ๋ฉ์๋๋ฅผ ๋ฐ๋ณตํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ์๋ฉด
this.particles.forEach((particle) => {
particle.update();
particle.draw();
});
์๋์ฒ๋ผ ์์ ์ ์ด ์ฐํ๋ ๊ฒ์ ํ์ธํ์ค ์ ์์ต๋๋ค.
ํํฐํด์ด ์์ง์ด๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ๋ ค๋ฉด Updateํจ์์์ ํ์ฌ x, y๊ฐ์ ์๋๋ฅผ ๋ถ์ฌ์ฃผ์๋ฉด ๋ฉ๋๋ค.
๊ฐ๋จํ๊ฒ this.y += 1์ ์ถ๊ฐํด๋ณด๋ฉด ์บ๋ฒ์ค๋ฅผ ์ด๊ธฐํํ๋ ์ต์ ์ ์ถ๊ฐํ์ง์์ ๊ธฐ์กด ๋ํ์ด ๊ทธ๋๋ก ๋ณด์ฌ ์ผ์ง์ ์ ๊ทธ๋ฆฌ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
update() {
this.y += 1;
}
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด canvas ํด๋์ค์ renderํจ์์ clearRect ํจ์๋ฅผ ํธ์ถํ ์๋์์ง๋ง, ๊ฐ์์์๋ ๋ฐฐ๊ฒฝ์ ์ญํ ์ ๊ฒธํ๊ธฐ ์ํด ๊ณต์ ํฐ์์ผ๋ก, ๋ฐฐ๊ฒฝ์ ๊ฒ์์์ผ๋ก ๋งค ํ๋ ์๋ง๋ค ์์น ํ๋ ๋ฐฉ์์ผ๋ก ์งํํ์ต๋๋ค.
// render ํจ์์ ์ถ๊ฐ๋ ๋ฐฐ๊ฒฝ์ ์น ํ๋ ์ฝ๋
this.ctx.fillStyle = this.bgColor;
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// particle draw ํจ์ ์ฝ๋
draw() {
this.ctx.fillStyle = "#ffffff";
// ํธ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์์ํ๋ค๊ณ ์๋ฆผ
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, 10, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
์ฌ๊ธฐ๊น์ง๊ฐ ๊ธฐ๋ณธ ์ฝ๋์ด๊ณ , ์ดํ ์๋ ์ฝ๋๋ค์ ์ถ๊ฐํด์ฃผ๋ฉด ๊ธฐ๋ณธ์ ์ธ ๋ถ๊ฝ๋์ด ์ ๋๋ฉ์ด์ ์ด ์์ฑ๋ฉ๋๋ค.
1. ๊ฐ ํํฐํด์ ๋๋คํ๊ฒ ๋ฐฐ์นํ๋ ์ฝ๋ ์ถ๊ฐ
2. ๊ณต์ด ํ๋ฉด๋ฐ์ผ๋ก ๋๊ฐ์ ๋ ๋ ์ด์ ๋ ๋๋ง๋์ง์๊ฒ ๋ณ๊ฒฝ
3. ๊ณต์ด ์ฌ๊ฐํํํ๊ฐ ์๋, ์ ํํ๋ก ํผ์ง๊ฒ ๋ณ๊ฒฝ
2๋ฒ๊น์ง ์ ์ฉ ์ ์๋์ ๊ฐ์ ์ ๋๋ฉ์ด์ ์ด ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
โจ์ค๋์ ํ์ต ์ธ์ฆ ์ท


๋ณธ ํฌ์คํ ์ ํจ์คํธ์บ ํผ์ค ํ๊ธ ์ฑ๋ฆฐ์ง ์ฐธ์ฌ๋ฅผ ์ํด ์์ฑํ์์ต๋๋ค.
https://fastcampus.co.kr/event_online_challenge_2401_mission
์ปค๋ฆฌ์ด ์ฑ์ฅ์ ์ํ ์ต๊ณ ์ ์ค๋ฌด๊ต์ก ์์นด๋ฐ๋ฏธ | ํจ์คํธ์บ ํผ์ค
์ฑ์ธ ๊ต์ก ์๋น์ค ๊ธฐ์ , ํจ์คํธ์บ ํผ์ค๋ ๊ฐ์ธ๊ณผ ์กฐ์ง์ ์ค์ง์ ์ธ '์ (ๆฅญ)'์ ์ฑ์ฅ์ ๋๊ณ ์ ๋ชจ๋ ์ข ๋ฅ์ ๊ต์ก ์ฝํ ์ธ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๋ํ๋ฏผ๊ตญ No. 1 ๊ต์ก ์๋น์ค ํ์ฌ์ ๋๋ค.
fastcampus.co.kr