UNPKG

pencil.js

Version:

Nice modular interactive 2D drawing library.

148 lines (129 loc) 4.53 kB
import Component from "@pencil.js/component"; import MouseEvent from "@pencil.js/mouse-event"; import Vector from "@pencil.js/vector"; /** * @module Draggable */ /** * @typedef {Object} DraggableOptions * @prop {Boolean} [x=true] - Can move along vertical axis * @prop {Boolean} [y=true] - Can move along horizontal axis * @prop {Vector} [constrain] - Relative limit of freedom */ /** * @typedef {Object} DraggableAPI * @prop {Function} x - Change the "x" value in the draggable's options * @prop {Function} y - Change the "y" value in the draggable's options * @prop {Function} constrain - Change the "constrain" value in the draggable's options * @prop {Function} stop - Stop the component from being draggable */ /** * Set this component draggable * @param {DraggableOptions} [options] - Additional options * @return {DraggableAPI} * @memberOf module:Component# */ Component.prototype.draggable = function draggable (options) { if (this.isRotatable) { throw new Error("Component can't be both rotatable and draggable."); } const cursorNotSet = this.options.cursor === Component.cursors.default; if (cursorNotSet) { this.options.cursor = Component.cursors.grab; } this.isDraggable = true; this.isDragged = false; const mergedOptions = { x: true, y: true, ...options, }; if (mergedOptions.constrain) { mergedOptions.constrain = Vector.from(mergedOptions.constrain); } let startPosition = null; let originPosition = null; const downHandler = ({ position }) => { if (cursorNotSet) { this.options.cursor = Component.cursors.grabbing; } startPosition = position; originPosition = this.position.clone(); this.isDragged = true; this.fire(new MouseEvent(MouseEvent.events.grab, this, this.position.clone())); }; this.on(MouseEvent.events.down, downHandler, true); const moveHandler = (event) => { if (this.isDragged && startPosition) { const difference = event.position.clone().subtract(startPosition); const move = originPosition.clone().add(mergedOptions.x && difference.x, mergedOptions.y && difference.y); this.position.set(move); if (mergedOptions.constrain) { this.position.constrain(mergedOptions.constrain.start, mergedOptions.constrain.end); } this.fire(new MouseEvent(MouseEvent.events.drag, this, this.position.clone())); } }; const upHandler = () => { if (cursorNotSet) { this.options.cursor = Component.cursors.grab; } if (this.isDragged && startPosition) { startPosition = null; this.isDragged = false; this.fire(new MouseEvent(MouseEvent.events.drop, this, this.position.clone())); } }; let scene; this.getScene().then((theScene) => { if (this.isDraggable) { scene = theScene; scene .on(MouseEvent.events.move, moveHandler) .on(MouseEvent.events.up, upHandler); } }); return { /** * Set the draggable "x" option * @param {Boolean} x - New value for "x" * @memberOf DraggableAPI# */ set x (x) { mergedOptions.x = x; }, /** * Set the draggable "y" option * @param {Boolean} y - New value for "y" * @memberOf DraggableAPI# */ set y (y) { mergedOptions.y = y; }, /** * Set the draggable "constrain" option * @param {Vector} constrain - New value for "constrain" * @memberOf DraggableAPI# */ set constrain (constrain) { mergedOptions.constrain = constrain ? Vector.from(constrain) : constrain; }, /** * Stop the component from being draggable * @memberOf DraggableAPI# */ stop: () => { this.removeListener(MouseEvent.events.down, downHandler); if (scene) { scene .removeListener(MouseEvent.events.move, moveHandler) .removeListener(MouseEvent.events.up, upHandler); } if (cursorNotSet) { this.options.cursor = Component.cursors.default; } this.isDraggable = false; this.isDragged = false; }, }; };