feat: Mac-like scrollbars - thin, no track, shown while scrolling or at the edge and fading out, none when there is nothing to scroll; a lone terminal's bar sits by the frame, right of the plus
This commit is contained in:
1 parent
baee90b564
commit
25ffa60d09
10 files changed
+78
-5
No files matched your search
@@ -0,0 +1,54 @@
|
||||
// Mac-like scrollbars: a scrolled element shows its thin scrollbar, and so
|
||||
// does one whose edge the pointer is at; after a second at rest it fades out.
|
||||
// Elements with nothing to scroll never show one. The look is in app.css.
|
||||
(function () {
|
||||
'use strict';
|
||||
var live = new Map(); // element -> {v: shown 0..1, until: ms to stay}
|
||||
var raf = 0;
|
||||
function el(t) { return t === document || t === window ? document.documentElement : t; }
|
||||
function scrollable(e) {
|
||||
return e.scrollHeight > e.clientHeight + 1 || e.scrollWidth > e.clientWidth + 1;
|
||||
}
|
||||
function show(e, ms) {
|
||||
if (!e || !e.style || !scrollable(e)) return;
|
||||
var s = live.get(e) || {v: 0, until: 0};
|
||||
s.until = Math.max(s.until, performance.now() + ms);
|
||||
live.set(e, s);
|
||||
if (!raf) raf = setTimeout(tick, 16);
|
||||
}
|
||||
var last = 0;
|
||||
// A timer, not animation frames: those stop in a tab out of sight, and the
|
||||
// bar would stay shown.
|
||||
function tick() {
|
||||
var now = performance.now();
|
||||
var dt = Math.min(64, now - (last || now - 16));
|
||||
last = now;
|
||||
live.forEach(function (s, e) {
|
||||
// In fast (about 0.12 s), out slowly (about 0.4 s).
|
||||
var want = now < s.until ? 1 : 0;
|
||||
s.v = want ? Math.min(1, s.v + dt / 120) : Math.max(0, s.v - dt / 400);
|
||||
e.style.setProperty('--sb', s.v.toFixed(3));
|
||||
if (!want && s.v === 0) { e.style.removeProperty('--sb'); live.delete(e); }
|
||||
});
|
||||
raf = live.size ? setTimeout(tick, 16) : 0;
|
||||
if (!raf) last = 0;
|
||||
}
|
||||
document.addEventListener('scroll', function (ev) { show(el(ev.target), 900); }, {capture: true, passive: true});
|
||||
// The pointer at a scrollable element's right or bottom edge shows its bar.
|
||||
var moved = null;
|
||||
document.addEventListener('pointermove', function (ev) {
|
||||
if (ev.pointerType !== 'mouse') return;
|
||||
if (!moved) requestAnimationFrame(function () {
|
||||
var m = moved; moved = null;
|
||||
for (var e = m.target; e && e.nodeType === 1; e = e.parentElement) {
|
||||
if (!scrollable(e)) continue;
|
||||
var st = getComputedStyle(e);
|
||||
if (!/(auto|scroll)/.test(st.overflowY + st.overflowX) && e !== document.documentElement) continue;
|
||||
var r = e === document.documentElement ? {right: innerWidth, bottom: innerHeight} : e.getBoundingClientRect();
|
||||
if (m.x >= r.right - 14 || m.y >= r.bottom - 14) show(e, 700);
|
||||
break;
|
||||
}
|
||||
});
|
||||
moved = {target: ev.target, x: ev.clientX, y: ev.clientY};
|
||||
}, {passive: true});
|
||||
})();
|
||||
Reference in new issue
Block a user