완성 코드
전체코드(보기)
더보기
const $canvas = document.querySelector("canvas");
const ctx = $canvas.getContext("2d");
const getRandomRadius = () => Math.random() * 1 + 0.5;
const getRandomSpeed = () => Math.random() * 0.3 + 0.1;
const getRandomDir = () => [-1, 1][Math.floor(Math.random() * 2)];
const Snow = {
data: [],
canvasWidth: $canvas.clientWidth,
canvasHeight: $canvas.clientHeight,
init() {
Snow.make();
Snow.loop();
},
loop() {
Snow.move();
Snow.draw();
window.requestAnimationFrame(Snow.loop);
},
make() {
const data = [];
// 랜덤한 데이터 200개 생성
for (let i = 0; i < 200; i++) {
const x = Math.random() * Snow.canvasWidth;
const y = Math.random() * Snow.canvasHeight;
const size = getRandomRadius();
const speed = getRandomSpeed();
const dir = getRandomDir();
data.push({ x, y, size, speed, dir });
}
// Snow 객체에 데이터 저장
Snow.data = data;
},
move() {
Snow.data = Snow.data.map((item) => {
// 방향에 맞게 이동
item.x += item.dir * item.speed;
item.y += item.speed;
// 캔버스를 벗어났는지 판단
const isMinOverPositionX = -item.size > item.x;
const isMaxOverPositionX = item.x > Snow.canvasWidth;
const isOverPositionY = item.y > Snow.canvasHeight;
// 벗어나면 반대방향, 맨 위로
if (isMinOverPositionX || isMaxOverPositionX) {
item.dir *= -1;
}
if (isOverPositionY) {
item.y = -item.size;
}
return item;
});
},
draw() {
ctx.clearRect(0, 0, Snow.canvasWidth, Snow.canvasHeight);
ctx.fillStyle = "#0f1018";
ctx.fillRect(0, 0, Snow.canvasWidth, Snow.canvasHeight);
Snow.data.forEach((item) => {
ctx.beginPath();
ctx.fillStyle = "rgba(255, 255, 255, .6)";
ctx.arc(item.x, item.y, item.size, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
});
},
};
Snow.init();
window.onresize = () => {
Snow.canvasWidth = $canvas.clientWidth;
Snow.canvasHeight = $canvas.clientHeight;
Snow.make();
};
코드 풀이
<canvas width="460" height="460" />
먼저 내리는 눈을 그려줄 캔버스 요소를 만들어줍니다. (예시로 460 사이즈 지정)
const $canvas = document.querySelector("canvas");
const ctx = $canvas.getContext("2d");
그려줄 캔버스 DOM을 찾아서 변수에 저장해 줍니다.
// 크기
const getRandomRadius = () => Math.random() * 1 + 0.5;
// 속도
const getRandomSpeed = () => Math.random() * 0.3 + 0.1;
// 방향
const getRandomDir = () => [-1, 1][Math.floor(Math.random() * 2)];
눈의 크기, 움직이는 속도, 방향을 랜덤 하게 가져오는 함수를 미리 선언해줍니다.
const Snow = {
data: [],
canvasWidth: $canvas.clientWidth,
canvasHeight: $canvas.clientHeight,
init() {
},
loop() {
},
make() {
},
move() {
},
draw() {
},
};
눈이 내리는 효과를 만들기 위해서 필요한 함수 총 5개와 캔버스 크기를 미리 세팅해 줍니다.
make() {
const data = [];
// 랜덤한 데이터 200개 생성
for (let i = 0; i < 200; i++) {
const x = Math.random() * Snow.canvasWidth;
const y = Math.random() * Snow.canvasHeight;
const size = getRandomRadius();
const speed = getRandomSpeed();
const dir = getRandomDir();
data.push({ x, y, size, speed, dir });
}
// Snow 객체에 데이터 저장
Snow.data = data;
},
- make() 함수는 눈 데이터를 만드는 함수입니다.
- 반복문을 돌려 시작 x, y 위치와 위에서 선언한 함수(크기, 속도, 방향)로 데이터를 만들어줍니다. ( 총 200개 )
move() {
Snow.data = Snow.data.map((item) => {
// 방향에 맞게 이동
item.x += item.dir * item.speed;
item.y += item.speed;
// 캔버스를 벗어났는지 판단
const isMinOverPositionX = -item.size > item.x;
const isMaxOverPositionX = item.x > Snow.canvasWidth;
const isOverPositionY = item.y > Snow.canvasHeight;
// 벗어나면 반대방향, 맨 위로
if (isMinOverPositionX || isMaxOverPositionX) {
item.dir *= -1;
}
if (isOverPositionY) {
item.y = -item.size;
}
return item;
});
},
- 데이터 반복문을 돌려서 각 아이템마다 가진 speed 만큼 더해서 누산 시켜줍니다.
- 이때 현재 x, y 위치 값이 캔버스를 벗어나면 반대방향으로 바꿔주거나 맨 위로 올려주는 조건을 거칩니다.
- 이후 Snow.Loop()가 반복되면서 speed 만큼 더해진 데이터를 다시 그려주게 됩니다.
draw() {
ctx.clearRect(0, 0, Snow.canvasWidth, Snow.canvasHeight);
ctx.fillStyle = "#0f1018";
ctx.fillRect(0, 0, Snow.canvasWidth, Snow.canvasHeight);
Snow.data.forEach((item) => {
ctx.beginPath();
ctx.fillStyle = "rgba(255, 255, 255, .6)";
ctx.arc(item.x, item.y, item.size, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
});
},
- 이전에 그려진 캔버스를 지워주고, 검은 배경으로 채워줍니다.
- move로 이동된 모든 눈 데이터를 ctx.arc() 메서드를 이용해서 원으로 그려줍니다.
ctx.arc( x, y, radius, startAngle, endAngle ) 인자를 필요로 하며, Math.PI * 2는 원의 총둘레입니다
loop() {
// 눈을 이동시킨 후, 그리기
Snow.move();
Snow.draw();
// Snow.loop() 함수를 계속 재귀함
window.requestAnimationFrame(Snow.loop);
},
- 필요한 함수를 다 제작했으니, 마지막으로 window.requestAnimationFrame()를 이용해서 재귀를 돌려줍니다.
- 눈 데이터를 이동시켜 준 후, 그려주는 순서로 함수를 호출해 줍니다.
https://developer.mozilla.org/ko/docs/Web/API/Window/requestAnimationFrame
const Snow = {
data: [],
init() {
Snow.make();
Snow.loop();
},
....
}
// 눈 그리기 시작
Snow.init();
- 마지막으로 처음에 세팅해 주는 Snow.init()에 데이터를 만들어주는 함수와 루프를 시작하는 함수를 호출해 줍니다.
- Snow.init() 코드로 실행해 주면 동작하게 됩니다.
창 크기에 맞추기
window.onresize = () => {
Snow.canvasWidth = $canvas.clientWidth;
Snow.canvasHeight = $canvas.clientHeight;
Snow.make();
};
창 크기가 변할 때마다, 캔버스 가로사이즈를 창 가로사이즈로 대체해 줍니다. 그 후 바뀐 크기에 맞는 눈 데이터를 생성해 줍니다.