Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import type { Attachment } from 'svelte/attachments';
export function scrollController(scroll: { y: number }): Attachment {
return (node) => {
if (!(node instanceof HTMLElement)) return;
return new ScrollController(node, scroll).start();
};
}
class ScrollController {
mouseWheelActive = $state(false);
constructor(
public scrollable: HTMLElement,
public scroll: { y: number }
) {}
/**
* Returns a cleanup function
*/
start() {
// console.debug(
// '[scroll controller] starting with scroll position',
// this.scroll.y,
// 'on',
// this.scrollable
// );
const cleanup = $effect.root(() => {
if (this.scroll.y === this.scrollable.scrollTop) return;
if (!this.scrollable.scrollHeight) return;
// TODO figure out a better way to ensure the scroll happens when the container is ready
requestAnimationFrame(() => {
if (!this.scrollable) return;
if (this.mouseWheelActive) return;
console.debug('[scroll controller] syncing scroll position to', this.scroll.y);
this.scrollable.scrollTo({
top: this.scroll.y,
});
});
});
const onscroll = () => {
this.scroll.y = this.scrollable.scrollTop;
};
const onmousewheel = () => {
this.mouseWheelActive = true;
setTimeout(() => {
this.mouseWheelActive = false;
}, 500);
};
this.scrollable.addEventListener('scroll', onscroll);
this.scrollable.addEventListener('mousewheel', onmousewheel, { passive: true });
return () => {
cleanup();
this.scrollable?.removeEventListener('scroll', onscroll);
this.scrollable?.removeEventListener('mousewheel', onmousewheel);
};
}
}
|