- From: Nat Duca <nduca@google.com>
- Date: Wed, 9 May 2012 15:23:29 -0700
- To: Yehuda Katz <yehuda.katz@jquery.com>
- Cc: Boris Zbarsky <bzbarsky@mit.edu>, Jatinder Mann <jmann@microsoft.com>, "public-web-perf@w3.org" <public-web-perf@w3.org>
So, presentation time is without a doubt an important value to report (though it sounds like not everyone is reporting this). Passing a frameBeginTime to callbacks allows disparate callbacks to agree with one another about when the frame began --- which is useful for animation state transitions, javascript-frame rate timing, etc. Here's an example where frameBeginTime versus presentTime is important: function Animation(duration) { this.startTime = window.performance.now(); this.duration = duration; requestAnimationFrame(this.tick.bind(this)); } Animation.prototype.tick = function(presentTime) { this.updatePositionTo(presentTime); if(presentTime > this.startTime + this.duration) this.dispatchEvent('animationEnded'); } Now, we do something like var a= new Animation(1000.0); a.addEventListener('animationEnded', function() { assert(window.performance.now() - a.startTime >= a.duration); }); This assert is going to fail --- this will end 16-30ms early --- somewhere around 990ms, we're going to get a tick for present for +16ms in the future, and thus fire the ended callback. Whoops! The naieve fix here is to check window.performance.now: Animation.prototype.tick = function(presentTime) { this.updatePositionTo(presentTime); // Check performance.now so that we fire events in a way that seems sane. if(window.performance.now() > this.startTime + this.duration) this.dispatchEvent('animationEnded'); } Ok, we're good, right? But, now, lets say I have two animations: var a= new Animation(1000.0); a.addEventListener('animationEnded', function() { assert(window.performance.now() - a.startTime >= a.duration); aEnded = true; checkBothEnded(); }); var b= new Animation(1000.0); b.addEventListener('animationEnded', function() { assert(window.performance.now() - b.startTime >= b.duration); bEnded = true; checkBothEnded(); }); checkBothEnded() { if (!checkTaskEnqueued) window.postTask(checkBothEnded); assert (aEnded && bEnded); } If we're lucky, this will just work. But, if we run this test on a bot somewhere, every once in a while, we will get a tick at window.performance.now()=999.99999ms AND then between the a callback and the b callback, now() becomes 1001 and so one ends but the other doesn't. This then forces animation systems to write their own "master raf code" which works fine if you have one framework across the entire page, but ~ugh~! I think that this is why most animation systems pass a frame begin time as well as a present time. But, I could be wrong. :) (CVDisplayLink is documented here, though it doesn't explain the rationale for the design: http://developer.apple.com/library/mac/documentation/QuartzCore/Reference/CVDisplayLinkRef/Reference/reference.html#//apple_ref/c/tdef/CVDisplayLinkOutputCallback) On Wed, May 9, 2012 at 11:20 AM, Yehuda Katz <yehuda.katz@jquery.com> wrote: > In this case, shouldn't the time be "the time that the animations will be > presented on > the screen". > > Yehuda Katz > (ph) 718.877.1325 > > > > On Wed, May 9, 2012 at 11:17 AM, Boris Zbarsky <bzbarsky@mit.edu> wrote: >> >> On 5/9/12 2:05 PM, Yehuda Katz wrote: >>> >>> What are authors expected to do with this timestamp? >> >> >> Decide what exactly to draw; it tells them what time the animation is to >> be drawn for. Especially for animations that are not inhertently >> frame-based (e.g. moving an object from point X to point Y). >> >>> What is the use-case? >> >> >> Synchronizing animations with each other. >> >> -Boris > >
Received on Wednesday, 9 May 2012 22:23:59 UTC