roiact
Version:
618 lines (551 loc) • 20.5 kB
JavaScript
"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;