requestAnimationFrame() - reflow, repaint

GY·2021년 12월 6일
3

Basic CS

목록 보기
12/28
post-thumbnail
post-custom-banner

🔸 reflow, repaint가 정확히 뭘까?

🔹 reflow

DOM reflow는 DOM이 화면에 표시되는 구조가 바뀌거나 CSS클래스가 변경될 때 일어난다.
즉, DOM트리가 배치되는 위치를 전체적으로 다시 계산해서 화면에 출력하는 것을 의미한다.

특정 요소의 위치가 변경되면 그에 영향을 받는 자식 요소들과 해당 요소 이후에 나타나는 요소들에 대해서 전체적으로 위치를 다시 계산해야 하기 때문에 DOM repaint보다 자원 소모가 크다.

🔹 repaint

DOM repaint 는 특정 요소의 visibility를 수정하거나 배경색, 글자색 등을 바꿀 때 일어난다.
위치가 아닌 화면에 표시되는 스타일만 바뀔 때 repaint가 일어난다.


🔸 이게 왜?

속성별로 reflow와 repaint가 일어나는 정도가 다른데, 이와 관련해 raf를 알아보려고 한다.

🔹 reflow 줄이기

cssText로 reflow가 한번만 실행되도록 하기

// bad
const body = document.body;
body.style.width = '50px';
body.style.height = '100px';
 
// good
const body = document.body;
body.style.cssText = 'width: 50px; heigh: 100px;';


💎 requestAnimationFrame

css의 transition으로 처리하기 어려운 애니메이션을 구현하기 위해서 사용할 수 있는 함수
web API로, 이 메서드에 콜백으로 전달된 메서드는 브라우저가 다음에 렌더링을 하기 전에 실행이 되는 것을 보장한다.
브라우저가 화면을 업데이트 하기 전에 전달받은 콜백함수를 수행하는 것이다.

🔸 장점

🔹 최대 1ms(1/1000s)로 제한되며 1초에 60번 동작

사람의 눈에 가장 부드러운 애니메이션처럼 보이기 위해서는 1초에 60프레임을 보여주어야 한다.
사람이 1초에 볼 수 있는 프레임의 최대 갯수가 60개이기 때문이다. 그 이상 부터는 더 많은 프레임을 보여주더라도 사람의 눈에는 차이가 크게 보이지 않는다.

반대로 말하면, 1프레임을 보여주는데 60프레임을 1초로 나눈 16.6ms이하를 사용해야 자연스러운 애니메이션이 구현되는 것이다.

즉 RAF를 사용하면 60번의 호출만 발생하여 최적화된 속도로 부드러운 애니메이션을 표현하면서 성능은 최대한 확보할 수 있다.

setInterval이나 setTimeout은 할 수 없을까?

  1. setInterval이나 setTimeout은 프레임을 신경쓰지 않고 동작한다. 만약 애니메이션 코드가 복잡해서 실행하는데 50ms가 걸린다면, 16.6ms동안 프레임을 찍어내지 못했기 때문에 화면이 뚝뚝 끊기는 듯한 현상이 발생한다.

  2. 자바스크립트는 싱글 스레드 언어이기 때문에, 한 번에 하나의 작업만 할 수 있다. 여러가지의 무거운 작업을 자바스크립트가 진행하게 되면 그 작업을 처리하느라 화면을 제때 렌더링하지 못한다. 작업을 실행한 다음 화면을 렌더링해야 하는데, 이 작업이 20ms동안 실행된다면 브라우저가 화면을 그려봤자 프레임이 누락되어 뚝뚝 끊기는 느낌이 나게 된다.

보통은 자주 호출되는 이벤트 핸들러는 보통 3~4ms정도로 실행을 마치도록 해야 자바스크립트 실행 이후 리플로우 과정까지 총 16ms내에 프레임을 찍어낼 수 있게 된다.

예시로 한번 더 자세히 살펴보자.

var el = document.querySelector('#움직일-요소');
var left = 0;
 
function frame() {
    left += 0.1;
    el.style.setProperty('transform', 'translateX(' + left + 'px)');
 
    if (left < 200) window.requestAnimationFrame(frame);
}
 
window.requestAnimationFrame(frame);
  • 지나치게 많은 함수 호출 횟수
    raf는 1초에 60번 위 함수를 실행한다.
    하지만 frame()을 for문으로 실행하면 1초에 1000번 정도 실행될 것이다.
    일반적으로 for문이 한번 반복문을 돌때 1밀리초 내외가 소요되기 때문이다.
  • 애니메이션 효과
    게다가 브라우저는 대부분 for문과 쌓인 콜스택이 비워진 후에야 렌더링 작업을 수행하므로 애니메이션처럼 순차적으로 이동하지 않고 한 번에 200픽셀을 이동하는 것으로 보일 것이다.

🔹 비교적 정확한 타이밍에 호출

다수의 애니메이션에도 각각 타이머 값을 생성 및 참조하지 않고 내부의 동일한 타이머 참조
즉, 정확한 타이밍에 호출
setInterval 함수는 반복시간이 짧아질 경우 실제 시간보다 느린 반응 속도를 보인다.
requestAnimationFrame은 재귀호출을 통해 함수호출을 반복하여 setInterval함수처럼 사용할 수 있는데, 이것은 setinterval함수 보다 정확한 타이밍에 호출된다.
브라우저가 16.6ms만에 프레임을 찍어낼 여유가 생겼을 때마다 애니메이션 코드가 실행되어 화면을 렌더링해 보여주기 때문이다.

requestAnimationFrame(function _name(value) {
    // code
    requestAnimationFrame(_name);
});
//[출처] [자바스크립트] requestAnimationFrame 알아보기|작성자 Scripter

🔹 백그라운드 동작 및 비활성화시 중지(성능 최적화)

setTImeout함수를 사용해 애니메이션을 구현했다고 가정하자.
setTimeout은 브라우저 창이 숨겨지거나 최소화되어 보이지 않거나 사용할 수 없는 상태일때도 애니메이션 작업을 동일하게 수행한다. 따라서 CPU 리소스와 배터리 수명은 계속해서 낭비된다.

반면, requestAnimationFrame은 페이지가 비활성 상태이면 페이지의 화면 그리기 작업도 브라우저에 의해 일시 중지 된다. 페이지가 다시 활성화될 때 마지막으로 머물렀던 곳에서 계속 실행하여 CPU 오버 헤드를 효과적으로 절약하고 성능과 배터리 수명을 개선할 수 있다.



사용법 / 유의사항

window.requestAnimationFrame은 비동기 함수인데, 다른 비동기 함수와 차이점이 있다.

  • setTImeout과 달리 브라우저가 실행시점을 결정한다.
  • setInterval과 달리 스스로 반복해서 호출하지 않아 재귀호출이 필요하다.
requestAnimationFrame(function _name(value) {
    // code
    requestAnimationFrame(_name);
});
//[출처] [자바스크립트] requestAnimationFrame 알아보기|작성자 Scripter

모니터의 주사율과 맞춰서 실행주기가 결정된다

기본적으로는 1초에 60번, 보통은 모니터의 주사율에 맞추어 함수를 실행하는데, 만약 모니터가 평균 주사율이 60FPS라면 1초에 60번, 140FPS라면 1초에 140번 프레임 함수를 실행한다.

Reference

profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.
post-custom-banner

0개의 댓글