눈 내리기 효과

백승일·2021년 8월 3일
0
import { useCallback, useEffect, useState } from 'react';

interface snowData {
  x:number, // 눈의 x좌표
  y:number, // 눈의 y좌표
  speedOfFall:number, // 눈의 낙하 속도
  speedOfWind:number, // 눈발의 속도
  step:number // 바람의 방향
} //눈이 가지는 정보

카운터로 받는 숫자 만큼 눈을 만드는 커스텀 훅

const useSnowData  = (count:number):[snowData[]] => {
  const [snowDatas,setSnowData] = useState<snowData[]>([]); //눈들의 배열
  const newSnowData = useCallback((datas:snowData[])=>datas.map(data=>{
    let x = data.x + Math.cos(data.speedOfWind);
    let y = data.y + data.speedOfWind;
    let speedOfWind = data.speedOfWind;
    let speedOfFall = data.speedOfFall;
    const step = data.step;
    if(y >= window.innerHeight-60){
      y =  0;
      x = Math.floor(Math.random()*window.innerWidth);
      speedOfFall = Math.random()*2+2;
      speedOfWind = 1;
    }
    if(x > window.innerWidth-50){
      x = window.innerWidth -50;
    }else if(x < 0){
      x = 50;
      speedOfWind = speedOfWind + data.step
    }
    return {x,y,speedOfFall,speedOfWind,step}
  }),[]);
  // 눈들의 위치와 바람을 바꿔주는 함수 
  
  const setSnowPosition =()=>{
    setSnowData(state=>newSnowData(state));
  };// 바뀐 정보를 적용하는 함수

  useEffect(()=>{
    const datas:snowData[] = [];
    for(let i =0; i<count; i++){
      const data = {
        x:Math.floor(Math.random()*window.innerWidth - 10),
        y:Math.floor(Math.random()*window.innerHeight + 10),
        speedOfFall:Math.random()*2 + 2,
        speedOfWind:1,
        step:Math.random()*0.1 + 0.05
      };
      datas.push(data);
    }
    setSnowData(()=>datas);
  },[]); 
  // useEffect를 사용해서 count의 수 만큼 snowData생성, 랜더링을 줄이기 위해 
  // 데이터를 다 만들고 한번에 넣기

  useEffect(()=>{
    const fall = setInterval(setSnowPosition,50)
    return ()=>{
      clearInterval(fall);
    }
  },[]);
  // setInterval로 매 초 상태 업데이트, 화면이 내려가면 clearInterval
  return [snowDatas]
}

export default useSnowData;

눈 그리기

import React from 'react';
import useSnowData from "../hooks/useSnowData";

// 눈의 색상은 props로 받음, useSnowData 훅으로 눈들의 데이터를 가져오고 적용

const Snows = ({color}:{color:string})=>{
  const [snows] = useSnowData(30);
  return <>{snows.map((data,idx)=>(<div key={idx} style={{
    position:"absolute",
    fontSize:"35px",
    color:`${color}`,
    left:`${data.x}px`,
    top:`${data.y}px`
  }} >*</div>))}</>
}

export default React.memo(Snows)

직면한 상황들
1. 랜더링이 너무 많이 일어나니 useRef를 이용해보자 -> useRef는 랜더링 자체가 안일어남 ->useState로 진행
2. 인라인 스타일보단 styled-component를 사용해보자 -> 잦은 랜더링 때문인지 styled-component에서 warning(over 200 classs)을 보내고, 또 성능또한 악화됨 -> 인라인 스타일로 진행

profile
뉴비 개발자

0개의 댓글