summaryrefslogtreecommitdiff
path: root/public/scrollbar.js
blob: da6adede755337cee22823db20fed84c61ef487b (plain)
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);