Group Leader
- Joined
- Jan 18, 2018
- Messages
- 127
Current issue:
Keyboard based scrolling is currently somewhat jittery and based on number of presses/repeats over time. Vertical scroll can currently be overridden, however overriding horizontal scroll cannot be done in a normal manner without potentially bringing other issues along (using
). Currently, well-written code can override this and only pass through on first frame. Ideally, it would be done in an automatic fashion.
Resolve:
TypeScript implementation (complex form). Use keybinds.ts for actual bindings to the internal functions.
The above implementations achieve three goals:[ul][*]Scroll is time-based, rather than jump/repeat based[/*][*]Composable and option based[/*][*]Does not add a spin loop if it's not in use[/*][/ul]Additionally, it holds state across multiple tabs, and always closes/cancels its requests when changing tabs/windows in order to avoid memory leaks in js.
Keyboard based scrolling is currently somewhat jittery and based on number of presses/repeats over time. Vertical scroll can currently be overridden, however overriding horizontal scroll cannot be done in a normal manner without potentially bringing other issues along (using
Code:
event.stopImmediatePropagation()
Resolve:
TypeScript implementation (complex form). Use keybinds.ts for actual bindings to the internal functions.
Code:
const dirs = new Uint8ClampedArray(4);
const _d = new Int32Array(dirs.buffer, dirs.byteOffset, 1);
// init using global `var SCROLL_SPEED = {x: +n, y: +n}`
const dtxy = typeof SCROLL_SPEED === 'object' ? SCROLL_SPEED : {x: 0.84, y: 0.94};
Object.seal(dtxy);
let t0 = 0.0;
let raf = 0;
/** direction, default is anticlockwise starting from bottom (down). */
export const enum Dirs { Down, Right, Up, Left }
const scroller = (T: DOMHighResTimeStamp) => {
if (_d[0] !== 0) {
const dt = Math.min(T - t0, 50);
const dx = (dirs[Dirs.Down] - dirs[Dirs.Up]);// & 0x800000ff;
const dy = (dirs[Dirs.Right] - dirs[Dirs.Left]);// & 0x800000ff;
const sc = document.fullscreenElement || document.scrollingElement || document.body;
sc.scrollBy(dt * dtxy.x * dx, dt * dtxy.y * dy);
t0 = T;
raf = requestAnimationFrame(scroller);
} else raf = 0;
}
interface TimeStamp { timeStamp: DOMHighResTimeStamp; }
/** scroll loop */
export const scr = <T extends TimeStamp>(e: T, d: Dirs) => {
++dirs[d];
raf || (
raf = requestAnimationFrame(scroller),
t0 = e.timeStamp
);
}
/** decrements the direction */
export const dec = (d: Dirs) => { --dirs[d]; }
/** clears the animation for later use */
export const can = () => {
cancelAnimationFrame(raf);
_d[0] = raf = 0;
}
/** set x and y speeds, optionally share state to other tabs */
export function sxy(x: number, y: number, send?: boolean) {
dtxy.x = Math.abs(x);
dtxy.y = Math.abs(y);
if (send) bc.postMessage([dtxy.x, dtxy.y]);
}
document.addEventListener('visibilitychange', can);
const bc = new BroadcastChannel('scroll-speeds');
bc.onmessage = e => sxy(e.data[0], e.data[1]);
The above implementations achieve three goals:[ul][*]Scroll is time-based, rather than jump/repeat based[/*][*]Composable and option based[/*][*]Does not add a spin loop if it's not in use[/*][/ul]Additionally, it holds state across multiple tabs, and always closes/cancels its requests when changing tabs/windows in order to avoid memory leaks in js.