New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is requestAnimationFrame called at the right point? #2569

Open
jakearchibald opened this Issue Apr 21, 2017 · 18 comments

Comments

9 participants
@jakearchibald
Collaborator

jakearchibald commented Apr 21, 2017

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.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Apr 21, 2017

Member

This seems vaguely related to the discussion in #512 (which got off topic pretty quickly)

Member

domenic commented Apr 21, 2017

This seems vaguely related to the discussion in #512 (which got off topic pretty quickly)

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Sep 26, 2017

Collaborator

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.

Collaborator

jakearchibald commented Sep 26, 2017

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.

@jakearchibald jakearchibald changed the title from requestNextAnimationFrame to Is requestAnimationFrame called at the right point? Sep 26, 2017

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Sep 26, 2017

Collaborator

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.

…then 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.

Collaborator

jakearchibald commented Sep 26, 2017

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.

…then 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.

@wanderview

This comment has been minimized.

Show comment
Hide comment
@wanderview

wanderview Sep 26, 2017

Member

Is this covered by any existing WPT cases?

Member

wanderview commented Sep 26, 2017

Is this covered by any existing WPT cases?

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Sep 26, 2017

Collaborator

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.

Collaborator

jakearchibald commented Sep 26, 2017

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.

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Sep 26, 2017

Collaborator

@wanderview I can't find one at a glance

Collaborator

jakearchibald commented Sep 26, 2017

@wanderview I can't find one at a glance

@wanderview

This comment has been minimized.

Show comment
Hide comment
@wanderview

wanderview Sep 26, 2017

Member

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.

Member

wanderview commented Sep 26, 2017

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.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Sep 27, 2017

Member

@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.

Member

annevk commented Sep 27, 2017

@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.

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Sep 27, 2017

Collaborator

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

Collaborator

jakearchibald commented Sep 27, 2017

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

@rocallahan

This comment has been minimized.

Show comment
Hide comment
@rocallahan

rocallahan Sep 27, 2017

requestAnimationFrame was created before promises existed.

rocallahan commented Sep 27, 2017

requestAnimationFrame was created before promises existed.

@rocallahan

This comment has been minimized.

Show comment
Hide comment
@rocallahan

rocallahan Sep 27, 2017

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 #2569 (comment).

rocallahan commented Sep 27, 2017

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 #2569 (comment).

@rocallahan

This comment has been minimized.

Show comment
Hide comment
@rocallahan

rocallahan Sep 27, 2017

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

rocallahan commented Sep 27, 2017

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

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@smaug----

This comment has been minimized.

Show comment
Hide comment
@smaug----

smaug---- Sep 27, 2017

Collaborator

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.

Collaborator

smaug---- commented Sep 27, 2017

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.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Mar 13, 2018

Member

@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

Member

annevk commented Mar 13, 2018

@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

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Mar 14, 2018

Collaborator
Collaborator

rniwa commented Mar 14, 2018

@smfr

This comment has been minimized.

Show comment
Hide comment
@smfr

smfr Mar 14, 2018

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.

smfr commented Mar 14, 2018

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.

@jakearchibald

This comment has been minimized.

Show comment
Hide comment
@jakearchibald

jakearchibald Mar 14, 2018

Collaborator

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.

Collaborator

jakearchibald commented Mar 14, 2018

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment