오늘은 내일 배움 캠프 자기소개서 페이지 제작 중, 적용한 파티클 만드는 방법에 대해서 리뷰를 하고자 한다.
<script>
const canvas = document.getElementById('particlesCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let animationStarted = false; // 애니메이션 시작 확인 변수
let particles = []; // 파티클 담는 배열 (애니메이션 중 움직이는 여러 개의 파티클 객체들이 저장)
..
- id가 particlesCanvas인 <canvas> 요소를 선택해서 저장한다.
- canvas 메서드인 getContext('2d') : <canvas> 요소에서 2D 그래픽을 그릴 수 있는 컨텍스트(context)를 반환한다.
- canvas 너비를 현재 브라우저 창의 너비(window.innerwidth)로 설정한다.
- canvas 높이를 현재 브라우저 창의 높이(window.innerheight)로 설정한다.
function createParticles(x, y) { // x, y 위치를 기준으로 입자(particle)들을 생성하는 함수
for (let i = 0; i < 30; i++) {
particles.push({
x,
y,
radius: Math.random() * 20 + 10,
emoji: '❤️',
speedX: (Math.random() - 0.5) * 4,
speedY: (Math.random() - 0.5) * 4,
life: 100
});
}
}
- 반복문 30번 반복 : 30개의 파티클을 생성하기 위한 반복문이다.
- particles.push({}) : 파티클을 저장하는 배열에 객체({key:value} 형태) 를 추가하였다.
- 파티클 객체는 다음과 같은 정보를 같는다.
- x(x : x 와 동일하다. key와 value 값이 동일할 때 x로 줄여서 사용.) → 파티클 시작 위치 중 x 값 저장
- y(위와 동일한 이유) → 파티클 시작 위치 중 y 값 저장
- radius : Math.radnom() * 20 + 10 → 파티클의 크기를 설정. (10~30 사이의 값이 랜덤으로 결정)
- emoji : '❤️' → 입자에 표시될 이모지이다.
- speedX, Y : (Math.random() - 0.5) * 4 → 파티클이 움직이는 속도. (-2~2의 속도를 갖는다. = 상하좌우 랜덤한 방향으로 퍼진다.)
- life: 100 → 파티클의 수명. (수명을 줄여 입자가 점점 사라지게 관리할 수 있다.)
let lastScrollY = window.scrollY;
function updateParticles() { // 입자들을 매 프레임마다 업데이트하고 다시 그리는 함수
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles = particles.filter(p => p.life > 0);
const scrollOffsetY = window.scrollY - lastScrollY;
particles.forEach(p => {
// 위치 업데이트
p.x -= p.speedX;
p.y -= p.speedY + scrollOffsetY; // 스크롤 보정!
p.life--;
ctx.font = `${p.radius}px serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(p.emoji, p.x, p.y);
});
lastScrollY = window.scrollY;
requestAnimationFrame(updateParticles);
}
- let lastScrollY = window.scrollY → 이전 프레임의 스크롤 위치를 저장한다.
- ctx.clearRect(0, 0, canvas.width, canvas.height) → 이전 프레임에 그려진 그래픽을 모두 지운다.
- particles = particles.filter(p => p.life > 0) → life가 0보다 큰 입자만 남긴다. 수명이 다된 입자들은 배열에서 제거
- const scrollOffsetY = window.scrollY - lastScrollY → 현재 스크롤 위치와 이전 스크롤 위치의 차이를 구한다.
- particles.forEach(p => { → 남아 있는 모든 입자들에 대해 하나씩 처리한다. (비용이 만만치 않을 예정이라는 것이다.)
- p.x -= p.speedX → 입자의 x 좌표를 속도만큼 이동시킨다. 왼쪽 또는 오른쪽으로 움직인다.
- p.y -= p.speedY + scrollOffsetY; → y좌표도 속도만큼 이동시키고, 여기에 스크롤 변화량도 더한다. (스크롤 시 보정을 하지 않으면, 파티클이 버튼을 눌렀던 위치가 아니라 스크롤된 화면 위치에 따라 움직이게 된다.)
- p.life-- → 입자의 수명을 하나 줄인다.
- ctx.font = `${p.radius}px serif → 이모지를 그릴 글꼴 크기를 입자의 radius 값으로 설정 (serif는 글꼴 종류인데, 이모지는 이 글꼴에서 잘 작동한다.)
- ctx.textAlign = 'center' → 이모지가 입자의 중심에 위치하도록 정렬 기준을 설정
- ctx.textBaseline = 'middle' → 이모지가 입자의 중심에 위치하도록 정렬 기준을 설정
- ctx.fillText(p.emoji, p.x, p.y) → 각 입자에 지정된 emoji 를 (p.x, p.y) 위치에 랜더링한다.
- lastScrollY = window.scrollY → 현재 스크롤 위치를 저장하여 다음 프레임에서 스크롤 차이를 계산할 수 있도록 한다.
- requestAnimationFrame(updateParticles) → 비동기적 재귀루프를 통해, 본인을 계속 호출하여 애니메이션 처럼 보이게 한다.
document.querySelectorAll('.imgs button').forEach(button => {
button.addEventListener('click', (e) => {
const rect = e.target.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
createParticles(x, y);
if (!animationStarted) {
updateParticles();
animationStarted = true;
}
});
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
</script>
다음과 같이 버튼을 누르면 사방으로 재각기 하트들이 파티클처럼 퍼지는 것을 볼 수 있다.
'웹 기초' 카테고리의 다른 글
[JavaScript] 브라우저 Cookie 사용 방법 (0) | 2025.04.11 |
---|---|
[Visual Studio Code Extensions] Live Server란? (0) | 2025.04.10 |
[FireBase] FireBase CORS 오류 및 해결 방법 (0) | 2025.04.08 |
[내일 배움 캠프, 웹 기초] html, <script> 태그의 위치 (0) | 2025.03.31 |
[내일 배움 캠프, 웹 기초] URL이란? (0) | 2025.03.31 |