반응형

제이쿼리에서는 js로 애니메이션 효과를 구현하려면 편하게 .animate 메소드를 사용할 수 있었지만

바닐라 js로 애니메이션을 구현하려고 하면 비교적 복잡한 코드로 구현이 필요하다는걸 알 수 있다.

 

애니메이션 작동 원리를 생각해보면 일정한 시간 간격으로 특정 위치로 이동하게 되는 소스를 구현해야 하는데 일정 시간을 반복하는걸 생각해보면 setInterval이나 setTimeout 를 생각할 수 있는데 두 가지 방법 보다는 requestAnimationFrame이 브라우저 측면에서는 훨씬 효율적이다.

 

1. requestAnimationFrame 란?

requestAnimationFrame는 (줄여서 RAF라고 부른다.) 브라우저에서 제공하는 메소드로 애니메이션과 같이 반복적인 작업을 수행할 때 사용한다. 

requestAnimationFrame의 특징을 설명하기 위해서는 setInterval, setTimeout 함수와 비교를 해보면 쉽게 알 수 있다.

setInterval / setTimeout requestAnimationFrame (RAF)
타이머의 주기가 정확하지 않기 때문에 애니메이션이 일정하지 않게 보이거나 끊겨 보일 수 있다. 브라우저의 리페인트 주기에 맞게 콜백 함수를 예약하기 때문에 브라우저가 화면을 갱신할 때에 맞춰 애니메이션을 실행한다. 따라서 부드러운 애니메이션을 볼 수 있다.
고정된 타이머 주기를 사용해서 타이머 기능이 필요하지 않더라도 불필요하게 작업이 되거나 배터리 소모가 된다. 브라우저에 최적화하여 애니메이션을 처리하기 때문에 사용자 디바이스의 사양이나 배터리 수명 등을 최적화 하여 실행한다.
브라우저가 비활성된 상태에서 혹은 백그라운드에서 실행을 멈추지 않고 계속해서 작업을 실행하기 때문에 불필요한 리소스를 소비하게 된다. 브라우저가 비활성된 상태이거나 백그라운드에서 실행을 할 때 애니메이션 처리를 조정하여 성능을 최적화한다. 
정해진 시간 간격에 따라 비동기적으로 작업을 예약하고 실행하기 때문에 다른 브라우저 작업과 동기화가 보장되지 않을 수 있다. 브라우저의 리페인트 주기에 맞게 콜백 함수를 실행하기 때문에 다른 브라우저 작업과 동기화 되어 실행한다.

 

그리고 

requestAnimationFrame은 초가 60프레임 정도를 구현한다.

(브라우저 상황에 따라 다를 수 있다. 하지만 최대한 구현하려한다.)

 

2. requestAnimationFrame 사용

기본적인 사용은 아래와 같다.

const callBackFnc = (timeStamp) => {
	console.log(timeStamp);  // timeStamp 값이 시간에 따라 계속 증가한다.
}

// requestAnimationFrame(콜백함수);
requestAnimationFrame(callBackFnc);

 

requestAnimationFrame(콜백함수) 로 사용하는데

이때 사용하는 콜백함수는 첫 번째 파라미터 값으로 타임 스탬프를 가져온다.

그리고 그 타임 스탬프는 시간의 경과에 따라 증가한다.

 

 

requestAnimationFrame의 간단한 사용 예시를 보자면

const testBtn = document.querySelector('.btn_test');
let testDuration = 0;

const scrollEvt = () => {
  testDuration += 50;
  const rafScroll = requestAnimationFrame(scrollEvt);
	
  window.scrollTo(0, testDuration);
  if(testDuration > 500){
    cancelAnimationFrame(rafScroll);
    testDuration = 0;
  }
}
testBtn.addEventListener('click', scrollEvt);

 

위 코드는 실무에서 쓰일 일은 없는 소스지만 간단하게 보자면

testBtn 버튼을 누르면 브라우저의 스크롤이 내가 지정한 위치만큼 이동하게 된다.

 

window.scrollTo(0, 원하는 위치);

로 지정을 하는데 testDuration이라는 반복되는 값을 만들어서 requestAnimationFrame 함수가 실행 될 때 마다

50씩 증가를 시켜 스크롤 위치에 변화를 준다.

 

그리고 증가하는 testDuration 변수 값이 500이 넘어가게 되면 requestAnimationFrame 함수의 반대 성격의  cancelAnimationframe() 함수를 사용해서 애니메이션을 멈출 수 있다.

 

(버튼 클릭 -> 50씩 스크롤 위치 증가 -> 증가된 값이 500이 넘어가면 멈춤)

 

 

3. requestAnimationFrame 활용

실무에서 쓸법한 내용으로 예시를 들자면

const duration = 500;  // 애니메이션 속도 조정
const moveEtc = (target) => {
  const targetEl = document.querySelector(target);	// 함수를 실행할 때 목적지 엘리먼트를 받아옴

  if(targetEl){
    let start;
    const targetPosition = targetEl.getBoundingClientRect().top;  // 파라미터로 받아온 target의 위치값 구하기
    const startPosition = window.scrollY;	// 애니메이션 이벤트가 발생했을 때의 현재 위치 값
    const distance = targetPosition;
    const easings = {	// 애니메이션의 easing 효과
      linear(t) {
        return t;
      },
      easeInQuad(t) {
        return t * t;
      },
      easeOutQuad(t) {
        return t * (2 - t);
      },
      easeInOutQuad(t) {
        return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      },
      easeInCubic(t) {
        return t * t * t;
      },
      easeOutCubic(t) {
        return (--t) * t * t + 1;
      },
      easeInOutCubic(t) {
        return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
      },
      easeInQuart(t) {
        return t * t * t * t;
      },
      easeOutQuart(t) {
        return 1 - (--t) * t * t * t;
      },
      easeInOutQuart(t) {
        return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
      },
      easeInQuint(t) {
        return t * t * t * t * t;
      },
      easeOutQuint(t) {
        return 1 + (--t) * t * t * t * t;
      },
      easeInOutQuint(t) {
        return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
      }
    };
    const moveScroll = function(timeStamp){  // requestAnimationFrame를 적용할 함수, 첫 번째 파라미터로 타임 스탬프 받아오기
      if(start === undefined){
        start = timeStamp;  // requestAnimationFrame 함수의 타임스탬프를 기준으로 시작 시간을 지정
      }

      const elapsed = timeStamp - start;  // 시작 시간, 타임 스탬프를 이용해서 애니메이션 경과 시간을 구한다.
      const targetPos = startPosition + distance * easings.linear(elapsed / duration);  // 현재 위치, 목표 위치, easing 등으로 최종 목적지까지의 애니메이션 형태를 구성
      window.scrollTo(0, targetPos);

      if(elapsed <= duration){
        requestAnimationFrame(moveScroll);
      }else if(timeStamp >= elapsed){
        // 애니메이션 종료 후
        const targetPosFix = window.scrollY + targetEl.getBoundingClientRect().top;
        window.scrollTo(0, targetPosFix);
      }
    };
    requestAnimationFrame(moveScroll);
  }
}

 

위와 같이 작성할 수 있다.

requestAnimationFrame(콜백함수)의 콜백함수는 첫 번째 파라미터로 받아온 타임 스탬프 값(timeStamp)을 이용해서 애니메이션 시작점, 목적지, 애니메이션의 경과 시간 등을 계산한다.

 

 

 


참고

https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame

https://codepen.io/pawelgrzybek/pen/ZeomJB

https://velog.io/@y_jem/react-%EC%8A%AC%EB%A1%AF-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EA%B8%B0%EB%8A%A5

 

 

 

반응형

+ Recent posts