js-draw
Version:
Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.
78 lines (77 loc) • 3.06 kB
JavaScript
import { Rect2, Vec2 } from '@js-draw/math';
import untilNextAnimationFrame from '../../util/untilNextAnimationFrame.mjs';
/**
* Automatically scrolls the viewport such that the user's pointer is visible.
*/
export default class ToPointerAutoscroller {
constructor(viewport, scrollByCanvasDelta) {
this.viewport = viewport;
this.scrollByCanvasDelta = scrollByCanvasDelta;
this.started = false;
this.updateLoopId = 0;
this.updateLoopRunning = false;
this.targetPoint = null;
this.scrollRate = 1000; // px/s
}
getScrollForPoint(screenPoint) {
const screenSize = this.viewport.getScreenRectSize();
const screenRect = new Rect2(0, 0, screenSize.x, screenSize.y);
// Starts autoscrolling when the cursor is **outside of** this region
const marginSize = 44;
const autoscrollBoundary = screenRect.grownBy(-marginSize);
if (autoscrollBoundary.containsPoint(screenPoint)) {
return Vec2.zero;
}
const closestEdgePoint = autoscrollBoundary.getClosestPointOnBoundaryTo(screenPoint);
const distToEdge = closestEdgePoint.distanceTo(screenPoint);
const toEdge = closestEdgePoint.minus(screenPoint);
// Go faster for points further away from the boundary.
const maximumScaleFactor = 1.25;
const scaleFactor = Math.min(distToEdge / marginSize, maximumScaleFactor);
return toEdge.normalizedOrZero().times(scaleFactor);
}
start() {
this.started = true;
}
onPointerMove(pointerScreenPosition) {
if (!this.started) {
return;
}
if (this.getScrollForPoint(pointerScreenPosition) === Vec2.zero) {
this.stopUpdateLoop();
}
else {
this.targetPoint = pointerScreenPosition;
this.startUpdateLoop();
}
}
stop() {
this.targetPoint = null;
this.started = false;
this.stopUpdateLoop();
}
startUpdateLoop() {
if (this.updateLoopRunning) {
return;
}
(async () => {
this.updateLoopId++;
const currentUpdateLoopId = this.updateLoopId;
let lastUpdateTime = performance.now();
while (this.updateLoopId === currentUpdateLoopId && this.targetPoint) {
this.updateLoopRunning = true;
const currentTime = performance.now();
const deltaTimeMs = currentTime - lastUpdateTime;
const scrollDirection = this.getScrollForPoint(this.targetPoint);
const screenScrollAmount = scrollDirection.times((this.scrollRate * deltaTimeMs) / 1000);
this.scrollByCanvasDelta(this.viewport.screenToCanvasTransform.transformVec3(screenScrollAmount));
lastUpdateTime = currentTime;
await untilNextAnimationFrame();
}
this.updateLoopRunning = false;
})();
}
stopUpdateLoop() {
this.updateLoopId++;
}
}