UNPKG

react-design-editor

Version:

Design Editor Tools with React.js + ant.design + fabric.js

883 lines (882 loc) 37.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const animejs_1 = __importDefault(require("animejs")); const fabric_1 = require("fabric"); const constants_1 = require("../constants"); /** * Event Handler Class * @author salgum1114 * @class EventHandler */ class EventHandler { constructor(handler) { /** * Detach event on document * */ this.destroy = () => { if (this.handler.editable) { this.handler.canvas.off({ 'object:modified': this.modified, 'object:scaling': this.scaling, 'object:moving': this.moving, 'object:moved': this.moved, 'object:rotating': this.rotating, 'mouse:wheel': this.mousewheel, 'mouse:down': this.mousedown, 'mouse:move': this.mousemove, 'mouse:up': this.mouseup, 'selection:cleared': this.selection, 'selection:created': this.selection, 'selection:updated': this.selection, }); } else { this.handler.canvas.off({ 'mouse:down': this.mousedown, 'mouse:move': this.mousemove, 'mouse:out': this.mouseout, 'mouse:up': this.mouseup, 'mouse:wheel': this.mousewheel, }); this.handler.getObjects().forEach(object => { object.off('mousedown', this.handler.eventHandler.object.mousedown); if (object.anime) { animejs_1.default.remove(object); } }); } this.handler.canvas.wrapperEl.removeEventListener('keydown', this.keydown); this.handler.canvas.wrapperEl.removeEventListener('keyup', this.keyup); this.handler.canvas.wrapperEl.removeEventListener('mousedown', this.onmousedown); this.handler.canvas.wrapperEl.removeEventListener('contextmenu', this.contextmenu); if (this.handler.keyEvent.clipboard) { this.handler.canvas.wrapperEl.removeEventListener('paste', this.paste); } }; /** * Individual object event * */ this.object = { /** * Mouse down event on object * @param {FabricEvent} opt */ mousedown: (opt) => { const { target } = opt; if (target && target.link && target.link.enabled) { const { onClick } = this.handler; if (onClick) { onClick(this.handler.canvas, target); } } }, /** * Mouse double click event on object * @param {FabricEvent} opt */ mousedblclick: (opt) => { const { target } = opt; if (target) { const { onDblClick } = this.handler; if (onDblClick) { onDblClick(this.handler.canvas, target); } } }, }; /** * Modified event object * * @param {FabricEvent} opt * @returns */ this.modified = (opt) => { const { target } = opt; if (!target) { return; } if (target.type === 'circle' && target.parentId) { return; } const { onModified } = this.handler; if (onModified) { onModified(target); } }; /** * Moving event object * * @param {FabricEvent} opt * @returns */ this.moving = (opt) => { const { target } = opt; if (this.handler.interactionMode === 'crop') { this.handler.cropHandler.moving(opt); } else { if (this.handler.editable && this.handler.guidelineOption.enabled) { this.handler.guidelineHandler.movingGuidelines(target); } if (target.type === 'activeSelection') { const activeSelection = target; activeSelection.getObjects().forEach((obj) => { const left = target.left + obj.left + target.width / 2; const top = target.top + obj.top + target.height / 2; if (obj.superType === 'node') { this.handler.portHandler.setCoords({ ...obj, left, top }); } else if (obj.superType === 'element') { const { id } = obj; const el = this.handler.elementHandler.findById(id); // TODO... Element object incorrect position this.handler.elementHandler.setPositionByOrigin(el, obj, left, top); } }); return; } if (target.superType === 'node') { this.handler.portHandler.setCoords(target); } else if (target.superType === 'element') { const { id } = target; const el = this.handler.elementHandler.findById(id); this.handler.elementHandler.setPosition(el, target); } } }; /** * Moved event object * * @param {FabricEvent} opt */ this.moved = (opt) => { const { target } = opt; this.handler.gridHandler.setCoords(target); if (!this.handler.transactionHandler.active) { this.handler.transactionHandler.save('moved'); } if (target.superType === 'element') { const { id } = target; const el = this.handler.elementHandler.findById(id); this.handler.elementHandler.setPosition(el, target); } }; /** * Scaling event object * * @param {FabricEvent} opt */ this.scaling = (opt) => { const { target } = opt; if (this.handler.interactionMode === 'crop') { this.handler.cropHandler.resize(opt); } // TODO...this.handler.guidelineHandler.scalingGuidelines(target); if (target.superType === 'element') { const { id, width, height } = target; const el = this.handler.elementHandler.findById(id); // update the element this.handler.elementHandler.setScaleOrAngle(el, target); this.handler.elementHandler.setSize(el, target); this.handler.elementHandler.setPosition(el, target); const video = target; if (video.type === 'video' && video.player) { video.player.setPlayerSize(width, height); } } }; /** * Scaled event object * * @param {FabricEvent} opt */ this.scaled = (_opt) => { if (!this.handler.transactionHandler.active) { this.handler.transactionHandler.save('scaled'); } }; /** * Rotating event object * * @param {FabricEvent} opt */ this.rotating = (opt) => { const { target } = opt; if (target.superType === 'element') { const { id } = target; const el = this.handler.elementHandler.findById(id); // update the element this.handler.elementHandler.setScaleOrAngle(el, target); } }; /** * Rotated event object * * @param {FabricEvent} opt */ this.rotated = (_opt) => { if (!this.handler.transactionHandler.active) { this.handler.transactionHandler.save('rotated'); } }; /** * Moing object at keyboard arrow key down event * * @param {KeyboardEvent} e * @returns */ this.arrowmoving = (e) => { const activeObject = this.handler.canvas.getActiveObject(); if (!activeObject) { return false; } if (activeObject.id === 'workarea') { return false; } if (e.code === constants_1.code.ARROW_UP) { activeObject.set('top', activeObject.top - 2); activeObject.setCoords(); this.handler.canvas.renderAll(); return true; } else if (e.code === constants_1.code.ARROW_DOWN) { activeObject.set('top', activeObject.top + 2); activeObject.setCoords(); this.handler.canvas.renderAll(); return true; } else if (e.code === constants_1.code.ARROW_LEFT) { activeObject.set('left', activeObject.left - 2); activeObject.setCoords(); this.handler.canvas.renderAll(); return true; } else if (e.code === constants_1.code.ARROW_RIGHT) { activeObject.set('left', activeObject.left + 2); activeObject.setCoords(); this.handler.canvas.renderAll(); return true; } if (this.handler.onModified) { this.handler.onModified(activeObject); } return true; }; /** * Zoom at mouse wheel event * * @param {FabricEvent<WheelEvent>} opt * @returns */ this.mousewheel = (opt) => { const event = opt; const { zoomEnabled } = this.handler; if (!zoomEnabled) { return; } const delta = event.e.deltaY; let zoomRatio = this.handler.canvas.getZoom(); if (delta > 0) { zoomRatio -= this.handler.zoomStep; } else { zoomRatio += this.handler.zoomStep; } this.handler.zoomHandler.zoomToPoint(new fabric_1.fabric.Point(this.handler.canvas.getWidth() / 2, this.handler.canvas.getHeight() / 2), zoomRatio); event.e.preventDefault(); event.e.stopPropagation(); }; /** * Mouse down event on object * * @param {FabricEvent<MouseEvent>} opt * @returns */ this.mousedown = (opt) => { const event = opt; const { editable } = this.handler; if (event.e.altKey && editable && !this.handler.interactionHandler.isDrawingMode()) { this.handler.interactionHandler.grab(); this.panning = true; return; } if (this.handler.interactionMode === 'grab') { this.panning = true; return; } const { target } = event; if (editable) { if (this.handler.prevTarget && this.handler.prevTarget.superType === 'link') { this.handler.prevTarget.set({ stroke: this.handler.prevTarget.originStroke || this.handler.prevTarget.stroke, }); } if (target && target.type === 'fromPort') { this.handler.linkHandler.init(target); return; } if (target && this.handler.interactionMode === 'link' && (target.type === 'toPort' || target.superType === 'node')) { let toPort; if (target.superType === 'node') { toPort = target.toPort; } else { toPort = target; } this.handler.linkHandler.generate(toPort); return; } this.handler.guidelineHandler.viewportTransform = this.handler.canvas.viewportTransform; this.handler.guidelineHandler.zoom = this.handler.canvas.getZoom(); if (this.handler.interactionMode === 'selection') { if (target && target.superType === 'link') { target.set({ stroke: target.selectedStroke || 'green', }); } this.handler.prevTarget = target; return; } if (this.handler.interactionMode === 'polygon') { if (target && this.handler.pointArray.length && target.id === this.handler.pointArray[0].id) { this.handler.drawingHandler.polygon.generate(this.handler.pointArray); } else { this.handler.drawingHandler.polygon.addPoint(event); } } else if (this.handler.interactionMode === 'line') { if (this.handler.pointArray.length && this.handler.activeLine) { this.handler.drawingHandler.line.generate(event); } else { this.handler.drawingHandler.line.addPoint(event); } } else if (this.handler.interactionMode === 'arrow') { if (this.handler.pointArray.length && this.handler.activeLine) { this.handler.drawingHandler.arrow.generate(event); } else { this.handler.drawingHandler.arrow.addPoint(event); } } } }; /** * Mouse move event on canvas * * @param {FabricEvent<MouseEvent>} opt * @returns */ this.mousemove = (opt) => { const event = opt; if (this.handler.interactionMode === 'grab' && this.panning) { this.handler.interactionHandler.moving(event.e); this.handler.canvas.requestRenderAll(); } if (!this.handler.editable && event.target) { if (event.target.superType === 'element') { return; } if (event.target.id !== 'workarea') { if (event.target !== this.handler.target) { this.handler.tooltipHandler.show(event.target); } } else { this.handler.tooltipHandler.hide(event.target); } } if (this.handler.interactionMode === 'polygon') { if (this.handler.activeLine && this.handler.activeLine.class === 'line') { const pointer = this.handler.canvas.getPointer(event.e); this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y }); const points = this.handler.activeShape.get('points'); points[this.handler.pointArray.length] = { x: pointer.x, y: pointer.y, }; this.handler.activeShape.set({ points, }); this.handler.canvas.requestRenderAll(); } } else if (this.handler.interactionMode === 'line') { if (this.handler.activeLine && this.handler.activeLine.class === 'line') { const pointer = this.handler.canvas.getPointer(event.e); this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y }); } this.handler.canvas.requestRenderAll(); } else if (this.handler.interactionMode === 'arrow') { if (this.handler.activeLine && this.handler.activeLine.class === 'line') { const pointer = this.handler.canvas.getPointer(event.e); this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y }); } this.handler.canvas.requestRenderAll(); } else if (this.handler.interactionMode === 'link') { if (this.handler.activeLine && this.handler.activeLine.class === 'line') { const pointer = this.handler.canvas.getPointer(event.e); this.handler.activeLine.set({ x2: pointer.x, y2: pointer.y }); } this.handler.canvas.requestRenderAll(); } return; }; /** * Mouse up event on canvas * * @param {FabricEvent<MouseEvent>} opt * @returns */ this.mouseup = (opt) => { const event = opt; if (this.handler.interactionMode === 'grab') { this.panning = false; return; } const { target, e } = event; if (this.handler.interactionMode === 'selection') { if (target && e.shiftKey && target.superType === 'node') { const node = target; this.handler.canvas.discardActiveObject(); const nodes = []; this.handler.nodeHandler.getNodePath(node, nodes); const activeSelection = new fabric_1.fabric.ActiveSelection(nodes, { canvas: this.handler.canvas, ...this.handler.activeSelectionOption, }); this.handler.canvas.setActiveObject(activeSelection); this.handler.canvas.requestRenderAll(); } } if (this.handler.editable && this.handler.guidelineOption.enabled) { this.handler.guidelineHandler.verticalLines.length = 0; this.handler.guidelineHandler.horizontalLines.length = 0; } this.handler.canvas.renderAll(); }; /** * Mouse out event on canvas * * @param {FabricEvent<MouseEvent>} opt */ this.mouseout = (opt) => { const event = opt; if (!event.target) { this.handler.tooltipHandler.hide(); } }; /** * Selection event event on canvas * * @param {FabricEvent} opt */ this.selection = (opt) => { const { onSelect, activeSelectionOption } = this.handler; const target = opt.target; if (target && target.type === 'activeSelection') { target.set({ ...activeSelectionOption, }); } if (onSelect) { onSelect(target); } }; /** * Called resize event on canvas * * @param {number} nextWidth * @param {number} nextHeight * @returns */ this.resize = (nextWidth, nextHeight) => { this.handler.canvas.setWidth(nextWidth).setHeight(nextHeight); this.handler.canvas.setBackgroundColor(this.handler.canvasOption.backgroundColor, this.handler.canvas.renderAll.bind(this.handler.canvas)); if (!this.handler.workarea) { return; } const diffWidth = nextWidth / 2 - this.handler.width / 2; const diffHeight = nextHeight / 2 - this.handler.height / 2; this.handler.width = nextWidth; this.handler.height = nextHeight; if (this.handler.workarea.layout === 'fixed') { this.handler.canvas.centerObject(this.handler.workarea); this.handler.workarea.setCoords(); if (this.handler.gridOption.enabled) { return; } this.handler.canvas.getObjects().forEach((obj) => { if (obj.id !== 'workarea') { const left = obj.left + diffWidth; const top = obj.top + diffHeight; obj.set({ left, top, }); obj.setCoords(); if (obj.superType === 'element') { const { id } = obj; const el = this.handler.elementHandler.findById(id); // update the element this.handler.elementHandler.setPosition(el, obj); } } }); this.handler.canvas.requestRenderAll(); return; } if (this.handler.workarea.layout === 'responsive') { const { scaleX } = this.handler.workareaHandler.calculateScale(); const center = this.handler.canvas.getCenter(); const deltaPoint = new fabric_1.fabric.Point(diffWidth, diffHeight); this.handler.canvas.relativePan(deltaPoint); this.handler.zoomHandler.zoomToPoint(new fabric_1.fabric.Point(center.left, center.top), scaleX); return; } const scaleX = nextWidth / this.handler.workarea.width; const scaleY = nextHeight / this.handler.workarea.height; const diffScaleX = nextWidth / (this.handler.workarea.width * this.handler.workarea.scaleX); const diffScaleY = nextHeight / (this.handler.workarea.height * this.handler.workarea.scaleY); this.handler.workarea.set({ scaleX, scaleY, }); this.handler.canvas.getObjects().forEach((obj) => { const { id } = obj; if (obj.id !== 'workarea') { const left = obj.left * diffScaleX; const top = obj.top * diffScaleY; const newScaleX = obj.scaleX * diffScaleX; const newScaleY = obj.scaleY * diffScaleY; obj.set({ scaleX: newScaleX, scaleY: newScaleY, left, top, }); obj.setCoords(); if (obj.superType === 'element') { const video = obj; const { width, height } = obj; const el = this.handler.elementHandler.findById(id); this.handler.elementHandler.setSize(el, obj); if (video.player) { video.player.setPlayerSize(width, height); } this.handler.elementHandler.setPosition(el, obj); } } }); this.handler.canvas.renderAll(); }; /** * Paste event on canvas * * @param {ClipboardEvent} e * @returns */ this.paste = async (e) => { if (this.handler.canvas.wrapperEl !== document.activeElement) { return false; } if (e.preventDefault) { e.preventDefault(); } if (e.stopPropagation) { e.stopPropagation(); } const clipboardData = e.clipboardData; if (clipboardData.types.length) { clipboardData.types.forEach((clipboardType) => { if (clipboardType === 'text/plain') { const textPlain = clipboardData.getData('text/plain'); try { const objects = JSON.parse(textPlain); const { gridOption: { grid = 10 }, isCut, } = this.handler; const padding = isCut ? 0 : grid; if (objects && Array.isArray(objects)) { const filteredObjects = objects.filter(obj => obj !== null); if (filteredObjects.length === 1) { const obj = filteredObjects[0]; if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) { return; } obj.left = obj.properties.left + padding; obj.top = obj.properties.top + padding; const createdObj = this.handler.add(obj, false, true); this.handler.canvas.setActiveObject(createdObj); this.handler.canvas.requestRenderAll(); this.handler.onAdd?.(createdObj); } else { const nodes = []; const targets = []; objects.forEach(obj => { if (!obj) { return; } if (obj.superType === 'link') { obj.fromNodeId = nodes[obj.fromNodeIndex].id; obj.toNodeId = nodes[obj.toNodeIndex].id; } else { obj.left = obj.properties.left + padding; obj.top = obj.properties.top + padding; } const createdObj = this.handler.add(obj, false, true); if (obj.superType === 'node') { nodes.push(createdObj); } else { targets.push(createdObj); } }); const activeSelection = new fabric_1.fabric.ActiveSelection(nodes.length ? nodes : targets, { canvas: this.handler.canvas, ...this.handler.activeSelectionOption, }); this.handler.canvas.setActiveObject(activeSelection); this.handler.canvas.requestRenderAll(); this.handler.onAdd?.(activeSelection); } if (!this.handler.transactionHandler.active) { this.handler.transactionHandler.save('paste'); } this.handler.isCut = false; this.handler.copy(); } } catch (error) { console.error(error); // const item = { // id: uuv4id(), // type: 'textbox', // text: textPlain, // }; // this.handler.add(item, true); } } else if (clipboardType === 'text/html') { // Todo ... // const textHtml = clipboardData.getData('text/html'); // console.log(textHtml); } else if (clipboardType === 'Files') { // Array.from(clipboardData.files).forEach((file) => { // const { type } = file; // if (type === 'image/png' || type === 'image/jpeg' || type === 'image/jpg') { // const item = { // id: uuid(), // type: 'image', // file, // superType: 'image', // }; // this.handler.add(item, true); // } else { // console.error('Not supported file type'); // } // }); } }); } return true; }; /** * Keydown event on document * * @param {KeyboardEvent} e */ this.keydown = (e) => { const { keyEvent, editable } = this.handler; if (!Object.keys(keyEvent).length) { return; } const { clipboard, grab } = keyEvent; if (this.handler.interactionHandler.isDrawingMode()) { if (this.handler.shortcutHandler.isEscape(e)) { if (this.handler.interactionMode === 'polygon') { this.handler.drawingHandler.polygon.finish(); } else if (this.handler.interactionMode === 'line') { this.handler.drawingHandler.line.finish(); } else if (this.handler.interactionMode === 'arrow') { this.handler.drawingHandler.arrow.finish(); } else if (this.handler.interactionMode === 'link') { this.handler.linkHandler.finish(); } } return; } if (this.handler.shortcutHandler.isW(e) && grab) { this.code = e.code; this.handler.interactionHandler.grab(); return; } if (e.altKey && editable && grab) { this.handler.interactionHandler.grab(); return; } if (this.handler.shortcutHandler.isEscape(e)) { if (this.handler.interactionMode === 'selection') { this.handler.canvas.discardActiveObject(); this.handler.canvas.renderAll(); } this.handler.tooltipHandler.hide(); } if (this.handler.canvas.wrapperEl !== document.activeElement) { return; } if (editable) { if (this.handler.shortcutHandler.isQ(e)) { this.code = e.code; } else if (this.handler.shortcutHandler.isDelete(e)) { this.handler.remove(); } else if (this.handler.shortcutHandler.isArrow(e)) { this.arrowmoving(e); } else if (this.handler.shortcutHandler.isCtrlA(e)) { e.preventDefault(); this.handler.selectAll(); } else if (this.handler.shortcutHandler.isCtrlC(e)) { e.preventDefault(); this.handler.copy(); } else if (this.handler.shortcutHandler.isCtrlV(e) && !clipboard) { e.preventDefault(); this.handler.paste(); } else if (this.handler.shortcutHandler.isCtrlX(e)) { e.preventDefault(); this.handler.cut(); } else if (this.handler.shortcutHandler.isCtrlZ(e)) { e.preventDefault(); this.handler.transactionHandler.undo(); } else if (this.handler.shortcutHandler.isCtrlY(e)) { e.preventDefault(); this.handler.transactionHandler.redo(); } else if (this.handler.shortcutHandler.isPlus(e)) { e.preventDefault(); this.handler.zoomHandler.zoomIn(); } else if (this.handler.shortcutHandler.isMinus(e)) { e.preventDefault(); this.handler.zoomHandler.zoomOut(); } else if (this.handler.shortcutHandler.isO(e)) { e.preventDefault(); this.handler.zoomHandler.zoomOneToOne(); } else if (this.handler.shortcutHandler.isP(e)) { e.preventDefault(); this.handler.zoomHandler.zoomToFit(); } return; } return; }; /** * Key up event on canvas * * @param {KeyboardEvent} _e */ this.keyup = (e) => { if (this.handler.interactionHandler.isDrawingMode()) { return; } if (!this.handler.shortcutHandler.isW(e)) { this.handler.interactionHandler.selection(); } }; /** * Context menu event on canvas * * @param {MouseEvent} e */ this.contextmenu = (e) => { e.preventDefault(); const { editable, onContext } = this.handler; if (editable && onContext) { const target = this.handler.canvas.findTarget(e, false); if (target && target.type !== 'activeSelection') { this.handler.select(target); } this.handler.contextmenuHandler.show(e, target); } }; /** * Mouse down event on canvas * * @param {MouseEvent} _e */ this.onmousedown = (_e) => { this.handler.contextmenuHandler.hide(); }; this.handler = handler; this.initialize(); } /** * Attch event on document * */ initialize() { if (this.handler.editable) { // @ts-ignore this.handler.canvas.on({ 'object:modified': this.modified, 'object:scaling': this.scaling, 'object:scaled': this.scaled, 'object:moving': this.moving, 'object:moved': this.moved, 'object:rotating': this.rotating, 'object:rotated': this.rotated, 'mouse:wheel': this.mousewheel, 'mouse:down': this.mousedown, 'mouse:move': this.mousemove, 'mouse:up': this.mouseup, 'selection:cleared': this.selection, 'selection:created': this.selection, 'selection:updated': this.selection, }); } else { // @ts-ignore this.handler.canvas.on({ 'mouse:down': this.mousedown, 'mouse:move': this.mousemove, 'mouse:out': this.mouseout, 'mouse:up': this.mouseup, 'mouse:wheel': this.mousewheel, }); } this.handler.canvas.wrapperEl.tabIndex = 1000; this.handler.canvas.wrapperEl.addEventListener('keydown', this.keydown, false); this.handler.canvas.wrapperEl.addEventListener('keyup', this.keyup, false); this.handler.canvas.wrapperEl.addEventListener('mousedown', this.onmousedown, false); this.handler.canvas.wrapperEl.addEventListener('contextmenu', this.contextmenu, false); if (this.handler.keyEvent.clipboard) { document.addEventListener('paste', this.paste, false); } } } exports.default = EventHandler;