UNPKG

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
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++; } }