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
|
function initScrollbox(box) {
const area = box.querySelector("[data-scroll-area]");
const track = box.querySelector("[data-scroll-track]");
const thumb = box.querySelector("[data-scroll-thumb]");
const inset = () => parseFloat(getComputedStyle(thumb).left) || 0;
const update = () => {
const pad = inset();
const room = track.clientHeight - pad * 2;
const maxScroll = area.scrollHeight - area.clientHeight;
const ratio = Math.min(1, area.clientHeight / area.scrollHeight);
const height = Math.max(room * ratio, thumb.clientWidth);
const progress = maxScroll > 0 ? area.scrollTop / maxScroll : 0;
thumb.style.height = `${height}px`;
thumb.style.top = `${pad + progress * (room - height)}px`;
};
thumb.addEventListener("pointerdown", (e) => {
e.preventDefault();
thumb.setPointerCapture(e.pointerId);
const startY = e.clientY;
const startTop = parseFloat(thumb.style.top) || 0;
const move = (ev) => {
const pad = inset();
const room = track.clientHeight - pad * 2 - thumb.offsetHeight;
const top = Math.min(
Math.max(startTop - pad + ev.clientY - startY, 0),
room,
);
const progress = room > 0 ? top / room : 0;
area.scrollTop = progress * (area.scrollHeight - area.clientHeight);
};
const stop = () => {
thumb.removeEventListener("pointermove", move);
thumb.removeEventListener("pointerup", stop);
thumb.removeEventListener("pointercancel", stop);
};
thumb.addEventListener("pointermove", move);
thumb.addEventListener("pointerup", stop);
thumb.addEventListener("pointercancel", stop);
});
area.addEventListener("scroll", update, { passive: true });
const observer = new ResizeObserver(update);
observer.observe(area);
for (const child of area.children) observer.observe(child);
update();
}
document.querySelectorAll("[data-scrollbox]").forEach(initScrollbox);
|