diff options
Diffstat (limited to 'shared/utils/src/try-scroll.ts')
| -rw-r--r-- | shared/utils/src/try-scroll.ts | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/shared/utils/src/try-scroll.ts b/shared/utils/src/try-scroll.ts new file mode 100644 index 0000000..1e6b0d2 --- /dev/null +++ b/shared/utils/src/try-scroll.ts @@ -0,0 +1,65 @@ +import type { Logger } from '@amp/web-apps-logger'; +export interface ScrollableElement { + scrollTop: number; + scrollHeight: number; + offsetHeight: number; +} + +// Global is okay here as this only runs in the browser +let nextTry: number | null = null; + +export function tryScroll( + log: Logger, + getScrollablePageElement: Function, + scrollY: number, +): void { + let tries = 0; + + if (nextTry !== null) { + window.cancelAnimationFrame(nextTry); + } + + nextTry = window.requestAnimationFrame(function doNextTry() { + // At 16ms per frame, this is 1600ms + // See: https://github.com/DockYard/ember-router-scroll/blob/2f17728f/addon/services/router-scroll.js#L56 + if (++tries >= 100) { + log.warn("wasn't able to restore scroll within 100 frames"); + nextTry = null; + return; + } + + let element = getScrollablePageElement(); + if (!element) { + log.warn( + 'could not restore scroll: the scrollable element is missing', + ); + return; + } + const { scrollHeight, offsetHeight } = element; + + // Only scroll once we're able to get a full screen of content when + // scrollTop is set to scrollY + // + // +16 is a bit of a fudge factor to count for imperfections in + // features like lazy loading. If the scroll position to restore is + // the very bottom of the page, then scrollY + offsetHeight must be + // exactly scrollHeight. But if lazy loading components (for example) + // cause the page to grow by a few pixels, then this will never hold. + // Thus, we fudge by a few pixels to be more forgiving in this scenario. + const canScroll = scrollY + offsetHeight <= scrollHeight + 16; + + if (!canScroll) { + log.info('page is not tall enough for scroll yet', { + scrollHeight, + offsetHeight, + }); + + nextTry = window.requestAnimationFrame(doNextTry); + return; + } + + element.scrollTop = scrollY; + log.info('scroll restored to', scrollY); + nextTry = null; + }); +} |
