barcode

눈 내리는 효과 만들기

Coding/JavaScript

Reference

https://gurtn.tistory.com/195

 

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

완성 코드 See the Pen Canvas Snow by hyukson (@hyukson) on CodePen. 전체코드(보기) 더보기 const $canvas = document.querySelector("canvas"); const ctx = $canvas.getContext("2d"); const getRandomRadius = () => Math.random() * 1 + 0.5; const getR

gurtn.tistory.com


사실 여기서 HTML이랑 CSS는 별 거 없다. 진짜로 별 거 없다.

 

<html>
    <head>
        <title>Let it snow</title>
        <link href="style.css" rel="stylesheet">
    </head>
    <body>
        <canvas width="1920" height='1080' />
    </body>
    <script src="script.js"></script>
</html>

이게 HTML이고

 

html {
    margin:0;
    padding:0;
}

body {
    margin:0;
    padding:0;
    overflow:hidden;
}

이게 CSS다. 진짜로 이게 다다.

 

그렇다는건 뭐다? 그죠 자바스크립트가 본론입니다. 일단 차근차근 따라가보면 어렵지 않은데 코드가 길어서 아 안돼 할 수는 있음... 근데 부분부분 뜯어보면 대충 이게 뭐 하는 함수인지 이해는 갈 것이다.


const $canvas = document.querySelector("canvas");
const ctx = $canvas.getContext("2d");

const getRandomRadius = () => Math.random() * 2 + 0.5;
const getRandomspeed = () => Math.random() * 0.3 + 0.5;
const getRandomDir = () => [-1, 1][Math.floor(Math.random() * 2)];

여기서 위의 두 줄은 캔버스(HTML에 캔버스가 또 있다... 뭔지는 모름)를 가져오는거고 아래 세 줄은 내릴 눈의 크기, 속도, 방향을 결정하는 함수다.

 

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 = [];

        for (let i = 0; i < 250; 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.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() {
        //CSS에 배경색 설정이 따로 안됐던게 이것때문이었고만? 
        ctx.clearRect(0, 0, Snow.canvasWidth, Snow.canvasHeight);
        ctx.fillStyle = '#010101';
        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 함수. 여기에 눈을 뿌리기 위한 모든 요소가 들어간다. 일단 다섯개의 함수로 구성되어 있는데, 위쪽부터 순서대로 1) 눈을 만드는(init) 함수(얘는 뭐 하는건 없고 init() 호출하면 make랑 loop가 세트로 소환된다), 2) 재귀 돌리는 함수, 3) 눈 데이터를 만드는 함수(저기 for문 안에 250을 바꾸면 눈 갯수가 바뀐다), 4) 눈이 제대로 이동하게 해 주는 함수, 5) 배경 채우고 눈을 원형으로 만들어주는 함수 이렇게 다섯개가 있다.

 

보면 위에 두개는 단촐한데 밑에 세개는 어우... 뭐가 많다 그죠? 일단 make는 말 그대로 눈 데이터를 만드는 함수이다. 저기 for문에 있는 250을 다른 숫자로 바꾸면 눈 개수가 변화하는데 한 1000정도 넣어두면 저기 어디 시베리아 평원에서 흩날리는 눈폭풍을 볼 수 있다. 눈의 크기, 방향, 속도는 위에 짜 둔 함수가 랜덤으로 정해주는데 이 함수의 수치를 조절하면 눈의 크기, 속도, 방향도 제어가 가능하다.

 

move는 눈이 제대로 이동하게 해 주는 함수인데, 이게 뭔 소리냐면 눈이 내리다보면 화면 밖으로 나가게 된다. 그러면 그대로 빠이짜이찌엔 안녕히계세요 여러분 하는 게 아니라 방향을 잡아주는 역할을 하는 함수이다. 나도 처음에 보고 뭔소린가 했는데 코딩하면서 설명 보고 납득함.

 

draw에서 볼 것은 fillstyle와 arc이다. fillstyle은 배경색을 채워주는 것이고, 그래서 어두운 배경에 눈이 흩날리고 있음에도 CSS에는 아무것도 없이 오버플로우 숨기라는 것만 되어 있었다. arc은... 이게 있어야 눈이 둥글어진다.

 

window.onresize = () => {
    Snow.canvasWidth = $canvas.clientWidth;
    Snow.canvasHeight = $canvas.clientHeight;

    Snow.make();
};

이쪽은 창 크기가 바뀌면 그 크기에 맞춰서 눈 내리는 저기를 다시 정해주는 함수인 듯... 나도 모르것다.