app/src/main/assets/reader/motion.js
raw · 1423 bytes
/* Port of the user's AccelerScroll / ReaderContent model: acceleration, speed cap,
* neutral-zone exponential braking. Units are CSS px/s and CSS px/s².
* No sensor integration or DOM dependencies, so the physics can be tested directly. */
(function (root) {
'use strict';
class Motion {
constructor() { this.reset(); }
reset() { this.velocity = 0; }
step(x, seconds, config) {
if (!Number.isFinite(x) || !Number.isFinite(seconds) || seconds <= 0 || seconds > .15) {
this.reset(); return 0;
}
const delta = x - config.neutral;
const acceleration = Math.abs(delta) <= config.deadzone ? 0 : delta * (config.reverse ? -1 : 1);
// Subdivide to keep acceleration/braking consistent at 60/90/120 Hz.
let distance = 0;
while (seconds > .000001) {
const dt = Math.min(seconds, .005), previous = this.velocity;
if (acceleration === 0) this.velocity *= Math.pow(1 - config.brake, dt / .02);
else this.velocity = Math.max(-config.speed, Math.min(config.speed,
this.velocity + config.acceleration * acceleration * dt));
if (Math.abs(this.velocity) < .01) this.velocity = 0;
distance += (previous + this.velocity) * .5 * dt;
seconds -= dt;
}
return distance;
}
}
if (typeof module === 'object' && module.exports) module.exports = Motion;
else root.NarouMotion = Motion;
})(globalThis);