Skip to content

Commit aba7be7

Browse files
committed
fix bottomReached mobile devs
1 parent c80d6e8 commit aba7be7

File tree

5 files changed

+43
-46
lines changed

5 files changed

+43
-46
lines changed

demo/components/Header.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ div {
4242
display: grid;
4343
gap: 30px;
4444
grid-auto-flow: column;
45+
overflow: auto;
4546
}
4647
4748
a {
@@ -50,6 +51,7 @@ a {
5051
text-decoration: none;
5152
cursor: pointer;
5253
color: white;
54+
white-space: nowrap;
5355
}
5456
5557
a:hover,

demo/components/Sidebar/TOC.vue

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ function customScroll(id: string) {
4343
});
4444
}
4545
46-
watch(activeId, (newId) => {
47-
/* console.log('activeId', newId); */
48-
});
49-
5046
const onClick = computed(() => (clickType.value === 'native' ? setActive : customScroll));
5147
</script>
5248

@@ -81,12 +77,7 @@ const onClick = computed(() => (clickType.value === 'native' ? setActive : custo
8177
background-color: #00adb538;
8278
transition: top 100ms;
8379
border-left: 4px solid #00adb5;
84-
}
85-
86-
@media (max-width: 610px) {
87-
.Tracker {
88-
transition: top 200ms;
89-
}
80+
border-radius: 0px 5px 5px 0px;
9081
}
9182
9283
ul {

src/useActive.ts

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ export function useActive(
9797
function jumpToEdges() {
9898
const { isBottom, isTop } = getEdges(root.value!);
9999

100-
if (isTop && jumpToFirst) {
100+
if (jumpToFirst && isTop) {
101101
return (activeId.value = ids.value[0]), true;
102102
}
103-
if (isBottom && jumpToLast) {
104-
return (activeId.value = ids.value.at(-1)!), true;
103+
if (jumpToLast && isBottom) {
104+
return (activeId.value = ids.value[ids.value.length - 1]), true;
105105
}
106106
}
107107

@@ -127,10 +127,8 @@ export function useActive(
127127
function onScrollDown({ isCancel } = { isCancel: false }) {
128128
// overlayHeight not needed as 'scroll-margin-top' is set instead
129129
const offset = rootTop.value + toBottom!;
130-
131-
const firstOut =
132-
[...getRects(targets.value, 'OUT', offset).keys()].at(-1) ??
133-
(jumpToFirst ? ids.value[0] : '');
130+
const outTargets = Array.from(getRects(targets.value, 'OUT', offset).keys());
131+
const firstOut = outTargets[outTargets.length - 1] ?? (jumpToFirst ? ids.value[0] : '');
134132

135133
// Prevent innatural highlighting with smoothscroll/custom easings
136134
if (ids.value.indexOf(firstOut) > ids.value.indexOf(activeId.value)) {
@@ -164,19 +162,6 @@ export function useActive(
164162
matchMedia.value = window.matchMedia(media).matches;
165163
}
166164

167-
// Returned
168-
function setActive(id: string) {
169-
if (id !== activeId.value) {
170-
activeId.value = id;
171-
isClick.value = true;
172-
}
173-
}
174-
175-
// Returned
176-
function isActive(id: string) {
177-
return id === activeId.value;
178-
}
179-
180165
onMounted(async () => {
181166
if (isHTML.value) {
182167
root.value = document.documentElement;
@@ -221,6 +206,14 @@ export function useActive(
221206
flush: 'post',
222207
});
223208

209+
watch(activeId, (newId) => {
210+
if (replaceHash) {
211+
const start = jumpToFirst ? 0 : -1;
212+
const newHash = `${location.pathname}${activeIndex.value > start ? `#${newId}` : ''}`;
213+
history.replaceState(history.state, '', newHash);
214+
}
215+
});
216+
224217
watch(matchMedia, (_matchMedia) => {
225218
if (_matchMedia) {
226219
onScrollDown();
@@ -229,13 +222,18 @@ export function useActive(
229222
}
230223
});
231224

232-
watch(activeId, (newId) => {
233-
if (replaceHash) {
234-
const start = jumpToFirst ? 0 : -1;
235-
const newHash = `${location.pathname}${activeIndex.value > start ? `#${newId}` : ''}`;
236-
history.replaceState(history.state, '', newHash);
225+
// Returned
226+
function setActive(id: string) {
227+
if (id !== activeId.value) {
228+
activeId.value = id;
229+
isClick.value = true;
237230
}
238-
});
231+
}
232+
233+
// Returned
234+
function isActive(id: string) {
235+
return id === activeId.value;
236+
}
239237

240238
return {
241239
isActive,

src/useScroll.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function useScroll({ isHTML, root, _setActive, matchMedia }: UseListeners
2121
return isHTML.value ? window.scrollY : root.value!.scrollTop;
2222
}
2323

24-
function setReady() {
24+
function setReady(maxFrames: number) {
2525
let rafPrevY: number;
2626
let rafId: DOMHighResTimeStamp;
2727
let frameCount = 0;
@@ -34,9 +34,9 @@ export function useScroll({ isHTML, root, _setActive, matchMedia }: UseListeners
3434
// console.log('Scrolling...');
3535
return requestAnimationFrame(scrollEnd);
3636
}
37-
// When equal, wait for 20 frames to be sure is idle
37+
// When equal, wait for 20 frames after scroll and 10 after mount to be sure is idle
3838
frameCount++;
39-
if (frameCount === 20) {
39+
if (frameCount === maxFrames) {
4040
isReady.value = true;
4141
isClick.value = false;
4242
console.log('Scroll end.');
@@ -85,7 +85,7 @@ export function useScroll({ isHTML, root, _setActive, matchMedia }: UseListeners
8585
onMounted(() => {
8686
if (matchMedia.value && location.hash) {
8787
// Wait for any eventual scroll to hash triggered by browser to end
88-
setReady();
88+
setReady(10);
8989
} else {
9090
isReady.value = true;
9191
}
@@ -118,14 +118,18 @@ export function useScroll({ isHTML, root, _setActive, matchMedia }: UseListeners
118118
{ immediate: true, flush: 'sync' }
119119
);
120120

121+
function resetReady() {
122+
setReady(20);
123+
}
124+
121125
watch(
122126
isClick,
123127
(_isClick, _, onCleanup) => {
124128
const rootEl = isHTML.value ? document : root.value!;
125129

126130
if (_isClick) {
127131
console.log('Adding additional listeners...');
128-
rootEl.addEventListener('scroll', setReady, ONCE);
132+
rootEl.addEventListener('scroll', resetReady, ONCE);
129133
rootEl.addEventListener('wheel', reScroll, ONCE);
130134
rootEl.addEventListener('keydown', onSpaceBar as EventListener, ONCE);
131135
rootEl.addEventListener('pointerdown', onPointerDown as EventListener, ONCE);
@@ -134,7 +138,7 @@ export function useScroll({ isHTML, root, _setActive, matchMedia }: UseListeners
134138
onCleanup(() => {
135139
if (_isClick) {
136140
console.log('Removing additional listeners...');
137-
rootEl.removeEventListener('scroll', setReady);
141+
rootEl.removeEventListener('scroll', resetReady);
138142
rootEl.removeEventListener('wheel', reScroll);
139143
rootEl.removeEventListener('keydown', onSpaceBar as EventListener);
140144
rootEl.removeEventListener('pointerdown', onPointerDown as EventListener);

src/utils.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export function useRestrictedRef<T>(matchMedia: Ref<boolean>, defaultValue: T):
2424
}
2525

2626
export function getRects(targets: HTMLElement[], filter: 'IN' | 'OUT' | 'ALL', userOffset = 0) {
27-
const extOffset = FIXED_BOUNDARY_OFFSET + userOffset;
2827
const map = new Map<string, number>();
2928

3029
targets.forEach((target) => {
@@ -36,7 +35,7 @@ export function getRects(targets: HTMLElement[], filter: 'IN' | 'OUT' | 'ALL', u
3635
const rectProp = target.getBoundingClientRect()[isOut ? 'top' : 'bottom'];
3736
const scrollMargin = isOut ? parseFloat(getComputedStyle(target).scrollMarginTop) : 0;
3837

39-
const offset = extOffset + scrollMargin;
38+
const offset = FIXED_BOUNDARY_OFFSET + userOffset + scrollMargin;
4039
const condition = isOut ? rectProp <= offset : rectProp >= offset;
4140

4241
if (condition) {
@@ -48,10 +47,13 @@ export function getRects(targets: HTMLElement[], filter: 'IN' | 'OUT' | 'ALL', u
4847
}
4948

5049
export function getEdges(root: HTMLElement) {
50+
// Mobile devices
51+
const clientHeight = root === document.documentElement ? window.innerHeight : root.clientHeight;
52+
5153
const isTopReached = root.scrollTop <= FIXED_TO_TOP_OFFSET;
52-
const isBottomReached = Math.abs(root.scrollHeight - root.clientHeight - root.scrollTop) < 1;
54+
const isBottomReached = Math.abs(root.scrollHeight - clientHeight - root.scrollTop) < 1;
5355
const isOverscrollTop = root.scrollTop < 0;
54-
const isOverscrollBottom = root.scrollTop > root.scrollHeight - root.clientHeight;
56+
const isOverscrollBottom = root.scrollTop > root.scrollHeight - clientHeight;
5557

5658
return {
5759
isTop: isTopReached || isOverscrollTop,

0 commit comments

Comments
 (0)