JavaScript를 이용하여 눈 내리는 효과를 만들어보려고 한다!
내가 구현하려는 효과의 경우 눈송이에 짧은 메시지가 쓰여있고, 눈송이가 겹쳐서 메시지가 가려지지 않도록 해야 했다.
document.addEventListener("DOMContentLoaded", () => {
const snowflakeWrapper = document.querySelector(".snowflake-wrapper");
const snowflake = document.querySelectorAll(".snowflake");
snowflake.forEach(snow => {
const randomX = Math.random() * (snowflakeWrapper.offsetWidth - snow.offsetWidth);
const duration = Math.random() * 20 + 10; // 10s ~ 30s
const delay = Math.random() * -30;
snow.style.setProperty("--random-x", randomX);
snow.style.animation = `snowAnimation ${duration}s linear infinite`;
snow.style.animationDelay = `${delay}s`;
});
});
초기에 작성한 코드는 눈송이들의 위치가 완전 랜덤하다보니, 눈송이끼리 겹쳐 메시지를 보기가 쉽지 않았다.
눈송이들이 차지할 수 있는 총 칸의 갯수를 계산한 뒤 눈송이들이 각각의 칸에 위치하여 서로 겹치지 않도록 수정했다.
document.addEventListener("DOMContentLoaded", () => {
const snowflakeWrapper = document.querySelector(".snowflake-wrapper");
const snowflake = document.querySelectorAll(".snowflake");
const snowflakeWidth = 100; // 눈송이 너비
snowflake.forEach(snow => {
const totalIndex = Math.floor(snowflakeWrapper.offsetWidth / snowflakeWidth); // 눈송이들이 차지할 수 있는 총 칸 갯수
const randomIndex = Math.floor(Math.random() * totalIndex);
const randomX = randomIndex * snowflakeWidth; // 칸 번호 * 눈송이 너비
const duration = Math.random() * 20 + 10; // 10s ~ 30s
const dealy = Math.random() * -30;
snow.style.setProperty("--random-x", randomX);
snow.style.animation = `snowAnimation ${duration}s linear infinite`;
snow.style.animationDelay = `${dealy}s`;
});
});
하지만 위와 같이 수정하니, 랜덤 숫자가 연속으로 동일하게 나오는 경우에는 여전히 겹침 현상이 발생했다.
이전 index와 현재 index가 같을 경우 현재 index에 +1을 추가해주는 코드를 추가했다.
document.addEventListener("DOMContentLoaded", () => {
const snowflakeWrapper = document.querySelector(".snowflake-wrapper");
const snowflake = document.querySelectorAll(".snowflake");
const snowflakeWidth = 100; // 눈송이 너비
let prevIndex = -1;
snowflake.forEach(snow => {
const totalIndex = Math.floor(snowflakeWrapper.offsetWidth / snowflakeWidth); // 눈송이들이 차지할 수 있는 총 칸 갯수
let randomIndex;
randomIndex = Math.floor(Math.random() * totalIndex);
if(randomIndex === prevIndex) {
randomIndex = (randomIndex + 1) % totalIndex;
}
prevIndex = randomIndex;
const randomX = randomIndex * snowflakeWidth; // 칸 번호 * 눈송이 너비
const duration = Math.random() * 20 + 10; // 10s ~ 30s
const dealy = Math.random() * -30;
snow.style.setProperty("--random-x", randomX);
snow.style.animation = `snowAnimation ${duration}s linear infinite`;
snow.style.animationDelay = `${dealy}s`;
});
});
좀 더 자연스러움을 주기 위해 눈송이들이 칸의 정중앙에 위치하는 것이 아닌 좀 더 다양한 위치에 눈송이가 나타나도록 오프셋을 추가하였다.
document.addEventListener("DOMContentLoaded", () => {
const snowflakeWrapper = document.querySelector(".snowflake-wrapper");
const snowflake = document.querySelectorAll(".snowflake");
const snowflakeWidth = 100; // 눈송이 너비
let prevIndex = -1;
snowflake.forEach(snow => {
const totalIndex = Math.floor(snowflakeWrapper.offsetWidth / snowflakeWidth); // 눈송이들이 차지할 수 있는 총 칸 갯수
let randomIndex;
randomIndex = Math.floor(Math.random() * totalIndex);
if(randomIndex === prevIndex) {
randomIndex = (randomIndex + 1) % totalIndex;
}
prevIndex = randomIndex;
const randomOffset = Math.random() * (snowflakeWidth * 0.2) - (snowflakeWidth * 0.1); // -10% ~ +10% 범위의 오프셋 추가
const randomX = (randomIndex * snowflakeWidth) + randomOffset; // 칸 번호 * 눈송이 너비 + 오프셋
const duration = Math.random() * 20 + 10; // 10s ~ 30s
const dealy = Math.random() * -30;
snow.style.setProperty("--random-x", randomX);
snow.style.animation = `snowAnimation ${duration}s linear infinite`;
snow.style.animationDelay = `${dealy}s`;
});
});