[JavaScript] 눈 내리는 효과 만들기

Carrie·2024년 11월 19일
0
post-thumbnail

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`;
    });
});

초기에 작성한 코드는 눈송이들의 위치가 완전 랜덤하다보니, 눈송이끼리 겹쳐 메시지를 보기가 쉽지 않았다.

✍️ 수정1

눈송이들이 차지할 수 있는 총 칸의 갯수를 계산한 뒤 눈송이들이 각각의 칸에 위치하여 서로 겹치지 않도록 수정했다.

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`;
    });
});

하지만 위와 같이 수정하니, 랜덤 숫자가 연속으로 동일하게 나오는 경우에는 여전히 겹침 현상이 발생했다.

✍️ 수정2

이전 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`;
    });
});


📌 참고
https://2minmin2.tistory.com/51

profile
Markup Developer🧑‍💻

0개의 댓글