-
Notifications
You must be signed in to change notification settings - Fork 56
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
Recursively check document height before scrollTo #217
Conversation
addon/index.js
Outdated
|
||
if ( | ||
(documentWidth + scrollBarWidth - window.innerWidth >= scrollHash.x | ||
&& documentHeight + scrollBarWidth - window.innerHeight >= scrollHash.y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of sync forced layout/reflow issues when requested :(
I don't think this is a good idea. rAF runs every 16ms so I'm wondering if we can do this in a rAF pool where we recursively check in a requestAnimationFrame if these conditions are true. |
if (targetElement || 'window' === scrollElement) { | ||
if (recursiveCheck) { | ||
// our own implementation | ||
tryScrollRecursively(window.scrollTo, scrollPosition); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also want to do this if the page is optimistically rendered.
addon/index.js
Outdated
@@ -103,12 +145,20 @@ let RouterScrollMixin = Mixin.create({ | |||
} | |||
|
|||
const delayScrollTop = get(this, 'service.delayScrollTop'); | |||
const afterPaint = get(this, 'service.afterPaint'); | |||
const afterIdle = get(this, 'service.afterIdle'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both of these aren't strictly necessary for this PR. Likely users won't want either (b/c it leaves artifacts for most route transition). However, I added and documented just to test/make sure this PR does what is says it does and I can test which implementation works best for the apps I work on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! Mainly comments around wording/consistency.
README.md
Outdated
the First Meaningful Paint using `delayScrollTop: true` in your config. `delayScrollTop` defaults to `false`. | ||
#### Scroll Timing | ||
|
||
You may want the default out of the box behaviour. We schedule scroll after Ember's `render`. This occurs on the tightest schedule between route transition start and end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/out of the box/"out of the box"
addon/index.js
Outdated
|
||
if (documentWidth + scrollBarWidth - window.innerWidth >= scrollHash.x | ||
&& documentHeight + scrollBarWidth - window.innerHeight >= scrollHash.y | ||
|| ATTEMPTS >= 60) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If 60 is a magic number, maybe set it as a const
addon/index.js
Outdated
if (requestId) { | ||
window.cancelAnimationFrame(requestId); | ||
} | ||
|
||
this._super(...arguments); | ||
}, | ||
|
||
/** | ||
* Updates the scroll position | ||
* @param {transition|transition[]} transition If before Ember 3.6, this will be an array of transitions, otherwise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this @param
be above the @method
?
addon/index.js
Outdated
this._super(...arguments); | ||
}, | ||
|
||
/** | ||
* Updates the scroll position | ||
* @param {transition|transition[]} transition If before Ember 3.6, this will be an array of transitions, otherwise | ||
* it will be a single transition | ||
* @method updateScrollPosition | ||
* @param {Object} transition | ||
* @param {Boolean} recursiveCheck - if check until document height is same or lger than than y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Description of this param could read a little better. if "true", check until document height is >= y
. Maybe replace y
with what it actually is.
addon/index.js
Outdated
@@ -103,12 +150,20 @@ let RouterScrollMixin = Mixin.create({ | |||
} | |||
|
|||
const delayScrollTop = get(this, 'service.delayScrollTop'); | |||
const whenPainted = get(this, 'service.whenPainted'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the expense of being wordier, I'm wondering if these new properties could be more explicit. Like, scrollWhenPainted
and scrollWhenIdle
or something like that? Also -- would settled
be a more accurate description than idle
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great idea! changed
@@ -47,7 +50,10 @@ const RouterScroll = Service.extend({ | |||
|
|||
// if we are looking to where to transition to next, we need to set the default to the position | |||
// of the targetElement on screen | |||
set(scrollMap, 'default', { x, y }); | |||
set(scrollMap, 'default', { | |||
x, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent formatting between this and line 26.
@@ -63,7 +69,10 @@ const RouterScroll = Service.extend({ | |||
|
|||
// only a `key` present after first load | |||
if (key && 'number' === typeOf(x) && 'number' === typeOf(y)) { | |||
set(scrollMap, key, { x, y }); | |||
set(scrollMap, key, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, too.
addon/services/router-scroll.js
Outdated
@@ -84,10 +93,16 @@ const RouterScroll = Service.extend({ | |||
set(this, 'targetElement', targetElement); | |||
} | |||
|
|||
const delayScrollTop = config.routerScroll.delayScrollTop; | |||
const { whenPainted, whenIdle, delayScrollTop } = config.routerScroll; | |||
if (delayScrollTop === true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these properties default to false
and only get set to true
when set by the user, it might be simpler to use something like:
setProperties(this, {
delayScrollTop,
whenPainted,
whenIdle
});
@jleja Really appreciate the thoroughness. All your considerations were implemented here! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
For heavy content pages, the window height might not be filled in all the way by the time you call
scrollTo
. As a result, clicking the back/forward button will just end up at the top of the page. We can start calling recursively the same scrollTo until we have a height that is >= than the previousy
.For scrolling to last remembered position, we need it to call
scrollTo(x,y)
at the exact right moment and not a minute early or late. This solution uses recursiverAF
callbacks to check and execute.ember-app-scheduler is a brilliant little library. However,
whenRouteIdle
andwhenRoutePainted
are too late (you will see the top of the page before scrolling to last remembered position). We ideally need to avoid these artifacts in the out of the box implementation (although you might want ember-app-scheduler'safterPaint
orafterIdle
scrollTo though