UNPKG

roiact

Version:
618 lines (551 loc) 20.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _Config = _interopRequireDefault(require("../../Config")); require("fabric"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } // from "../../node_modules/fabric/dist/fabric.js"; var Canvas = /*#__PURE__*/function () { /** * Constructs a Canvas instance, initializes fabric canvas */ function Canvas(canvasId, _ref) { var opacity = _ref.opacity, onShapeRemove = _ref.onShapeRemove, setDrawing = _ref.setDrawing; _classCallCheck(this, Canvas); this.FCanvas = new window.fabric.Canvas(canvasId, { backgroundColor: "rgba(255, 255, 255, ".concat((100 - opacity) / 100, ")") }); this.drawing = false; this.drawingData = {}; this.storeShapes = []; this.shapeMode = null; this.threshold = 50; this.shapesCounter = 0; this.polygonKeyListener = this.polygonKeyListener.bind(this); this.cancelKeyListener = this.cancelKeyListener.bind(this); this.onShapeRemove = onShapeRemove; this.setDrawing = setDrawing; window.fabric.util.addListener(window, 'keyup', this.cancelKeyListener); } _createClass(Canvas, [{ key: "cancelKeyListener", value: function cancelKeyListener(e) { var _this = this; if (e.keyCode === 46) { // cancel if (this.drawing) { this.drawingData = {}; this.drawing = false; this.redraw(); this.setDrawing(false); } else { var shape = this.getActiveSelection(); if (shape) { if (shape._objects) { shape._objects.forEach(function (s) { _this.FCanvas.remove(s); _this.onShapeRemove(s.id); }); } else { this.FCanvas.remove(shape); this.onShapeRemove(shape.id); } } } } } }, { key: "deinitialize", value: function deinitialize() { window.fabric.util.removeListener(window, 'keyup', this.cancelKeyListener); } /** * Updates the redux shapes, used to redraw everything in sync with * the store * @param shapes array of stored shapes */ }, { key: "setStoreShapes", value: function setStoreShapes(shapes) { this.storeShapes = _toConsumableArray(shapes); this.shapesCounter = shapes.reduce(function (acc, item) { return Math.max(acc, item.id); }, 0); } /** * Updates the redux shapes, used to redraw everything in sync with * the store * @param mode inboud|outbound */ }, { key: "setShapeMode", value: function setShapeMode(mode) { this.shapeMode = mode; } }, { key: "setShapeThreshold", value: function setShapeThreshold(t) { this.threshold = t; } /** * Sets the opacity of the canvas background * @param opacity */ }, { key: "setOpacity", value: function setOpacity(opacity) { this.FCanvas.setBackgroundColor("rgba(255, 255, 255, ".concat((100 - opacity) / 100, ")"), this.FCanvas.renderAll.bind(this.FCanvas)); } }, { key: "resize", value: function resize(size) { if (size) { this.FCanvas.setWidth(size.width); this.FCanvas.setHeight(size.height); this.redraw(); } } /** * Redraw the canvas * It removes all current shapes and redraws the stored ones */ }, { key: "redraw", value: function redraw() { var _this2 = this; if (this.storeShapes) { var _this$FCanvas; (_this$FCanvas = this.FCanvas).remove.apply(_this$FCanvas, _toConsumableArray(this.FCanvas.getObjects())); this.storeShapes.forEach(function (shape) { return _this2.FCanvas.add(_this2.shapeFromData(_this2.redux2fabric(_objectSpread(_objectSpread({}, shape.data), {}, { id: shape.id, name: shape.name, type: shape.type, mode: shape.mode, threshold: shape.threshold }), _this2.FCanvas.width, _this2.FCanvas.height))); }); } } /** * Provides the fabric object class given its configuration * * @param data shape configuration * @return fabric object instance */ }, { key: "shapeFromData", value: function shapeFromData(data) { switch (data.type) { case 'rect': return new window.fabric.Rect(_objectSpread({}, data)); case 'polygon': return new window.fabric.Polygon(data.get('points'), _objectSpread({}, data)); default: return null; } } /** * Number of shapes currently on canvas * @return number of shapes */ }, { key: "shapesCount", value: function shapesCount() { return this.FCanvas.getObjects().length; } /** * Shapes on canvas * @return fabric shapes instances */ }, { key: "shapes", value: function shapes() { return this.FCanvas.getObjects(); } /** * Enables selection on canvas */ }, { key: "enableSelection", value: function enableSelection() { this.FCanvas.selection = true; } /** * Disables selection on canvas * It's better to disable selection while drawing, because * can mess up events */ }, { key: "disableSelection", value: function disableSelection() { this.FCanvas.selection = false; } /** * Provides the active selection object * return selection */ }, { key: "getActiveSelection", value: function getActiveSelection() { return this.FCanvas.getActiveObject(); } /** * Provides the active selection object * return selection */ }, { key: "setActiveSelection", value: function setActiveSelection(id) { this.FCanvas.setActiveObject(this.shapes().filter(function (obj) { return obj.id === id; })[0]); this.FCanvas.renderAll(); } /** * Removes a shape from canvas given the id */ }, { key: "removeShape", value: function removeShape(id) { this.FCanvas.remove(this.shapes().filter(function (obj) { return obj.id === id; })[0]); } /** * Draws a shape in the dirfferent phases: * - init (mousdown) * - draw (mousemove) * - end (mouseup) * * @param shapeType * @param eventType mousedown|mousemove * @param isDrawing * @param e the event object */ }, { key: "drawShape", value: function drawShape(shapeType, eventType, e, handleDrawEnd) { var phase; if (eventType === 'mousemove' && !this.drawing) return; if (eventType === 'mousedown' && !this.drawing) { this.setDrawing(true); phase = 'init'; this.drawing = true; } else { phase = 'draw'; } switch (shapeType) { case 'rect': return this["".concat(phase, "Rectangle")](eventType, e, handleDrawEnd); case 'polygon': return this["".concat(phase, "Polygon")](eventType, e, handleDrawEnd); default: break; } } // mousedown }, { key: "initRectangle", value: function initRectangle(eventType, e) { var mouse = this.FCanvas.getPointer(e.e); var shape = new window.fabric.Rect({ width: 0, height: 0, left: mouse.x, top: mouse.y, fill: _Config.default.canvas["".concat(this.shapeMode, "Fill")], stroke: _Config.default.canvas["".concat(this.shapeMode, "Stroke")], strokeUniform: true, id: ++this.shapesCounter, type: 'rect', name: "rectangle_".concat(this.shapesCounter), mode: this.shapeMode, threshold: this.threshold }); this.drawingData.x = mouse.x; this.drawingData.y = mouse.y; this.drawingData.shape = shape; this.FCanvas.add(shape); } // mousemove }, { key: "drawRectangle", value: function drawRectangle(eventType, e, handleDrawEnd) { if (eventType === 'mousedown') { return this.endRectangle(handleDrawEnd); } var mouse = this.FCanvas.getPointer(e.e); var w = Math.abs(mouse.x - this.drawingData.x); var h = Math.abs(mouse.y - this.drawingData.y); if (!w || !h) { return false; } var left = Math.min(this.drawingData.x, mouse.x); var top = Math.min(this.drawingData.y, mouse.y); this.drawingData.shape.set('width', w).set('height', h).set('left', left).set('top', top); this.FCanvas.renderAll(); } }, { key: "endRectangle", value: function endRectangle(handleDrawEnd) { this.setDrawing(false); var shape = this.drawingData.shape; if (shape.width > 1 && shape.height > 1) { // we create a new copy of the shape because we're going // to clean the shape used during the mouse events var finalShape = this.shapeFromData(shape); this.FCanvas.remove(shape); this.FCanvas.add(finalShape); // set the shape as active this.FCanvas.setActiveObject(finalShape); this.drawing = false; this.drawingData = {}; handleDrawEnd(shape); } else { this.FCanvas.remove(shape); this.drawingData = {}; --this.shapesCounter; this.drawing = false; } } }, { key: "polygonKeyListener", value: function polygonKeyListener(e) { if (e.keyCode === 27) { this.drawing = false; this.endPolygon(this.drawingData.handleDrawEnd); } } // mousedown }, { key: "initPolygon", value: function initPolygon(eventType, e, handleDrawEnd) { var mouse = this.FCanvas.getPointer(e.e); this.drawingData.handleDrawEnd = handleDrawEnd; window.fabric.util.addListener(window, 'keyup', this.polygonKeyListener); var shape = new window.fabric.Polygon([{ x: mouse.x, y: mouse.y }, { x: mouse.x + 10, y: mouse.y + 10 }], { fill: _Config.default.canvas["".concat(this.shapeMode, "Fill")], stroke: _Config.default.canvas["".concat(this.shapeMode, "Stroke")], strokeUniform: true, selectable: false, id: ++this.shapesCounter, type: 'polygon', name: "polygon_".concat(this.shapesCounter), mode: this.shapeMode, threshold: this.threshold }); this.drawingData.x = mouse.x; this.drawingData.y = mouse.y; this.drawingData.shape = shape; this.FCanvas.add(shape); } }, { key: "drawPolygon", value: function drawPolygon(eventType, e, handleDrawEnd) { var mouse = this.FCanvas.getPointer(e.e); var points = this.drawingData.shape.get('points'); if (eventType === 'mousemove') { points.pop(); } else {} points.push({ x: mouse.x, y: mouse.y }); var fill = this.drawingData.shape.get('fill'); var stroke = this.drawingData.shape.get('stroke'); var threshold = this.drawingData.shape.get('threshold'); var id = this.drawingData.shape.get('id'); var type = this.drawingData.shape.get('type'); var name = this.drawingData.shape.get('name'); var mode = this.drawingData.shape.get('mode'); this.FCanvas.remove(this.drawingData.shape); this.drawingData.shape = new window.fabric.Polygon(points, { fill: fill, stroke: stroke, id: id, type: type, name: name, mode: mode, strokeUniform: true, selectable: false, threshold: threshold }); this.FCanvas.add(this.drawingData.shape); this.FCanvas.renderAll(); } }, { key: "endPolygon", value: function endPolygon(handleDrawEnd) { window.fabric.util.removeListener(window, 'keyup', this.polygonKeyListener); this.setDrawing(false); var shape = this.drawingData.shape; var fill = shape.get('fill'); var id = shape.get('id'); var type = shape.get('type'); var name = shape.get('name'); var mode = shape.get('mode'); var threshold = shape.get('threshold'); var points = shape.points; // points.pop() // uncomment to remove last mousemove point if (points.length > 2) { var finalShape = this.shapeFromData(shape); finalShape.set('fill', fill); finalShape.set('id', id); finalShape.set('type', type); finalShape.set('name', name); finalShape.set('mode', mode); finalShape.set('threshold', threshold); finalShape.set('selectable', true); this.FCanvas.remove(shape); this.drawingData = {}; this.FCanvas.add(finalShape); // set the shape as active this.FCanvas.setActiveObject(finalShape); this.drawing = false; handleDrawEnd(shape); } else { this.FCanvas.remove(shape); this.drawingData = {}; --this.shapesCounter; this.drawing = false; } } /** * Conversion function used to store a fabric shape * Fabric uses absolute (in px) positioning and measuring of a shape, i.e. a rectangle * is described by (top, left) position of the top left corner, width and height. * Also fabric uses two scale factors which act on width and height when the shape * is resized. * I can't store such measures in absolute px, we need to work in percentage in order * to be able to draw above every image size. Also I don't like to use the scaling factors, * so I'll re-calculate width and height and set them to 1 * * @param shape the fabric object class * @param canvasWidth the canvas width * @param canvasHeight the canvas height * @return converted object * */ }, { key: "fabric2redux", value: function fabric2redux(shape) { var _this3 = this; shape = shape.toJSON(); var left = shape.left * 100 / this.FCanvas.width; var top = shape.top * 100 / this.FCanvas.height; var width = shape.width * shape.scaleX * 100 / this.FCanvas.width; var height = shape.height * shape.scaleY * 100 / this.FCanvas.height; if (shape.type === 'polygon') { shape.points = shape.points.map(function (p) { return { x: p.x * shape.scaleX * 100 / _this3.FCanvas.width, y: p.y * shape.scaleY * 100 / _this3.FCanvas.height }; }); } return _objectSpread(_objectSpread({}, shape), {}, { left: left, top: top, width: width, height: height, scaleX: 1, scaleY: 1 }); } /** * Conversion function used create a fabric shape from the stored redux object * * @param shape the stored redux object * @param canvasWidth the canvas width * @param canvasHeight the canvas height * @return the fabric shape object instance * */ }, { key: "redux2fabric", value: function redux2fabric(shape) { var _this4 = this; var left = Math.round(shape.left * this.FCanvas.width / 100); var top = Math.round(shape.top * this.FCanvas.height / 100); var width = Math.round(shape.width * this.FCanvas.width / 100); var height = Math.round(shape.height * this.FCanvas.height / 100); if (shape.type === 'rect') { return new window.fabric.Rect(_objectSpread(_objectSpread({}, shape), {}, { left: left, top: top, width: width, height: height })); } else if (shape.type === 'polygon') { shape.points = shape.points.map(function (p) { return { x: p.x * _this4.FCanvas.width / 100, y: p.y * _this4.FCanvas.height / 100 }; }); return new window.fabric.Polygon(shape.points, _objectSpread(_objectSpread({}, shape), {}, { left: left, top: top, width: width, height: height })); } } // all listener controllers }, { key: "addMouseListeners", value: function addMouseListeners(cbMousedown, cbMousemove, cbMouseup) { this.FCanvas.on('mouse:down', cbMousedown); this.FCanvas.on('mouse:move', cbMousemove); } }, { key: "removeMouseListeners", value: function removeMouseListeners() { this.FCanvas.off('mouse:down'); this.FCanvas.off('mouse:move'); } }, { key: "addSelectionListeners", value: function addSelectionListeners(cbCreated, cbUpdated, cbCleared) { this.FCanvas.on('selection:created', cbCreated); this.FCanvas.on('selection:updated', cbUpdated); this.FCanvas.on('selection:cleared', cbCleared); } }, { key: "removeSelectionListeners", value: function removeSelectionListeners() { this.FCanvas.off('selection:created'); this.FCanvas.off('selection:updated'); this.FCanvas.off('selection:cleared'); } }, { key: "addShapeUpdateListener", value: function addShapeUpdateListener(cb) { this.FCanvas.on('object:modified', cb); } }, { key: "removeShapeUpdateListener", value: function removeShapeUpdateListener() { this.FCanvas.off('object:modified'); } }]); return Canvas; }(); var _default = Canvas; exports.default = _default;