UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

104 lines (103 loc) 4.07 kB
import { Vec2 } from '@js-draw/math'; export const defaultStationaryDetectionConfig = { maxSpeed: 8.5, // screenPx/s maxRadius: 11, // screenPx minTimeSeconds: 0.5, // s }; export default class StationaryPenDetector { // Only handles one pen. As such, `startPointer` should be the same device/finger // as `updatedPointer` in `onPointerMove`. // // A new `StationaryPenDetector` should be created for each gesture. constructor(startPointer, config, onStationary) { this.config = config; this.onStationary = onStationary; this.timeout = null; this.stationaryStartPointer = startPointer; this.lastPointer = startPointer; this.averageVelocity = Vec2.zero; this.setStationaryTimeout(this.config.minTimeSeconds * 1000); } // Returns true if stationary onPointerMove(currentPointer) { if (!this.stationaryStartPointer) { // Destoroyed return; } if (currentPointer.id !== this.stationaryStartPointer.id) { return false; } // dx: "Δx" Displacement from last. const dxFromLast = currentPointer.screenPos.minus(this.lastPointer.screenPos); const dxFromStationaryStart = currentPointer.screenPos.minus(this.stationaryStartPointer.screenPos); // dt: Delta time: // /1000: Convert to s. let dtFromLast = (currentPointer.timeStamp - this.lastPointer.timeStamp) / 1000; // s // Don't divide by zero if (dtFromLast === 0) { dtFromLast = 1; } const currentVelocity = dxFromLast.times(1 / dtFromLast); // px/s // Slight smoothing of the velocity to prevent input jitter from affecting the // velocity too significantly. this.averageVelocity = this.averageVelocity.lerp(currentVelocity, 0.5); // px/s const dtFromStart = currentPointer.timeStamp - this.stationaryStartPointer.timeStamp; // ms const movedOutOfRadius = dxFromStationaryStart.length() > this.config.maxRadius; this.hasMovedOutOfRadius ||= movedOutOfRadius; // If not stationary if (movedOutOfRadius || this.averageVelocity.length() > this.config.maxSpeed || dtFromStart < this.config.minTimeSeconds) { this.stationaryStartPointer = currentPointer; this.lastPointer = currentPointer; this.setStationaryTimeout(this.config.minTimeSeconds * 1000); return false; } const stationaryTimeoutMs = this.config.minTimeSeconds * 1000 - dtFromStart; this.lastPointer = currentPointer; return stationaryTimeoutMs <= 0; } onPointerUp(pointer) { if (pointer.id !== this.stationaryStartPointer?.id) { this.cancelStationaryTimeout(); } } destroy() { this.cancelStationaryTimeout(); this.stationaryStartPointer = null; } getHasMovedOutOfRadius() { return this.hasMovedOutOfRadius; } cancelStationaryTimeout() { if (this.timeout !== null) { clearTimeout(this.timeout); this.timeout = null; } } setStationaryTimeout(timeoutMs) { if (this.timeout !== null) { return; } if (timeoutMs <= 0) { this.onStationary(this.lastPointer); } else { this.timeout = setTimeout(() => { this.timeout = null; if (!this.stationaryStartPointer) { // Destroyed return; } const timeSinceStationaryStart = performance.now() - this.stationaryStartPointer.timeStamp; const timeRemaining = this.config.minTimeSeconds * 1000 - timeSinceStationaryStart; if (timeRemaining <= 0) { this.onStationary(this.lastPointer); } else { this.setStationaryTimeout(timeRemaining); } }, timeoutMs); } } }