UNPKG

js-draw

Version:

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

126 lines (125 loc) 5.86 kB
import { Vec2 } from '@js-draw/math'; export var PointerDevice; (function (PointerDevice) { PointerDevice[PointerDevice["Pen"] = 0] = "Pen"; PointerDevice[PointerDevice["Eraser"] = 1] = "Eraser"; PointerDevice[PointerDevice["Touch"] = 2] = "Touch"; PointerDevice[PointerDevice["PrimaryButtonMouse"] = 3] = "PrimaryButtonMouse"; PointerDevice[PointerDevice["RightButtonMouse"] = 4] = "RightButtonMouse"; PointerDevice[PointerDevice["Other"] = 5] = "Other"; })(PointerDevice || (PointerDevice = {})); // Provides a snapshot containing information about a pointer. A Pointer // object is immutable — it will not be updated when the pointer's information changes. export default class Pointer { constructor( // The (x, y) position of the pointer relative to the top-left corner // of the visible canvas. screenPos, // Position of the pointer relative to the top left corner of the drawing // surface. canvasPos, pressure, isPrimary, down, device, // Unique ID for the pointer id, // Numeric timestamp (milliseconds, as from `performance.now()`). timeStamp) { this.screenPos = screenPos; this.canvasPos = canvasPos; this.pressure = pressure; this.isPrimary = isPrimary; this.down = down; this.device = device; this.id = id; this.timeStamp = timeStamp; } /** * Snaps this pointer to the nearest grid point (rounds the coordinates of this * pointer based on the current zoom). Returns a new Pointer and does not modify * this. */ snappedToGrid(viewport) { const snappedCanvasPos = viewport.snapToGrid(this.canvasPos); return this.withCanvasPosition(snappedCanvasPos, viewport); } // Snap this pointer to the X or Y axis (whichever is closer), where (0,0) // is considered to be at `originPointScreen`. // @internal lockedToXYAxesScreen(originPointScreen, viewport) { const current = this.screenPos; const currentFromStart = current.minus(originPointScreen); // Determine whether the last point was closer to being on the // x- or y- axis. const projOntoXAxis = Vec2.unitX.times(currentFromStart.x); const projOntoYAxis = Vec2.unitY.times(currentFromStart.y); let pos; if (currentFromStart.dot(projOntoXAxis) > currentFromStart.dot(projOntoYAxis)) { pos = projOntoXAxis; } else { pos = projOntoYAxis; } pos = pos.plus(originPointScreen); return this.withScreenPosition(pos, viewport); } /** @see {@link withCanvasPosition} */ withScreenPosition(screenPos, viewport) { const canvasPos = viewport.screenToCanvas(screenPos); return this.withCanvasPosition(canvasPos, viewport); } /** Returns a copy of this pointer with a changed timestamp. */ withTimestamp(timeStamp) { return new Pointer(this.screenPos, this.canvasPos, this.pressure, this.isPrimary, this.down, this.device, this.id, timeStamp); } /** * Returns a copy of this pointer with a new position. The screen position is determined * by the given `canvasPos`. */ withCanvasPosition(canvasPos, viewport) { const screenPos = viewport.canvasToScreen(canvasPos); return new Pointer(screenPos, canvasPos, this.pressure, this.isPrimary, this.down, this.device, this.id, this.timeStamp); } // Creates a Pointer from a DOM event. If `relativeTo` is given, (0, 0) in screen coordinates is // considered the top left of `relativeTo`. static ofEvent(evt, isDown, viewport, relativeTo) { let screenPos = Vec2.of(evt.clientX, evt.clientY); if (relativeTo) { const bbox = relativeTo.getBoundingClientRect(); screenPos = screenPos.minus(Vec2.of(bbox.left, bbox.top)); } const pointerTypeToDevice = { mouse: PointerDevice.PrimaryButtonMouse, pen: PointerDevice.Pen, touch: PointerDevice.Touch, }; let device = pointerTypeToDevice[evt.pointerType] ?? PointerDevice.Other; const eraserButtonMask = 0x20; if (device === PointerDevice.Pen && (evt.buttons & eraserButtonMask) !== 0) { device = PointerDevice.Eraser; } const timeStamp = evt.timeStamp; const canvasPos = viewport.roundPoint(viewport.screenToCanvas(screenPos)); if (device === PointerDevice.PrimaryButtonMouse) { if (evt.buttons & 0x2) { device = PointerDevice.RightButtonMouse; } // Commented out: Mouse up events seem to not satisfy this condition on mouse up. // else if (!(evt.buttons & 0x1)) { // device = PointerDevice.Other; //} } return new Pointer(screenPos, canvasPos, evt.pressure ?? null, evt.isPrimary, isDown, device, evt.pointerId, timeStamp); } // Create a new Pointer from a point on the canvas. // Intended for unit tests. static ofCanvasPoint(canvasPos, isDown, viewport, id = 0, device = PointerDevice.Pen, isPrimary = true, pressure = null, timeStamp = null) { const screenPos = viewport.canvasToScreen(canvasPos); timeStamp ??= performance.now(); return new Pointer(screenPos, canvasPos, pressure, isPrimary, isDown, device, id, timeStamp); } // Create a new Pointer from a point on the screen. // Intended for unit tests. static ofScreenPoint(screenPos, isDown, viewport, id = 0, device = PointerDevice.Pen, isPrimary = true, pressure = null, timeStamp = null) { const canvasPos = viewport.screenToCanvas(screenPos); timeStamp ??= performance.now(); return new Pointer(screenPos, canvasPos, pressure, isPrimary, isDown, device, id, timeStamp); } }