DOM reflow는 DOM이 화면에 표시되는 구조가 바뀌거나 CSS클래스가 변경될 때 일어난다.
즉, DOM트리가 배치되는 위치를 전체적으로 다시 계산해서 화면에 출력하는 것을 의미한다.
특정 요소의 위치가 변경되면 그에 영향을 받는 자식 요소들과 해당 요소 이후에 나타나는 요소들에 대해서 전체적으로 위치를 다시 계산해야 하기 때문에 DOM repaint보다 자원 소모가 크다.
DOM repaint 는 특정 요소의 visibility를 수정하거나 배경색, 글자색 등을 바꿀 때 일어난다.
위치가 아닌 화면에 표시되는 스타일만 바뀔 때 repaint가 일어난다.
속성별로 reflow와 repaint가 일어나는 정도가 다른데, 이와 관련해 raf를 알아보려고 한다.
// 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;';
css의 transition으로 처리하기 어려운 애니메이션을 구현하기 위해서 사용할 수 있는 함수
web API로, 이 메서드에 콜백으로 전달된 메서드는 브라우저가 다음에 렌더링을 하기 전에 실행이 되는 것을 보장한다.
브라우저가 화면을 업데이트 하기 전에 전달받은 콜백함수를 수행하는 것이다.
사람의 눈에 가장 부드러운 애니메이션처럼 보이기 위해서는 1초에 60프레임을 보여주어야 한다.
사람이 1초에 볼 수 있는 프레임의 최대 갯수가 60개이기 때문이다. 그 이상 부터는 더 많은 프레임을 보여주더라도 사람의 눈에는 차이가 크게 보이지 않는다.
반대로 말하면, 1프레임을 보여주는데 60프레임을 1초로 나눈 16.6ms이하를 사용해야 자연스러운 애니메이션이 구현되는 것이다.
즉 RAF를 사용하면 60번의 호출만 발생하여 최적화된 속도로 부드러운 애니메이션을 표현하면서 성능은 최대한 확보할 수 있다.
setInterval이나 setTimeout은 할 수 없을까?
setInterval이나 setTimeout은 프레임을 신경쓰지 않고 동작한다. 만약 애니메이션 코드가 복잡해서 실행하는데 50ms가 걸린다면, 16.6ms동안 프레임을 찍어내지 못했기 때문에 화면이 뚝뚝 끊기는 듯한 현상이 발생한다.
자바스크립트는 싱글 스레드 언어이기 때문에, 한 번에 하나의 작업만 할 수 있다. 여러가지의 무거운 작업을 자바스크립트가 진행하게 되면 그 작업을 처리하느라 화면을 제때 렌더링하지 못한다. 작업을 실행한 다음 화면을 렌더링해야 하는데, 이 작업이 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);
다수의 애니메이션에도 각각 타이머 값을 생성 및 참조하지 않고 내부의 동일한 타이머 참조
즉, 정확한 타이밍에 호출
setInterval 함수는 반복시간이 짧아질 경우 실제 시간보다 느린 반응 속도를 보인다.
requestAnimationFrame은 재귀호출을 통해 함수호출을 반복하여 setInterval함수처럼 사용할 수 있는데, 이것은 setinterval함수 보다 정확한 타이밍에 호출된다.
브라우저가 16.6ms만에 프레임을 찍어낼 여유가 생겼을 때마다 애니메이션 코드가 실행되어 화면을 렌더링해 보여주기 때문이다.
requestAnimationFrame(function _name(value) {
// code
requestAnimationFrame(_name);
});
//[출처] [자바스크립트] requestAnimationFrame 알아보기|작성자 Scripter
setTImeout함수를 사용해 애니메이션을 구현했다고 가정하자.
setTimeout은 브라우저 창이 숨겨지거나 최소화되어 보이지 않거나 사용할 수 없는 상태일때도 애니메이션 작업을 동일하게 수행한다. 따라서 CPU 리소스와 배터리 수명은 계속해서 낭비된다.
반면, requestAnimationFrame은 페이지가 비활성 상태이면 페이지의 화면 그리기 작업도 브라우저에 의해 일시 중지 된다. 페이지가 다시 활성화될 때 마지막으로 머물렀던 곳에서 계속 실행하여 CPU 오버 헤드를 효과적으로 절약하고 성능과 배터리 수명을 개선할 수 있다.
window.requestAnimationFrame은 비동기 함수인데, 다른 비동기 함수와 차이점이 있다.
requestAnimationFrame(function _name(value) {
// code
requestAnimationFrame(_name);
});
//[출처] [자바스크립트] requestAnimationFrame 알아보기|작성자 Scripter
기본적으로는 1초에 60번, 보통은 모니터의 주사율에 맞추어 함수를 실행하는데, 만약 모니터가 평균 주사율이 60FPS라면 1초에 60번, 140FPS라면 1초에 140번 프레임 함수를 실행한다.