requestAnimationFrame API - 이제 1밀리초 미만의 정밀도 지원

Ilmari Heikkinen

requestAnimationFrame를 사용해 왔다면 페인트가 화면의 새로고침 속도에 동기화되어 최대한 높은 충실도의 애니메이션을 볼 수 있었습니다. 또한 사용자가 다른 탭으로 전환할 때 CPU 팬 소음과 배터리 전력을 절약할 수 있습니다.

하지만 API의 일부가 변경될 예정입니다. 콜백 함수에 전달되는 타임스탬프가 일반적인 Date.now() 유형의 타임스탬프에서 페이지가 열렸을 때부터의 부동 소수점 밀리초의 고해상도 측정값으로 변경됩니다. 이 값을 사용하는 경우 아래 설명에 따라 코드를 업데이트해야 합니다.

명확히 말씀드리자면 다음과 같은 내용입니다.

// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
    // the value of timestamp is changing
});

여기에 제공된 일반적인 requestAnimFrame shim을 사용하는 경우 타임스탬프 값을 사용하지 않는 것입니다. 걱정하지 마세요. :)

이유

왜냐하면 rAF를 사용하면 이상적인 60fps를 얻을 수 있으며 60fps는 프레임당 16.7ms로 변환됩니다. 하지만 정수 밀리초로 측정하면 관찰하고 타겟팅하려는 모든 항목에 대해 1/16의 정밀도가 적용됩니다.

16ms와 16 정수 ms 그래프 비교

위에서 볼 수 있듯이 파란색 막대는 새 프레임을 페인트하기 전에 모든 작업을 완료해야 하는 최대 시간을 나타냅니다 (60fps 기준). 16개가 넘는 작업을 실행하고 있을 수 있지만 정수 밀리초를 사용하면 이러한 매우 큰 증분으로만 예약하고 측정할 수 있습니다. 충분하지 않습니다.

고해상도 타이머는 훨씬 더 정확한 수치를 제공하여 이 문제를 해결합니다.

Date.now()         //  1337376068250
performance.now()  //  20303.427000007

고해상도 타이머는 현재 Chrome에서 window.performance.webkitNow()로 사용할 수 있으며, 이 값은 일반적으로 rAF 콜백에 전달된 새 인수 값과 같습니다. 사양이 표준을 통해 더 진행되면 메서드의 접두사가 삭제되고 performance.now()를 통해 사용할 수 있게 됩니다.

또한 위의 두 값은 수십 배 차이가 있습니다. performance.now()는 특정 페이지 로드가 시작된 후 경과한 부동 소수점 밀리초 (구체적으로는 performance.navigationStart)를 나타냅니다.

사용 중

이 설계 패턴을 사용하는 애니메이션 라이브러리가 주요 문제입니다.

function MyAnimation(duration) {
    this.startTime = Date.now();
    this.duration = duration;
    requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
    var now = Date.now();
    if (time > now) {
        this.dispatchEvent("ended");
        return;
    }
    ...
    requestAnimFrame(this.tick.bind(this));
}

이 문제를 해결하는 수정사항은 매우 간단합니다. window.performance.now()를 사용하도록 startTimenow를 확장합니다.

this.startTime = window.performance.now ?
                    (performance.now() + performance.timing.navigationStart) :
                    Date.now();

이는 매우 순진한 구현으로, 접두사가 있는 now() 메서드를 사용하지 않으며 IE8에 없는 Date.now() 지원을 가정합니다.

기능 감지

위의 패턴을 사용하지 않고 어떤 종류의 콜백 값이 반환되는지 확인하려면 다음 기법을 사용하세요.

requestAnimationFrame(function(timestamp){

    if (timestamp < 1e12){
        // .. high resolution timer
    } else {
        // integer milliseconds since unix epoch
    }

    // ...

if (timestamp < 1e12)를 확인하는 것은 처리 중인 숫자의 크기를 확인하는 빠른 테스트입니다. 기술적으로는 웹페이지가 30년 동안 계속 열려 있는 경우에만 거짓양성이 발생할 수 있습니다. 하지만 정수로 올림 처리되는 것이 아니라 부동 소수점 수인지 테스트할 수는 없습니다. 충분한 고해상도 타이머를 요청하면 어느 시점에서는 정수 값을 반환할 수밖에 없습니다.

이 변경사항은 Chrome 21에서 적용될 예정이므로 이미 이 콜백 매개변수를 사용하고 있다면 코드를 업데이트하시기 바랍니다.