Skip to content
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

Flash of blank space in data scroller use case #187

Closed
marijnh opened this issue Dec 15, 2016 · 7 comments
Closed

Flash of blank space in data scroller use case #187

marijnh opened this issue Dec 15, 2016 · 7 comments

Comments

@marijnh
Copy link

marijnh commented Dec 15, 2016

(I'm the maintainer of CodeMirror, a code editor that supports large documents by only drawing the range around the current viewport.)

So the spec says

An Intersection Observer processing step should take place during the "Update the rendering" steps, after step 9, run the fullscreen rendering steps, and before step 10, run the animation frame callbacks, in the in the HTML Processing Model.

which means that drawing happens first, and observers are notified afterwards. I understand the reasons for this. However, that does mean that in the 'data scroller' scenario, there doesn't appear to be a way to prevent users from seeing the content flash into view as they scroll quickly (yank scrollbar or throw-scroll). This may not sound like a big deal, but in CodeMirror, users repeatedly and insistently complained about this until we fixed it (with a bunch of scary tricks some of which are starting to fall apart now that browsers delay scroll events more).

If you run the little demo below, which sets up a scrolling element with two colored elements inside of it, and colors the bottom red one blue as soon as the observer tells us it has become visible, you can easily see a flash of red when you scroll quickly.

Has a way to avoid this been discussed? Is it considered too problematic for performance, or is this whole use case not really in scope for this proposal?

<!doctype html><meta charset=utf8>

<div style="border: 1px solid black; height: 300px; width: 500px; overflow-y: scroll" id="scroller">
  <div style="background: blue; height: 700px"></div>
  <div style="background: red; height: 5000px" id="space"></div>
</div>

<script>
var space = document.querySelector("#space"),
    scroller = document.querySelector("#scroller")
new IntersectionObserver(function(entries) {
  space.style.background = "blue"
}, {
  root: scroller,
  rootMargin: '0px'
}).observe(space)
</script>
@mpb
Copy link
Collaborator

mpb commented Dec 15, 2016

Set a larger rootMargin and you will be notified a little in advance before your visible range is actually on screen. That parameter was added specifically for the "infinite scroller" use case. Will that work with your requirements?

@marijnh
Copy link
Author

marijnh commented Dec 15, 2016

Throw scrolling can move in increments of 1000s of pixels. Keeping that wide a margin around the viewport is too expensive in my case.

@szager-chromium
Copy link
Collaborator

IntersectionObserver is not really intended to drive per-frame layout. As mpb@ pointed out, you can use root margin to give yourself some extra runway to respond to scroll events, but the fundamental problem remains.

@ojanvafai
Copy link
Collaborator

There's no solution in any modern browser that will guarantee no flashes of blank space because scrolling happens on a different thread from JS. There are some scenarios under which browsers will scroll on the JS thread, but they are becoming increasingly rare as browsers further optimized their pages.

Our conclusion from that was that the only solution for infinite scrollers is to have some amount of runway. Definitely open to other ideas.

The fastest thing you could do today is have a scroll listener that does a double requestAnimationFrame and calls takeRecords. In that world you'd always miss exactly one frame.

I guess you can force a layout in your scroll listener and avoid missing a frame, but you'd still have the runway problem with threaded scrolling. How does codemirror handle threaded scrolling today?

@marijnh
Copy link
Author

marijnh commented Dec 16, 2016

There are some scenarios under which browsers will scroll on the JS thread, but they are becoming increasingly rare as browsers further optimized their pages.

So I guess the idea of providing client code with some hook to force scrolling of a given container to happen on the JS thread so that it can intercept it is considered undesirable because 'people will abuse it'. I can kind of understand that position, but it is unfortunate that it breaks this use case, forcing people to completely hijack scrolling—which leads to an even worse user experience.

but you'd still have the runway problem with threaded scrolling. How does codemirror handle threaded scrolling today?

It doesn't, that's why I'm looking for a better solution, which brought me here.

Feel free to close this if you decide that guaranteed JS-free scrolling is more important than data scrolling libs.

@szager-chromium
Copy link
Collaborator

I don't think there's a universally correct answer for this, but I will say that based on experience, users are more bothered by scrolling-induced jank (i.e. dropped frames) than by painting stale content.

IntersectionObserver is intended more as an after-the-fact reporting mechanism, and one if its design goals is to avoid impacting rendering performance. As you point out, there are other mechanisms you can use to intercept scroll events and avoid painting stale content, albeit with the side effect that you may drop frames. Which approach you choose is really up to you.

@marijnh
Copy link
Author

marijnh commented Dec 16, 2016

@szager-chromium wrote:

As you point out, there are other mechanisms you can use to intercept scroll events and avoid painting stale content,

I was claiming that there aren't such mechanisms, that I'm aware of. scroll events fire after drawing as well, and are often delayed for a long time on modern browsers. If you know a reliable technique for doing this, please point it out to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants