summaryrefslogtreecommitdiff
path: root/shared/utils/src/try-scroll.ts
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /shared/utils/src/try-scroll.ts
init commit
Diffstat (limited to 'shared/utils/src/try-scroll.ts')
-rw-r--r--shared/utils/src/try-scroll.ts65
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;
+ });
+}