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

일마리 하이키넨

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을 사용하는 경우 타임스탬프 값을 사용하지 않습니다. 연결이 끊겼습니다. :)

이유

왜냐하면 Well rAF는 궁극적인 60fps를 얻는 데 도움이 되며 60fps는 프레임당 16.7ms로 변환됩니다. 그러나 정수 밀리초로 측정하면 관찰하고 타겟팅하려는 모든 것에 대해 1/16의 정밀도를 갖게 됩니다.

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

위에서 볼 수 있듯이 파란색 막대는 새 프레임을 칠하기 전에 모든 작업을 실행해야 하는 최대 시간 (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));
}

이 문제를 수정하는 방법은 매우 간단합니다. startTimenow를 보강하여 window.performance.now()를 사용하면 됩니다.

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에 적용될 예정이므로 이미 이 콜백 매개변수를 활용하고 있다면 코드를 업데이트해야 합니다.