Html: Is requestAnimationFrame called at the right point?

Created on 21 Apr 2017  路  21Comments  路  Source: whatwg/html

I'm repurposing this slightly.

Currently the spec says that requestAnimationFrame callbacks should come before the very next render, unless requestAnimationFrame is called after the browser has started processing the raf callback queue, in which case the callbacks happen before the following render.

Chrome & Firefox do this, but Edge and Safari don't. They run requestAnimationFrame callbacks before the following render.

test.style.transform = 'translate(0, 0)';

document.querySelector('button').addEventListener('click', () => {
  const test = document.querySelector('.test');
  test.style.transform = 'translate(400px, 0)';

  requestAnimationFrame(() => {
    test.style.transition = 'transform 3s linear';
    test.style.transform = 'translate(200px, 0)';
  });
});

In Edge and Safari, the box will move to the left. In Chrome and Firefox (spec compliant) it'll move to the right. Demo.

Developers are pretty confused about this, with currently 60% expecting the Edge/Safari behaviour.

Should we do something about this?

Here's the original issue text:


It's currently impossible to request a callback for the next animation frame.

requestAnimationFrame(cb) will call cb before the next visual update in most cases, unless it's called during "run the animation frame callbacks", in which case it'll be called after the next visual update.

Sometimes you want to wait until after a natural layout/render to perform particular steps, to avoid a synchronous layout.

function requestNextAnimationFrame(cb) {
  requestAnimationFrame(() => requestAnimationFrame(cb));
}

The above guarantees cb will be called after a natural layout/render, but if called during "run the animation frame callbacks" you end up waiting an additional frame.

Another way to solve this problem would be to provide insight into the current position within the event loop, so a user-land implementation of requestNextAnimationFrame could avoid calling raf twice if it's already in that phase of the loop.

additioproposal

Most helpful comment

This takes me back to thinking we need something like requestAfterAnimationFrame, which lands after paint.

Or maybe, requestAnimationFrame(cb, { mode: 'after-frame' }). We could have before-frame, after-frame, and the current default of surprise-me.

All 21 comments

This seems vaguely related to the discussion in https://github.com/whatwg/html/issues/512 (which got off topic pretty quickly)

It seems like both Edge & Safari treat requestAnimationFrame as "next frame" already (box moves to the left). https://safari-raf-bug.glitch.me/

Replies to https://twitter.com/jaffathecake/status/912601447442927617 suggest developers are very confused about this.

Feels like we should do something to make this kind of scheduling more explicit, but I'm not sure how, yet.

If the use cases of requestAnimationFrame are:

  • Let me debounce things that happen multiple times a frame.
  • Sync me to the refresh rate of the screen.

鈥hen the spec makes the most sense, and Safari and Edge are needlessly delaying the callbacks. I guess I'll file bugs with them and see if they disagree.

Is this covered by any existing WPT cases?

This takes me back to thinking we need something like requestAfterAnimationFrame, which lands after paint. This is the point you'd do all your style/layout-dependent reading, and you'd do all your style/layout writing in requestAnimationFrame.

@wanderview I can't find one at a glance

This takes me back to thinking we need something like requestAfterAnimationFrame, which lands after paint.

Or maybe, requestAnimationFrame(cb, { mode: 'after-frame' }). We could have before-frame, after-frame, and the current default of surprise-me.

@smaug---- also occasionally asks for an "after animation frame callback".

@rocallahan and @heycam might recall some of the original motivation for the timing used by the specification.

@wanderview that works, although it might encourage GC? I think that's why raf uses a callback rather than a promise.

requestAnimationFrame was created before promises existed.

The current spec and Firefox/Chrome behavior let you use requestAnimationFrame to batch work to happen before the next render. That seems useful. I guess I'm agreeing with https://github.com/whatwg/html/issues/2569#issuecomment-332174541.

I'm pretty sure that was the original motivation, though I can't be sure.

Yeah, the current behavior feels reasonable. I'm very surprised people think that is confusing.

https://www.w3.org/Bugs/Public/show_bug.cgi?id=28644 is separate thing. Quite often one wants to do something right after rendering. Something which isn't possibly about rendering but something else.

@rniwa could you maybe add Apple's rationale for doing this after the frame rather than before? Or include the relevant folks who can.

cc @mstange

cc @smfr @hober

I think WebKit's behavior falls out of our "it's like a timer" thinking. In fact, we fall back to a timer in the implementation in some cases. I agree that the Safari behavior seems wrong in the case where you really want to have changes show up before the up-coming paint.

requestAnimationFrame's timing as specced is really useful for debouncing things like mousemove, where you only need to update once per frame, but waiting an additional frame introduces visual lag.

I believe WebKit is switching to match other browsers here.

This issue still persists on Safari.

This issue still persists on Safari.

Please clarify, with Safari version, more details and a testcase filed at bugs.webkit.org

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iane87 picture iane87  路  3Comments

holloway picture holloway  路  3Comments

NE-SmallTown picture NE-SmallTown  路  4Comments

FANMixco picture FANMixco  路  3Comments

lazarljubenovic picture lazarljubenovic  路  4Comments