@erase2d/fabric 1.1.10 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/dist/src/erase.js +36 -0
- package/dist/core/dist/src/erase.js.map +1 -0
- package/dist/fabric/src/ClippingGroup.js +46 -0
- package/dist/fabric/src/ClippingGroup.js.map +1 -0
- package/dist/fabric/src/EraserBrush.js +433 -0
- package/dist/fabric/src/EraserBrush.js.map +1 -0
- package/dist/fabric/src/ErasingEffect.js +118 -0
- package/dist/fabric/src/ErasingEffect.js.map +1 -0
- package/dist/fabric/src/isTransparent.js +52 -0
- package/dist/fabric/src/isTransparent.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/src/ClippingGroup.d.ts +8 -0
- package/dist/src/ClippingGroup.d.ts.map +1 -0
- package/dist/src/EraserBrush.d.ts +114 -0
- package/dist/src/EraserBrush.d.ts.map +1 -0
- package/dist/src/ErasingEffect.d.ts +27 -0
- package/dist/src/ErasingEffect.d.ts.map +1 -0
- package/dist/src/isTransparent.d.ts +7 -0
- package/dist/src/isTransparent.d.ts.map +1 -0
- package/package.json +5 -2
- package/rollup.config.mjs +0 -42
- package/tsconfig.json +0 -8
- package/types/fabric.d.ts +0 -22
- package/types/index.d.ts +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var drawImage = function drawImage(destination, source) {
|
|
2
|
+
var globalCompositeOperation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'source-over';
|
|
3
|
+
destination.save();
|
|
4
|
+
destination.imageSmoothingEnabled = true;
|
|
5
|
+
destination.imageSmoothingQuality = 'high';
|
|
6
|
+
destination.globalCompositeOperation = globalCompositeOperation;
|
|
7
|
+
destination.resetTransform();
|
|
8
|
+
destination.drawImage(source.canvas, 0, 0);
|
|
9
|
+
destination.restore();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param destination context to erase
|
|
15
|
+
* @param source context on which the path is drawn upon
|
|
16
|
+
* @param erasingEffect effect to apply to {@link source} after clipping {@link destination}:
|
|
17
|
+
* - drawing all non erasable visuals to achieve a selective erasing effect.
|
|
18
|
+
* - drawing all erasable visuals without their erasers to achieve an undo erasing effect.
|
|
19
|
+
*/
|
|
20
|
+
var erase = function erase(destination, source, erasingEffect) {
|
|
21
|
+
// clip destination
|
|
22
|
+
drawImage(destination, source, 'destination-out');
|
|
23
|
+
|
|
24
|
+
// draw erasing effect
|
|
25
|
+
if (erasingEffect) {
|
|
26
|
+
drawImage(source, erasingEffect, 'source-in');
|
|
27
|
+
} else {
|
|
28
|
+
source.save();
|
|
29
|
+
source.resetTransform();
|
|
30
|
+
source.clearRect(0, 0, source.canvas.width, source.canvas.height);
|
|
31
|
+
source.restore();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { drawImage, erase };
|
|
36
|
+
//# sourceMappingURL=erase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erase.js","sources":["../../../../../core/dist/src/erase.js"],"sourcesContent":["var drawImage = function drawImage(destination, source) {\n var globalCompositeOperation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'source-over';\n destination.save();\n destination.imageSmoothingEnabled = true;\n destination.imageSmoothingQuality = 'high';\n destination.globalCompositeOperation = globalCompositeOperation;\n destination.resetTransform();\n destination.drawImage(source.canvas, 0, 0);\n destination.restore();\n};\n\n/**\n *\n * @param destination context to erase\n * @param source context on which the path is drawn upon\n * @param erasingEffect effect to apply to {@link source} after clipping {@link destination}:\n * - drawing all non erasable visuals to achieve a selective erasing effect.\n * - drawing all erasable visuals without their erasers to achieve an undo erasing effect.\n */\nvar erase = function erase(destination, source, erasingEffect) {\n // clip destination\n drawImage(destination, source, 'destination-out');\n\n // draw erasing effect\n if (erasingEffect) {\n drawImage(source, erasingEffect, 'source-in');\n } else {\n source.save();\n source.resetTransform();\n source.clearRect(0, 0, source.canvas.width, source.canvas.height);\n source.restore();\n }\n};\n\nexport { drawImage, erase };\n//# sourceMappingURL=erase.js.map\n"],"names":["drawImage","destination","source","globalCompositeOperation","arguments","length","undefined","save","imageSmoothingEnabled","imageSmoothingQuality","resetTransform","canvas","restore","erase","erasingEffect","clearRect","width","height"],"mappings":"AAAO,IAAMA,SAAS,GAAG,SAAZA,SAASA,CACpBC,WAAqC,EACrCC,MAAgC,EAE7B;AAAA,EAAA,IADHC,wBAAkD,GAAAC,SAAA,CAAAC,MAAA,GAAA,CAAA,IAAAD,SAAA,CAAA,CAAA,CAAA,KAAAE,SAAA,GAAAF,SAAA,CAAA,CAAA,CAAA,GAAG,aAAa;EAElEH,WAAW,CAACM,IAAI,EAAE;EAClBN,WAAW,CAACO,qBAAqB,GAAG,IAAI;EACxCP,WAAW,CAACQ,qBAAqB,GAAG,MAAM;EAC1CR,WAAW,CAACE,wBAAwB,GAAGA,wBAAwB;EAC/DF,WAAW,CAACS,cAAc,EAAE;EAC5BT,WAAW,CAACD,SAAS,CAACE,MAAM,CAACS,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;EAC1CV,WAAW,CAACW,OAAO,EAAE;AACvB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,IAAMC,KAAK,GAAG,SAARA,KAAKA,CAChBZ,WAAqC,EACrCC,MAAgC,EAChCY,aAAwC,EACrC;AACH;AACAd,EAAAA,SAAS,CAACC,WAAW,EAAEC,MAAM,EAAE,iBAAiB,CAAC;;AAEjD;AACA,EAAA,IAAIY,aAAa,EAAE;AACjBd,IAAAA,SAAS,CAACE,MAAM,EAAEY,aAAa,EAAE,WAAW,CAAC;AAC/C,EAAA,CAAC,MAAM;IACLZ,MAAM,CAACK,IAAI,EAAE;IACbL,MAAM,CAACQ,cAAc,EAAE;AACvBR,IAAAA,MAAM,CAACa,SAAS,CAAC,CAAC,EAAE,CAAC,EAAEb,MAAM,CAACS,MAAM,CAACK,KAAK,EAAEd,MAAM,CAACS,MAAM,CAACM,MAAM,CAAC;IACjEf,MAAM,CAACU,OAAO,EAAE;AAClB,EAAA;AACF;;;;"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineProperty as _defineProperty, inherits as _inherits, createClass as _createClass, classCallCheck as _classCallCheck, callSuper as _callSuper, objectSpread2 as _objectSpread2, assertThisInitialized as _assertThisInitialized } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { classRegistry, LayoutManager, FixedLayout, Path, Group } from 'fabric';
|
|
3
|
+
|
|
4
|
+
var ClippingGroup = /*#__PURE__*/function (_Group) {
|
|
5
|
+
_inherits(ClippingGroup, _Group);
|
|
6
|
+
function ClippingGroup(objects, options) {
|
|
7
|
+
var _this;
|
|
8
|
+
_classCallCheck(this, ClippingGroup);
|
|
9
|
+
_this = _callSuper(this, ClippingGroup, [objects, _objectSpread2({
|
|
10
|
+
originX: 'center',
|
|
11
|
+
originY: 'center',
|
|
12
|
+
left: 0,
|
|
13
|
+
top: 0,
|
|
14
|
+
layoutManager: new LayoutManager(new FixedLayout())
|
|
15
|
+
}, options)]);
|
|
16
|
+
_defineProperty(_assertThisInitialized(_this), "blockErasing", false);
|
|
17
|
+
return _this;
|
|
18
|
+
}
|
|
19
|
+
_createClass(ClippingGroup, [{
|
|
20
|
+
key: "drawObject",
|
|
21
|
+
value: function drawObject(ctx) {
|
|
22
|
+
var paths = [];
|
|
23
|
+
var objects = [];
|
|
24
|
+
this._objects.forEach(function (object) {
|
|
25
|
+
return (object instanceof Path ? paths : objects).push(object);
|
|
26
|
+
});
|
|
27
|
+
ctx.save();
|
|
28
|
+
ctx.fillStyle = 'black';
|
|
29
|
+
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
|
|
30
|
+
ctx.restore();
|
|
31
|
+
!this.blockErasing && paths.forEach(function (path) {
|
|
32
|
+
path.render(ctx);
|
|
33
|
+
});
|
|
34
|
+
objects.forEach(function (object) {
|
|
35
|
+
object.globalCompositeOperation = object.inverted ? 'destination-out' : 'source-in';
|
|
36
|
+
object.render(ctx);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}]);
|
|
40
|
+
return ClippingGroup;
|
|
41
|
+
}(Group);
|
|
42
|
+
_defineProperty(ClippingGroup, "type", 'clipping');
|
|
43
|
+
classRegistry.setClass(ClippingGroup);
|
|
44
|
+
|
|
45
|
+
export { ClippingGroup };
|
|
46
|
+
//# sourceMappingURL=ClippingGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClippingGroup.js","sources":["../../../src/ClippingGroup.ts"],"sourcesContent":["import {\n FabricObject,\n FixedLayout,\n Group,\n GroupProps,\n LayoutManager,\n Path,\n classRegistry,\n} from 'fabric';\n\nexport class ClippingGroup extends Group {\n static type = 'clipping';\n\n private blockErasing = false;\n\n constructor(objects: FabricObject[], options: Partial<GroupProps>) {\n super(objects, {\n originX: 'center',\n originY: 'center',\n left: 0,\n top: 0,\n layoutManager: new LayoutManager(new FixedLayout()),\n ...options,\n });\n }\n\n drawObject(ctx: CanvasRenderingContext2D) {\n const paths: Path[] = [];\n const objects: FabricObject[] = [];\n this._objects.forEach((object) =>\n (object instanceof Path ? paths : objects).push(object)\n );\n\n ctx.save();\n ctx.fillStyle = 'black';\n ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);\n ctx.restore();\n\n !this.blockErasing &&\n paths.forEach((path) => {\n path.render(ctx);\n });\n\n objects.forEach((object) => {\n object.globalCompositeOperation = object.inverted\n ? 'destination-out'\n : 'source-in';\n object.render(ctx);\n });\n }\n}\n\nclassRegistry.setClass(ClippingGroup);\n"],"names":["ClippingGroup","_Group","_inherits","objects","options","_this","_classCallCheck","_callSuper","_objectSpread","originX","originY","left","top","layoutManager","LayoutManager","FixedLayout","_defineProperty","_assertThisInitialized","_createClass","key","value","drawObject","ctx","paths","_objects","forEach","object","Path","push","save","fillStyle","fillRect","width","height","restore","blockErasing","path","render","globalCompositeOperation","inverted","Group","classRegistry","setClass"],"mappings":";;;AAUA,IAAaA,aAAa,0BAAAC,MAAA,EAAA;EAAAC,SAAA,CAAAF,aAAA,EAAAC,MAAA,CAAA;AAKxB,EAAA,SAAAD,aAAAA,CAAYG,OAAuB,EAAEC,OAA4B,EAAE;AAAA,IAAA,IAAAC,KAAA;AAAAC,IAAAA,eAAA,OAAAN,aAAA,CAAA;AACjEK,IAAAA,KAAA,GAAAE,UAAA,CAAA,IAAA,EAAAP,aAAA,EAAA,CAAMG,OAAO,EAAAK,cAAA,CAAA;AACXC,MAAAA,OAAO,EAAE,QAAQ;AACjBC,MAAAA,OAAO,EAAE,QAAQ;AACjBC,MAAAA,IAAI,EAAE,CAAC;AACPC,MAAAA,GAAG,EAAE,CAAC;AACNC,MAAAA,aAAa,EAAE,IAAIC,aAAa,CAAC,IAAIC,WAAW,EAAE;AAAC,KAAA,EAChDX,OAAO,CAAA,CAAA,CAAA;AACTY,IAAAA,eAAA,CAAAC,sBAAA,CAAAZ,KAAA,mBAVkB,KAAK,CAAA;AAAA,IAAA,OAAAA,KAAA;AAW5B,EAAA;AAACa,EAAAA,YAAA,CAAAlB,aAAA,EAAA,CAAA;IAAAmB,GAAA,EAAA,YAAA;AAAAC,IAAAA,KAAA,EAED,SAAAC,UAAAA,CAAWC,GAA6B,EAAE;MACxC,IAAMC,KAAa,GAAG,EAAE;MACxB,IAAMpB,OAAuB,GAAG,EAAE;AAClC,MAAA,IAAI,CAACqB,QAAQ,CAACC,OAAO,CAAC,UAACC,MAAM,EAAA;AAAA,QAAA,OAC3B,CAACA,MAAM,YAAYC,IAAI,GAAGJ,KAAK,GAAGpB,OAAO,EAAEyB,IAAI,CAACF,MAAM,CAAC;AAAA,MAAA,CACzD,CAAC;MAEDJ,GAAG,CAACO,IAAI,EAAE;MACVP,GAAG,CAACQ,SAAS,GAAG,OAAO;MACvBR,GAAG,CAACS,QAAQ,CAAC,CAAC,IAAI,CAACC,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAACC,MAAM,GAAG,CAAC,EAAE,IAAI,CAACD,KAAK,EAAE,IAAI,CAACC,MAAM,CAAC;MACxEX,GAAG,CAACY,OAAO,EAAE;MAEb,CAAC,IAAI,CAACC,YAAY,IAChBZ,KAAK,CAACE,OAAO,CAAC,UAACW,IAAI,EAAK;AACtBA,QAAAA,IAAI,CAACC,MAAM,CAACf,GAAG,CAAC;AAClB,MAAA,CAAC,CAAC;AAEJnB,MAAAA,OAAO,CAACsB,OAAO,CAAC,UAACC,MAAM,EAAK;QAC1BA,MAAM,CAACY,wBAAwB,GAAGZ,MAAM,CAACa,QAAQ,GAC7C,iBAAiB,GACjB,WAAW;AACfb,QAAAA,MAAM,CAACW,MAAM,CAACf,GAAG,CAAC;AACpB,MAAA,CAAC,CAAC;AACJ,IAAA;AAAC,GAAA,CAAA,CAAA;AAAA,EAAA,OAAAtB,aAAA;AAAA,CAAA,CAvCgCwC,KAAK;AAwCvCxB,eAAA,CAxCYhB,aAAa,EAAA,MAAA,EACV,UAAU,CAAA;AAyC1ByC,aAAa,CAACC,QAAQ,CAAC1C,aAAa,CAAC;;;;"}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { inherits as _inherits, classCallCheck as _classCallCheck, callSuper as _callSuper, defineProperty as _defineProperty, assertThisInitialized as _assertThisInitialized, createClass as _createClass, get as _get, getPrototypeOf as _getPrototypeOf, asyncToGenerator as _asyncToGenerator, regeneratorRuntime as _regeneratorRuntime, toConsumableArray as _toConsumableArray, slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { erase } from '../../core/dist/src/erase.js';
|
|
3
|
+
import * as fabric from 'fabric';
|
|
4
|
+
import { Group } from 'fabric';
|
|
5
|
+
import { ClippingGroup } from './ClippingGroup.js';
|
|
6
|
+
import { draw } from './ErasingEffect.js';
|
|
7
|
+
|
|
8
|
+
function walk(objects, path) {
|
|
9
|
+
return objects.flatMap(function (object) {
|
|
10
|
+
if (!object.erasable || !object.intersectsWithObject(path)) {
|
|
11
|
+
return [];
|
|
12
|
+
} else if (object instanceof Group && object.erasable === 'deep') {
|
|
13
|
+
return walk(object.getObjects(), path);
|
|
14
|
+
} else {
|
|
15
|
+
return [object];
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
var assertClippingGroup = function assertClippingGroup(object) {
|
|
20
|
+
var curr = object.clipPath;
|
|
21
|
+
if (curr instanceof ClippingGroup) {
|
|
22
|
+
return curr;
|
|
23
|
+
}
|
|
24
|
+
var strokeWidth = object.strokeWidth;
|
|
25
|
+
var strokeWidthFactor = new fabric.Point(strokeWidth, strokeWidth);
|
|
26
|
+
var strokeVector = object.strokeUniform ? strokeWidthFactor.divide(object.getObjectScaling()) : strokeWidthFactor;
|
|
27
|
+
var next = new ClippingGroup([], {
|
|
28
|
+
width: object.width + strokeVector.x,
|
|
29
|
+
height: object.height + strokeVector.y
|
|
30
|
+
});
|
|
31
|
+
if (curr) {
|
|
32
|
+
var _curr$translateToOrig = curr.translateToOriginPoint(new fabric.Point(), curr.originX, curr.originY),
|
|
33
|
+
x = _curr$translateToOrig.x,
|
|
34
|
+
y = _curr$translateToOrig.y;
|
|
35
|
+
curr.originX = curr.originY = 'center';
|
|
36
|
+
fabric.util.sendObjectToPlane(curr, undefined, fabric.util.createTranslateMatrix(x, y));
|
|
37
|
+
next.add(curr);
|
|
38
|
+
}
|
|
39
|
+
return object.clipPath = next;
|
|
40
|
+
};
|
|
41
|
+
function commitErasing(object, sourceInObjectPlane) {
|
|
42
|
+
var clipPath = assertClippingGroup(object);
|
|
43
|
+
clipPath.add(sourceInObjectPlane);
|
|
44
|
+
clipPath.set('dirty', true);
|
|
45
|
+
object.set('dirty', true);
|
|
46
|
+
}
|
|
47
|
+
function eraseObject(_x, _x2) {
|
|
48
|
+
return _eraseObject.apply(this, arguments);
|
|
49
|
+
}
|
|
50
|
+
function _eraseObject() {
|
|
51
|
+
_eraseObject = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(object, source) {
|
|
52
|
+
var clone;
|
|
53
|
+
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
|
|
54
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
55
|
+
case 0:
|
|
56
|
+
_context4.next = 2;
|
|
57
|
+
return source.clone();
|
|
58
|
+
case 2:
|
|
59
|
+
clone = _context4.sent;
|
|
60
|
+
fabric.util.sendObjectToPlane(clone, undefined, object.calcTransformMatrix());
|
|
61
|
+
commitErasing(object, clone);
|
|
62
|
+
return _context4.abrupt("return", clone);
|
|
63
|
+
case 6:
|
|
64
|
+
case "end":
|
|
65
|
+
return _context4.stop();
|
|
66
|
+
}
|
|
67
|
+
}, _callee4);
|
|
68
|
+
}));
|
|
69
|
+
return _eraseObject.apply(this, arguments);
|
|
70
|
+
}
|
|
71
|
+
function eraseCanvasDrawable(_x3, _x4, _x5) {
|
|
72
|
+
return _eraseCanvasDrawable.apply(this, arguments);
|
|
73
|
+
}
|
|
74
|
+
function _eraseCanvasDrawable() {
|
|
75
|
+
_eraseCanvasDrawable = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(object, vpt, source) {
|
|
76
|
+
var clone, d;
|
|
77
|
+
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
|
|
78
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
79
|
+
case 0:
|
|
80
|
+
_context5.next = 2;
|
|
81
|
+
return source.clone();
|
|
82
|
+
case 2:
|
|
83
|
+
clone = _context5.sent;
|
|
84
|
+
d = vpt && object.translateToOriginPoint(new fabric.Point(), object.originX, object.originY);
|
|
85
|
+
fabric.util.sendObjectToPlane(clone, undefined, d ? fabric.util.multiplyTransformMatrixArray([[1, 0, 0, 1, d.x, d.y],
|
|
86
|
+
// apply vpt from center of drawable
|
|
87
|
+
vpt, [1, 0, 0, 1, -d.x, -d.y], object.calcTransformMatrix()]) : object.calcTransformMatrix());
|
|
88
|
+
commitErasing(object, clone);
|
|
89
|
+
return _context5.abrupt("return", clone);
|
|
90
|
+
case 7:
|
|
91
|
+
case "end":
|
|
92
|
+
return _context5.stop();
|
|
93
|
+
}
|
|
94
|
+
}, _callee5);
|
|
95
|
+
}));
|
|
96
|
+
return _eraseCanvasDrawable.apply(this, arguments);
|
|
97
|
+
}
|
|
98
|
+
var setCanvasDimensions = function setCanvasDimensions(el, ctx, _ref) {
|
|
99
|
+
var width = _ref.width,
|
|
100
|
+
height = _ref.height;
|
|
101
|
+
var retinaScaling = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
|
102
|
+
el.width = width;
|
|
103
|
+
el.height = height;
|
|
104
|
+
if (retinaScaling > 1) {
|
|
105
|
+
el.setAttribute('width', (width * retinaScaling).toString());
|
|
106
|
+
el.setAttribute('height', (height * retinaScaling).toString());
|
|
107
|
+
ctx.scale(retinaScaling, retinaScaling);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Supports **selective** erasing: only erasable objects are affected by the eraser brush.
|
|
113
|
+
*
|
|
114
|
+
* Supports **{@link inverted}** erasing: the brush can "undo" erasing.
|
|
115
|
+
*
|
|
116
|
+
* Supports **alpha** erasing: setting the alpha channel of the `color` property controls the eraser intensity.
|
|
117
|
+
*
|
|
118
|
+
* In order to support selective erasing, the brush clips the entire canvas and
|
|
119
|
+
* masks all non-erasable objects over the erased path, see {@link draw}.
|
|
120
|
+
*
|
|
121
|
+
* If **{@link inverted}** draws all objects, erasable objects without their eraser, over the erased path.
|
|
122
|
+
* This achieves the desired effect of seeming to erase or undo erasing on erasable objects only.
|
|
123
|
+
*
|
|
124
|
+
* After erasing is done the `end` event {@link ErasingEndEvent} is fired, after which erasing will be committed to the tree.
|
|
125
|
+
* @example
|
|
126
|
+
* canvas = new Canvas();
|
|
127
|
+
* const eraser = new EraserBrush(canvas);
|
|
128
|
+
* canvas.freeDrawingBrush = eraser;
|
|
129
|
+
* canvas.isDrawingMode = true;
|
|
130
|
+
* eraser.on('start', (e) => {
|
|
131
|
+
* console.log('started erasing');
|
|
132
|
+
* // prevent erasing
|
|
133
|
+
* e.preventDefault();
|
|
134
|
+
* });
|
|
135
|
+
* eraser.on('end', (e) => {
|
|
136
|
+
* const { targets: erasedTargets, path } = e.detail;
|
|
137
|
+
* e.preventDefault(); // prevent erasing being committed to the tree
|
|
138
|
+
* eraser.commit({ targets: erasedTargets, path }); // commit manually since default was prevented
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* In case of performance issues trace {@link drawEffect} calls and consider preventing it from executing
|
|
142
|
+
* @example
|
|
143
|
+
* const eraser = new EraserBrush(canvas);
|
|
144
|
+
* eraser.on('redraw', (e) => {
|
|
145
|
+
* // prevent effect redraw on pointer down (e.g. useful if canvas didn't change)
|
|
146
|
+
* e.detail.type === 'start' && e.preventDefault());
|
|
147
|
+
* // prevent effect redraw after canvas has rendered (effect will become stale)
|
|
148
|
+
* e.detail.type === 'render' && e.preventDefault());
|
|
149
|
+
* });
|
|
150
|
+
*/
|
|
151
|
+
var EraserBrush = /*#__PURE__*/function (_fabric$PencilBrush) {
|
|
152
|
+
_inherits(EraserBrush, _fabric$PencilBrush);
|
|
153
|
+
function EraserBrush(canvas) {
|
|
154
|
+
var _this;
|
|
155
|
+
_classCallCheck(this, EraserBrush);
|
|
156
|
+
_this = _callSuper(this, EraserBrush, [canvas]);
|
|
157
|
+
/**
|
|
158
|
+
* When set to `true` the brush will create a visual effect of undoing erasing
|
|
159
|
+
*/
|
|
160
|
+
_defineProperty(_assertThisInitialized(_this), "inverted", false);
|
|
161
|
+
_defineProperty(_assertThisInitialized(_this), "active", false);
|
|
162
|
+
var el = document.createElement('canvas');
|
|
163
|
+
var ctx = el.getContext('2d');
|
|
164
|
+
if (!ctx) {
|
|
165
|
+
throw new Error('Failed to get context');
|
|
166
|
+
}
|
|
167
|
+
setCanvasDimensions(el, ctx, canvas, _this.canvas.getRetinaScaling());
|
|
168
|
+
_this.effectContext = ctx;
|
|
169
|
+
_this.eventEmitter = new EventTarget();
|
|
170
|
+
return _this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @returns disposer make sure to call it to avoid memory leaks
|
|
175
|
+
*/
|
|
176
|
+
_createClass(EraserBrush, [{
|
|
177
|
+
key: "on",
|
|
178
|
+
value: function on(type, cb, options) {
|
|
179
|
+
var _this2 = this;
|
|
180
|
+
this.eventEmitter.addEventListener(type, cb, options);
|
|
181
|
+
return function () {
|
|
182
|
+
return _this2.eventEmitter.removeEventListener(type, cb, options);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}, {
|
|
186
|
+
key: "drawEffect",
|
|
187
|
+
value: function drawEffect() {
|
|
188
|
+
draw(this.effectContext, {
|
|
189
|
+
opacity: new fabric.Color(this.color).getAlpha(),
|
|
190
|
+
inverted: this.inverted
|
|
191
|
+
}, {
|
|
192
|
+
canvas: this.canvas
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @override
|
|
198
|
+
*/
|
|
199
|
+
}, {
|
|
200
|
+
key: "_setBrushStyles",
|
|
201
|
+
value: function _setBrushStyles() {
|
|
202
|
+
var ctx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.canvas.contextTop;
|
|
203
|
+
_get(_getPrototypeOf(EraserBrush.prototype), "_setBrushStyles", this).call(this, ctx);
|
|
204
|
+
ctx.strokeStyle = 'black';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @override strictly speaking the eraser needs a full render only if it has opacity set.
|
|
209
|
+
* However since {@link PencilBrush} is designed for subclassing that is what we have to work with.
|
|
210
|
+
*/
|
|
211
|
+
}, {
|
|
212
|
+
key: "needsFullRender",
|
|
213
|
+
value: function needsFullRender() {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @override erase
|
|
219
|
+
*/
|
|
220
|
+
}, {
|
|
221
|
+
key: "_render",
|
|
222
|
+
value: function _render() {
|
|
223
|
+
var ctx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.canvas.getTopContext();
|
|
224
|
+
_get(_getPrototypeOf(EraserBrush.prototype), "_render", this).call(this, ctx);
|
|
225
|
+
erase(this.canvas.getContext(), ctx, this.effectContext);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @override {@link drawEffect}
|
|
230
|
+
*/
|
|
231
|
+
}, {
|
|
232
|
+
key: "onMouseDown",
|
|
233
|
+
value: function onMouseDown(pointer, context) {
|
|
234
|
+
var _this3 = this;
|
|
235
|
+
if (!this.eventEmitter.dispatchEvent(new CustomEvent('start', {
|
|
236
|
+
detail: context,
|
|
237
|
+
cancelable: true
|
|
238
|
+
}))) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
this.active = true;
|
|
242
|
+
this.eventEmitter.dispatchEvent(new CustomEvent('redraw', {
|
|
243
|
+
detail: {
|
|
244
|
+
type: 'start'
|
|
245
|
+
},
|
|
246
|
+
cancelable: true
|
|
247
|
+
})) && this.drawEffect();
|
|
248
|
+
|
|
249
|
+
// consider a different approach
|
|
250
|
+
this._disposer = this.canvas.on('after:render', function (_ref2) {
|
|
251
|
+
var ctx = _ref2.ctx;
|
|
252
|
+
if (ctx !== _this3.canvas.getContext()) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
_this3.eventEmitter.dispatchEvent(new CustomEvent('redraw', {
|
|
256
|
+
detail: {
|
|
257
|
+
type: 'render'
|
|
258
|
+
},
|
|
259
|
+
cancelable: true
|
|
260
|
+
})) && _this3.drawEffect();
|
|
261
|
+
_this3._render();
|
|
262
|
+
});
|
|
263
|
+
_get(_getPrototypeOf(EraserBrush.prototype), "onMouseDown", this).call(this, pointer, context);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @override run if active
|
|
268
|
+
*/
|
|
269
|
+
}, {
|
|
270
|
+
key: "onMouseMove",
|
|
271
|
+
value: function onMouseMove(pointer, context) {
|
|
272
|
+
this.active && this.eventEmitter.dispatchEvent(new CustomEvent('move', {
|
|
273
|
+
detail: context,
|
|
274
|
+
cancelable: true
|
|
275
|
+
})) && _get(_getPrototypeOf(EraserBrush.prototype), "onMouseMove", this).call(this, pointer, context);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @override run if active, dispose of {@link drawEffect} listener
|
|
280
|
+
*/
|
|
281
|
+
}, {
|
|
282
|
+
key: "onMouseUp",
|
|
283
|
+
value: function onMouseUp(context) {
|
|
284
|
+
var _this$_disposer;
|
|
285
|
+
this.active && _get(_getPrototypeOf(EraserBrush.prototype), "onMouseUp", this).call(this, context);
|
|
286
|
+
this.active = false;
|
|
287
|
+
(_this$_disposer = this._disposer) === null || _this$_disposer === void 0 || _this$_disposer.call(this);
|
|
288
|
+
delete this._disposer;
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @override {@link fabric.PencilBrush} logic
|
|
294
|
+
*/
|
|
295
|
+
}, {
|
|
296
|
+
key: "convertPointsToSVGPath",
|
|
297
|
+
value: function convertPointsToSVGPath(points) {
|
|
298
|
+
return _get(_getPrototypeOf(EraserBrush.prototype), "convertPointsToSVGPath", this).call(this, this.decimate ? this.decimatePoints(points, this.decimate) : points);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @override
|
|
303
|
+
*/
|
|
304
|
+
}, {
|
|
305
|
+
key: "createPath",
|
|
306
|
+
value: function createPath(pathData) {
|
|
307
|
+
var path = _get(_getPrototypeOf(EraserBrush.prototype), "createPath", this).call(this, pathData);
|
|
308
|
+
path.set(this.inverted ? {
|
|
309
|
+
globalCompositeOperation: 'source-over',
|
|
310
|
+
stroke: 'white'
|
|
311
|
+
} : {
|
|
312
|
+
globalCompositeOperation: 'destination-out',
|
|
313
|
+
stroke: 'black',
|
|
314
|
+
opacity: new fabric.Color(this.color).getAlpha()
|
|
315
|
+
});
|
|
316
|
+
return path;
|
|
317
|
+
}
|
|
318
|
+
}, {
|
|
319
|
+
key: "commit",
|
|
320
|
+
value: function () {
|
|
321
|
+
var _commit = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(_ref3) {
|
|
322
|
+
var path, targets;
|
|
323
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
324
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
325
|
+
case 0:
|
|
326
|
+
path = _ref3.path, targets = _ref3.targets;
|
|
327
|
+
_context3.t0 = Map;
|
|
328
|
+
_context3.next = 4;
|
|
329
|
+
return Promise.all([].concat(_toConsumableArray(targets.map( /*#__PURE__*/function () {
|
|
330
|
+
var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(object) {
|
|
331
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
332
|
+
while (1) switch (_context.prev = _context.next) {
|
|
333
|
+
case 0:
|
|
334
|
+
_context.t0 = object;
|
|
335
|
+
_context.next = 3;
|
|
336
|
+
return eraseObject(object, path);
|
|
337
|
+
case 3:
|
|
338
|
+
_context.t1 = _context.sent;
|
|
339
|
+
return _context.abrupt("return", [_context.t0, _context.t1]);
|
|
340
|
+
case 5:
|
|
341
|
+
case "end":
|
|
342
|
+
return _context.stop();
|
|
343
|
+
}
|
|
344
|
+
}, _callee);
|
|
345
|
+
}));
|
|
346
|
+
return function (_x7) {
|
|
347
|
+
return _ref4.apply(this, arguments);
|
|
348
|
+
};
|
|
349
|
+
}())), _toConsumableArray([[this.canvas.backgroundImage, !this.canvas.backgroundVpt ? this.canvas.viewportTransform : undefined], [this.canvas.overlayImage, !this.canvas.overlayVpt ? this.canvas.viewportTransform : undefined]].filter(function (_ref5) {
|
|
350
|
+
var _ref6 = _slicedToArray(_ref5, 1),
|
|
351
|
+
object = _ref6[0];
|
|
352
|
+
return !!(object !== null && object !== void 0 && object.erasable);
|
|
353
|
+
}).map( /*#__PURE__*/function () {
|
|
354
|
+
var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(_ref7) {
|
|
355
|
+
var _ref9, object, vptFlag;
|
|
356
|
+
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
357
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
358
|
+
case 0:
|
|
359
|
+
_ref9 = _slicedToArray(_ref7, 2), object = _ref9[0], vptFlag = _ref9[1];
|
|
360
|
+
_context2.t0 = object;
|
|
361
|
+
_context2.next = 4;
|
|
362
|
+
return eraseCanvasDrawable(object, vptFlag, path);
|
|
363
|
+
case 4:
|
|
364
|
+
_context2.t1 = _context2.sent;
|
|
365
|
+
return _context2.abrupt("return", [_context2.t0, _context2.t1]);
|
|
366
|
+
case 6:
|
|
367
|
+
case "end":
|
|
368
|
+
return _context2.stop();
|
|
369
|
+
}
|
|
370
|
+
}, _callee2);
|
|
371
|
+
}));
|
|
372
|
+
return function (_x8) {
|
|
373
|
+
return _ref8.apply(this, arguments);
|
|
374
|
+
};
|
|
375
|
+
}()))));
|
|
376
|
+
case 4:
|
|
377
|
+
_context3.t1 = _context3.sent;
|
|
378
|
+
return _context3.abrupt("return", new _context3.t0(_context3.t1));
|
|
379
|
+
case 6:
|
|
380
|
+
case "end":
|
|
381
|
+
return _context3.stop();
|
|
382
|
+
}
|
|
383
|
+
}, _callee3, this);
|
|
384
|
+
}));
|
|
385
|
+
function commit(_x6) {
|
|
386
|
+
return _commit.apply(this, arguments);
|
|
387
|
+
}
|
|
388
|
+
return commit;
|
|
389
|
+
}()
|
|
390
|
+
/**
|
|
391
|
+
* @override handle events
|
|
392
|
+
*/
|
|
393
|
+
}, {
|
|
394
|
+
key: "_finalizeAndAddPath",
|
|
395
|
+
value: function _finalizeAndAddPath() {
|
|
396
|
+
var points = this['_points'];
|
|
397
|
+
if (points.length < 2) {
|
|
398
|
+
this.eventEmitter.dispatchEvent(new CustomEvent('cancel', {
|
|
399
|
+
cancelable: false
|
|
400
|
+
}));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
var path = this.createPath(this.convertPointsToSVGPath(points));
|
|
404
|
+
var targets = walk(this.canvas.getObjects(), path);
|
|
405
|
+
this.eventEmitter.dispatchEvent(new CustomEvent('end', {
|
|
406
|
+
detail: {
|
|
407
|
+
path: path,
|
|
408
|
+
targets: targets
|
|
409
|
+
},
|
|
410
|
+
cancelable: true
|
|
411
|
+
})) && this.commit({
|
|
412
|
+
path: path,
|
|
413
|
+
targets: targets
|
|
414
|
+
});
|
|
415
|
+
this.canvas.clearContext(this.canvas.contextTop);
|
|
416
|
+
this.canvas.requestRenderAll();
|
|
417
|
+
this._resetShadow();
|
|
418
|
+
}
|
|
419
|
+
}, {
|
|
420
|
+
key: "dispose",
|
|
421
|
+
value: function dispose() {
|
|
422
|
+
var canvas = this.effectContext.canvas;
|
|
423
|
+
// prompt GC
|
|
424
|
+
canvas.width = canvas.height = 0;
|
|
425
|
+
// release ref?
|
|
426
|
+
// delete this.effectContext
|
|
427
|
+
}
|
|
428
|
+
}]);
|
|
429
|
+
return EraserBrush;
|
|
430
|
+
}(fabric.PencilBrush);
|
|
431
|
+
|
|
432
|
+
export { EraserBrush, commitErasing, eraseCanvasDrawable, eraseObject };
|
|
433
|
+
//# sourceMappingURL=EraserBrush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EraserBrush.js","sources":["../../../src/EraserBrush.ts"],"sourcesContent":["import { erase } from '@erase2d/core';\nimport * as fabric from 'fabric';\nimport { FabricObject, Group, Path } from 'fabric';\nimport { ClippingGroup } from './ClippingGroup';\nimport { draw } from './ErasingEffect';\n\nexport type EventDetailMap = {\n start: fabric.TEvent<fabric.TPointerEvent>;\n move: fabric.TEvent<fabric.TPointerEvent>;\n end: {\n path: fabric.Path;\n targets: fabric.FabricObject[];\n };\n redraw: { type: 'start' | 'render' };\n cancel: never;\n};\n\nexport type ErasingEventType = keyof EventDetailMap;\n\nexport type ErasingEvent<T extends ErasingEventType> = CustomEvent<\n EventDetailMap[T]\n>;\n\nfunction walk(objects: FabricObject[], path: Path): FabricObject[] {\n return objects.flatMap((object) => {\n if (!object.erasable || !object.intersectsWithObject(path)) {\n return [];\n } else if (object instanceof Group && object.erasable === 'deep') {\n return walk(object.getObjects(), path);\n } else {\n return [object];\n }\n });\n}\n\nconst assertClippingGroup = (object: fabric.FabricObject) => {\n const curr = object.clipPath;\n\n if (curr instanceof ClippingGroup) {\n return curr;\n }\n\n const strokeWidth = object.strokeWidth;\n const strokeWidthFactor = new fabric.Point(strokeWidth, strokeWidth);\n const strokeVector = object.strokeUniform\n ? strokeWidthFactor.divide(object.getObjectScaling())\n : strokeWidthFactor;\n\n const next = new ClippingGroup([], {\n width: object.width + strokeVector.x,\n height: object.height + strokeVector.y,\n });\n\n if (curr) {\n const { x, y } = curr.translateToOriginPoint(\n new fabric.Point(),\n curr.originX,\n curr.originY\n );\n curr.originX = curr.originY = 'center';\n fabric.util.sendObjectToPlane(\n curr,\n undefined,\n fabric.util.createTranslateMatrix(x, y)\n );\n next.add(curr as FabricObject);\n }\n\n return (object.clipPath = next);\n};\n\nexport function commitErasing(\n object: fabric.FabricObject,\n sourceInObjectPlane: fabric.Path\n) {\n const clipPath = assertClippingGroup(object);\n clipPath.add(sourceInObjectPlane);\n clipPath.set('dirty', true);\n object.set('dirty', true);\n}\n\nexport async function eraseObject(\n object: fabric.FabricObject,\n source: fabric.Path\n) {\n const clone = await source.clone();\n fabric.util.sendObjectToPlane(clone, undefined, object.calcTransformMatrix());\n commitErasing(object, clone);\n return clone;\n}\n\nexport async function eraseCanvasDrawable(\n object: fabric.FabricObject,\n vpt: fabric.TMat2D | undefined,\n source: fabric.Path\n) {\n const clone = await source.clone();\n const d =\n vpt &&\n object.translateToOriginPoint(\n new fabric.Point(),\n object.originX,\n object.originY\n );\n fabric.util.sendObjectToPlane(\n clone,\n undefined,\n d\n ? fabric.util.multiplyTransformMatrixArray([\n [1, 0, 0, 1, d.x, d.y],\n // apply vpt from center of drawable\n vpt,\n [1, 0, 0, 1, -d.x, -d.y],\n object.calcTransformMatrix(),\n ])\n : object.calcTransformMatrix()\n );\n commitErasing(object, clone);\n return clone;\n}\n\nconst setCanvasDimensions = (\n el: HTMLCanvasElement,\n ctx: CanvasRenderingContext2D,\n { width, height }: fabric.TSize,\n retinaScaling = 1\n) => {\n el.width = width;\n el.height = height;\n if (retinaScaling > 1) {\n el.setAttribute('width', (width * retinaScaling).toString());\n el.setAttribute('height', (height * retinaScaling).toString());\n ctx.scale(retinaScaling, retinaScaling);\n }\n};\n\n/**\n * Supports **selective** erasing: only erasable objects are affected by the eraser brush.\n *\n * Supports **{@link inverted}** erasing: the brush can \"undo\" erasing.\n *\n * Supports **alpha** erasing: setting the alpha channel of the `color` property controls the eraser intensity.\n *\n * In order to support selective erasing, the brush clips the entire canvas and\n * masks all non-erasable objects over the erased path, see {@link draw}.\n *\n * If **{@link inverted}** draws all objects, erasable objects without their eraser, over the erased path.\n * This achieves the desired effect of seeming to erase or undo erasing on erasable objects only.\n *\n * After erasing is done the `end` event {@link ErasingEndEvent} is fired, after which erasing will be committed to the tree.\n * @example\n * canvas = new Canvas();\n * const eraser = new EraserBrush(canvas);\n * canvas.freeDrawingBrush = eraser;\n * canvas.isDrawingMode = true;\n * eraser.on('start', (e) => {\n * console.log('started erasing');\n * // prevent erasing\n * e.preventDefault();\n * });\n * eraser.on('end', (e) => {\n * const { targets: erasedTargets, path } = e.detail;\n * e.preventDefault(); // prevent erasing being committed to the tree\n * eraser.commit({ targets: erasedTargets, path }); // commit manually since default was prevented\n * });\n *\n * In case of performance issues trace {@link drawEffect} calls and consider preventing it from executing\n * @example\n * const eraser = new EraserBrush(canvas);\n * eraser.on('redraw', (e) => {\n * // prevent effect redraw on pointer down (e.g. useful if canvas didn't change)\n * e.detail.type === 'start' && e.preventDefault());\n * // prevent effect redraw after canvas has rendered (effect will become stale)\n * e.detail.type === 'render' && e.preventDefault());\n * });\n */\nexport class EraserBrush extends fabric.PencilBrush {\n /**\n * When set to `true` the brush will create a visual effect of undoing erasing\n */\n inverted = false;\n\n effectContext: CanvasRenderingContext2D;\n\n private eventEmitter: EventTarget;\n private active = false;\n private _disposer?: VoidFunction;\n\n constructor(canvas: fabric.Canvas) {\n super(canvas);\n const el = document.createElement('canvas');\n const ctx = el.getContext('2d');\n if (!ctx) {\n throw new Error('Failed to get context');\n }\n setCanvasDimensions(el, ctx, canvas, this.canvas.getRetinaScaling());\n this.effectContext = ctx;\n this.eventEmitter = new EventTarget();\n }\n\n /**\n * @returns disposer make sure to call it to avoid memory leaks\n */\n on<T extends ErasingEventType>(\n type: T,\n cb: (evt: ErasingEvent<T>) => any,\n options?: boolean | AddEventListenerOptions\n ) {\n this.eventEmitter.addEventListener(type, cb as EventListener, options);\n return () =>\n this.eventEmitter.removeEventListener(type, cb as EventListener, options);\n }\n\n drawEffect() {\n draw(\n this.effectContext,\n {\n opacity: new fabric.Color(this.color).getAlpha(),\n inverted: this.inverted,\n },\n { canvas: this.canvas }\n );\n }\n\n /**\n * @override\n */\n _setBrushStyles(ctx: CanvasRenderingContext2D = this.canvas.contextTop) {\n super._setBrushStyles(ctx);\n ctx.strokeStyle = 'black';\n }\n\n /**\n * @override strictly speaking the eraser needs a full render only if it has opacity set.\n * However since {@link PencilBrush} is designed for subclassing that is what we have to work with.\n */\n needsFullRender(): boolean {\n return true;\n }\n\n /**\n * @override erase\n */\n _render(ctx: CanvasRenderingContext2D = this.canvas.getTopContext()): void {\n super._render(ctx);\n erase(this.canvas.getContext(), ctx, this.effectContext);\n }\n\n /**\n * @override {@link drawEffect}\n */\n onMouseDown(\n pointer: fabric.Point,\n context: fabric.TEvent<fabric.TPointerEvent>\n ): void {\n if (\n !this.eventEmitter.dispatchEvent(\n new CustomEvent('start', { detail: context, cancelable: true })\n )\n ) {\n return;\n }\n\n this.active = true;\n\n this.eventEmitter.dispatchEvent(\n new CustomEvent('redraw', {\n detail: { type: 'start' },\n cancelable: true,\n })\n ) && this.drawEffect();\n\n // consider a different approach\n this._disposer = this.canvas.on('after:render', ({ ctx }) => {\n if (ctx !== this.canvas.getContext()) {\n return;\n }\n this.eventEmitter.dispatchEvent(\n new CustomEvent('redraw', {\n detail: { type: 'render' },\n cancelable: true,\n })\n ) && this.drawEffect();\n this._render();\n });\n\n super.onMouseDown(pointer, context);\n }\n\n /**\n * @override run if active\n */\n onMouseMove(\n pointer: fabric.Point,\n context: fabric.TEvent<fabric.TPointerEvent>\n ): void {\n this.active &&\n this.eventEmitter.dispatchEvent(\n new CustomEvent('move', { detail: context, cancelable: true })\n ) &&\n super.onMouseMove(pointer, context);\n }\n\n /**\n * @override run if active, dispose of {@link drawEffect} listener\n */\n onMouseUp(context: fabric.TEvent<fabric.TPointerEvent>): boolean {\n this.active && super.onMouseUp(context);\n this.active = false;\n this._disposer?.();\n delete this._disposer;\n return false;\n }\n\n /**\n * @override {@link fabric.PencilBrush} logic\n */\n convertPointsToSVGPath(points: fabric.Point[]): fabric.util.TSimplePathData {\n return super.convertPointsToSVGPath(\n this.decimate ? this.decimatePoints(points, this.decimate) : points\n );\n }\n\n /**\n * @override\n */\n createPath(pathData: fabric.util.TSimplePathData) {\n const path = super.createPath(pathData);\n path.set(\n this.inverted\n ? {\n globalCompositeOperation: 'source-over',\n stroke: 'white',\n }\n : {\n globalCompositeOperation: 'destination-out',\n stroke: 'black',\n opacity: new fabric.Color(this.color).getAlpha(),\n }\n );\n return path;\n }\n\n async commit({\n path,\n targets,\n }: EventDetailMap['end']): Promise<Map<fabric.FabricObject, fabric.Path>> {\n return new Map(\n await Promise.all([\n ...targets.map(async (object) => {\n return [object, await eraseObject(object, path)] as const;\n }),\n ...(\n [\n [\n this.canvas.backgroundImage,\n !this.canvas.backgroundVpt\n ? this.canvas.viewportTransform\n : undefined,\n ],\n [\n this.canvas.overlayImage,\n !this.canvas.overlayVpt\n ? this.canvas.viewportTransform\n : undefined,\n ],\n ] as const\n )\n .filter(([object]) => !!object?.erasable)\n .map(async ([object, vptFlag]) => {\n return [\n object,\n await eraseCanvasDrawable(object as FabricObject, vptFlag, path),\n ] as [fabric.FabricObject, fabric.Path];\n }),\n ])\n );\n }\n\n /**\n * @override handle events\n */\n _finalizeAndAddPath(): void {\n const points = this['_points'];\n\n if (points.length < 2) {\n this.eventEmitter.dispatchEvent(\n new CustomEvent('cancel', {\n cancelable: false,\n })\n );\n return;\n }\n\n const path = this.createPath(this.convertPointsToSVGPath(points));\n const targets = walk(this.canvas.getObjects(), path);\n\n this.eventEmitter.dispatchEvent(\n new CustomEvent('end', {\n detail: {\n path,\n targets,\n },\n cancelable: true,\n })\n ) && this.commit({ path, targets });\n\n this.canvas.clearContext(this.canvas.contextTop);\n this.canvas.requestRenderAll();\n\n this._resetShadow();\n }\n\n dispose() {\n const { canvas } = this.effectContext;\n // prompt GC\n canvas.width = canvas.height = 0;\n // release ref?\n // delete this.effectContext\n }\n}\n"],"names":["walk","objects","path","flatMap","object","erasable","intersectsWithObject","Group","getObjects","assertClippingGroup","curr","clipPath","ClippingGroup","strokeWidth","strokeWidthFactor","fabric","Point","strokeVector","strokeUniform","divide","getObjectScaling","next","width","x","height","y","_curr$translateToOrig","translateToOriginPoint","originX","originY","util","sendObjectToPlane","undefined","createTranslateMatrix","add","commitErasing","sourceInObjectPlane","set","eraseObject","_x","_x2","_eraseObject","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee4","source","clone","wrap","_callee4$","_context4","prev","sent","calcTransformMatrix","abrupt","stop","eraseCanvasDrawable","_x3","_x4","_x5","_eraseCanvasDrawable","_callee5","vpt","d","_callee5$","_context5","multiplyTransformMatrixArray","setCanvasDimensions","el","ctx","_ref","retinaScaling","length","setAttribute","toString","scale","EraserBrush","_fabric$PencilBrush","_inherits","canvas","_this","_classCallCheck","_callSuper","_defineProperty","_assertThisInitialized","document","createElement","getContext","Error","getRetinaScaling","effectContext","eventEmitter","EventTarget","_createClass","key","value","on","type","cb","options","_this2","addEventListener","removeEventListener","drawEffect","draw","opacity","Color","color","getAlpha","inverted","_setBrushStyles","contextTop","_get","_getPrototypeOf","prototype","call","strokeStyle","needsFullRender","_render","getTopContext","erase","onMouseDown","pointer","context","_this3","dispatchEvent","CustomEvent","detail","cancelable","active","_disposer","_ref2","onMouseMove","onMouseUp","_this$_disposer","convertPointsToSVGPath","points","decimate","decimatePoints","createPath","pathData","globalCompositeOperation","stroke","_commit","_callee3","_ref3","targets","_callee3$","_context3","t0","Map","Promise","all","concat","_toConsumableArray","map","_ref4","_callee","_callee$","_context","t1","_x7","backgroundImage","backgroundVpt","viewportTransform","overlayImage","overlayVpt","filter","_ref5","_ref6","_slicedToArray","_ref8","_callee2","_ref7","_ref9","vptFlag","_callee2$","_context2","_x8","commit","_x6","_finalizeAndAddPath","clearContext","requestRenderAll","_resetShadow","dispose","PencilBrush"],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { toConsumableArray as _toConsumableArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
import { Group } from 'fabric';
|
|
3
|
+
import { ClippingGroup } from './ClippingGroup.js';
|
|
4
|
+
|
|
5
|
+
function walk(objects) {
|
|
6
|
+
return objects.flatMap(function (object) {
|
|
7
|
+
if (!object.erasable || object.isNotVisible()) {
|
|
8
|
+
return [];
|
|
9
|
+
} else if (object instanceof Group && object.erasable === 'deep') {
|
|
10
|
+
return walk(object.getObjects());
|
|
11
|
+
} else {
|
|
12
|
+
return [object];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function drawCanvas(ctx, canvas, objects) {
|
|
17
|
+
canvas.clearContext(ctx);
|
|
18
|
+
ctx.imageSmoothingEnabled = canvas.imageSmoothingEnabled;
|
|
19
|
+
ctx.imageSmoothingQuality = 'high';
|
|
20
|
+
// @ts-expect-error node-canvas stuff
|
|
21
|
+
ctx.patternQuality = 'best';
|
|
22
|
+
canvas._renderBackground(ctx);
|
|
23
|
+
ctx.save();
|
|
24
|
+
ctx.transform.apply(ctx, _toConsumableArray(canvas.viewportTransform));
|
|
25
|
+
objects.forEach(function (object) {
|
|
26
|
+
return object.render(ctx);
|
|
27
|
+
});
|
|
28
|
+
ctx.restore();
|
|
29
|
+
var clipPath = canvas.clipPath;
|
|
30
|
+
if (clipPath) {
|
|
31
|
+
// fabric crap
|
|
32
|
+
clipPath._set('canvas', canvas);
|
|
33
|
+
clipPath.shouldCache();
|
|
34
|
+
clipPath._transformDone = true;
|
|
35
|
+
// @ts-expect-error fabric type crap
|
|
36
|
+
clipPath.renderCache({
|
|
37
|
+
forClipping: true
|
|
38
|
+
});
|
|
39
|
+
canvas.drawClipPathOnCanvas(ctx, clipPath);
|
|
40
|
+
}
|
|
41
|
+
canvas._renderOverlay(ctx);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Prepare the pattern for the erasing brush
|
|
46
|
+
* This pattern will be drawn on the top context after clipping the main context,
|
|
47
|
+
* achieving a visual effect of erasing only erasable objects.
|
|
48
|
+
*
|
|
49
|
+
* This is designed to support erasing a collection with both erasable and non-erasable objects while maintaining object stacking.\
|
|
50
|
+
* Iterates over collections to allow nested selective erasing.\
|
|
51
|
+
* Prepares objects before rendering the pattern brush.\
|
|
52
|
+
* If brush is **NOT** inverted render all non-erasable objects.\
|
|
53
|
+
* If brush is inverted render all objects, erasable objects without their eraser.
|
|
54
|
+
* This will render the erased parts as if they were not erased in the first place, achieving an undo effect.
|
|
55
|
+
*
|
|
56
|
+
* Caveat:
|
|
57
|
+
* Does not support erasing effects of shadows
|
|
58
|
+
*
|
|
59
|
+
*/
|
|
60
|
+
function draw(ctx, _ref, _ref2) {
|
|
61
|
+
var inverted = _ref.inverted,
|
|
62
|
+
opacity = _ref.opacity;
|
|
63
|
+
var canvas = _ref2.canvas,
|
|
64
|
+
_ref2$objects = _ref2.objects,
|
|
65
|
+
objects = _ref2$objects === void 0 ? canvas._objectsToRender || canvas._objects : _ref2$objects,
|
|
66
|
+
_ref2$background = _ref2.background,
|
|
67
|
+
background = _ref2$background === void 0 ? canvas.backgroundImage : _ref2$background,
|
|
68
|
+
_ref2$overlay = _ref2.overlay,
|
|
69
|
+
overlay = _ref2$overlay === void 0 ? canvas.overlayImage : _ref2$overlay;
|
|
70
|
+
// prepare tree
|
|
71
|
+
var alpha = 1 - opacity;
|
|
72
|
+
var restore = walk([].concat(_toConsumableArray(objects), _toConsumableArray([background, overlay].filter(function (d) {
|
|
73
|
+
return !!d;
|
|
74
|
+
})))).map(function (object) {
|
|
75
|
+
if (!inverted) {
|
|
76
|
+
var _object$parent;
|
|
77
|
+
// render only non-erasable objects
|
|
78
|
+
var _opacity = object.opacity;
|
|
79
|
+
object.opacity *= alpha;
|
|
80
|
+
(_object$parent = object.parent) === null || _object$parent === void 0 || _object$parent.set('dirty', true);
|
|
81
|
+
return {
|
|
82
|
+
object: object,
|
|
83
|
+
opacity: _opacity
|
|
84
|
+
};
|
|
85
|
+
} else if (object.clipPath instanceof ClippingGroup) {
|
|
86
|
+
// render all objects without eraser
|
|
87
|
+
object.clipPath['blockErasing'] = true;
|
|
88
|
+
object.clipPath.set('dirty', true);
|
|
89
|
+
object.set('dirty', true);
|
|
90
|
+
return {
|
|
91
|
+
object: object,
|
|
92
|
+
clipPath: object.clipPath
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// draw
|
|
98
|
+
drawCanvas(ctx, canvas, objects);
|
|
99
|
+
|
|
100
|
+
// restore
|
|
101
|
+
restore.forEach(function (entry) {
|
|
102
|
+
if (!entry) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (entry.opacity) {
|
|
106
|
+
var _entry$object$parent;
|
|
107
|
+
entry.object.opacity = entry.opacity;
|
|
108
|
+
(_entry$object$parent = entry.object.parent) === null || _entry$object$parent === void 0 || _entry$object$parent.set('dirty', true);
|
|
109
|
+
} else if (entry.clipPath) {
|
|
110
|
+
entry.clipPath['blockErasing'] = false;
|
|
111
|
+
entry.clipPath.set('dirty', true);
|
|
112
|
+
entry.object.set('dirty', true);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { draw };
|
|
118
|
+
//# sourceMappingURL=ErasingEffect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErasingEffect.js","sources":["../../../src/ErasingEffect.ts"],"sourcesContent":["import { Canvas, FabricObject, Group } from 'fabric';\nimport { ClippingGroup } from './ClippingGroup';\n\nfunction walk(objects: FabricObject[]): FabricObject[] {\n return objects.flatMap((object) => {\n if (!object.erasable || object.isNotVisible()) {\n return [];\n } else if (object instanceof Group && object.erasable === 'deep') {\n return walk(object.getObjects());\n } else {\n return [object];\n }\n });\n}\n\nfunction drawCanvas(\n ctx: CanvasRenderingContext2D,\n canvas: Canvas,\n objects: FabricObject[],\n) {\n canvas.clearContext(ctx);\n\n ctx.imageSmoothingEnabled = canvas.imageSmoothingEnabled;\n ctx.imageSmoothingQuality = 'high';\n // @ts-expect-error node-canvas stuff\n ctx.patternQuality = 'best';\n\n canvas._renderBackground(ctx);\n\n ctx.save();\n ctx.transform(...canvas.viewportTransform);\n objects.forEach((object) => object.render(ctx));\n ctx.restore();\n\n const clipPath = canvas.clipPath;\n if (clipPath) {\n // fabric crap\n clipPath._set('canvas', canvas);\n clipPath.shouldCache();\n clipPath._transformDone = true;\n // @ts-expect-error fabric type crap\n clipPath.renderCache({ forClipping: true });\n canvas.drawClipPathOnCanvas(ctx, clipPath as any);\n }\n\n canvas._renderOverlay(ctx);\n}\n\n/**\n * Prepare the pattern for the erasing brush\n * This pattern will be drawn on the top context after clipping the main context,\n * achieving a visual effect of erasing only erasable objects.\n *\n * This is designed to support erasing a collection with both erasable and non-erasable objects while maintaining object stacking.\\\n * Iterates over collections to allow nested selective erasing.\\\n * Prepares objects before rendering the pattern brush.\\\n * If brush is **NOT** inverted render all non-erasable objects.\\\n * If brush is inverted render all objects, erasable objects without their eraser.\n * This will render the erased parts as if they were not erased in the first place, achieving an undo effect.\n *\n * Caveat:\n * Does not support erasing effects of shadows\n *\n */\nexport function draw(\n ctx: CanvasRenderingContext2D,\n { inverted, opacity }: { inverted: boolean; opacity: number },\n {\n canvas,\n objects = canvas._objectsToRender || canvas._objects,\n background = canvas.backgroundImage,\n overlay = canvas.overlayImage,\n }: {\n canvas: Canvas;\n objects?: FabricObject[];\n background?: FabricObject;\n overlay?: FabricObject;\n },\n) {\n // prepare tree\n const alpha = 1 - opacity;\n const restore = walk([\n ...objects,\n ...([background, overlay] as FabricObject[]).filter((d) => !!d),\n ]).map((object) => {\n if (!inverted) {\n // render only non-erasable objects\n const opacity = object.opacity;\n object.opacity *= alpha;\n object.parent?.set('dirty', true);\n return { object, opacity };\n } else if (object.clipPath instanceof ClippingGroup) {\n // render all objects without eraser\n object.clipPath['blockErasing'] = true;\n object.clipPath.set('dirty', true);\n object.set('dirty', true);\n return { object, clipPath: object.clipPath };\n }\n });\n\n // draw\n drawCanvas(ctx, canvas, objects);\n\n // restore\n restore.forEach((entry) => {\n if (!entry) {\n return;\n }\n if (entry.opacity) {\n entry.object.opacity = entry.opacity;\n entry.object.parent?.set('dirty', true);\n } else if (entry.clipPath) {\n entry.clipPath['blockErasing'] = false;\n entry.clipPath.set('dirty', true);\n entry.object.set('dirty', true);\n }\n });\n}\n"],"names":["walk","objects","flatMap","object","erasable","isNotVisible","Group","getObjects","drawCanvas","ctx","canvas","clearContext","imageSmoothingEnabled","imageSmoothingQuality","patternQuality","_renderBackground","save","transform","apply","_toConsumableArray","viewportTransform","forEach","render","restore","clipPath","_set","shouldCache","_transformDone","renderCache","forClipping","drawClipPathOnCanvas","_renderOverlay","draw","_ref","_ref2","inverted","opacity","_ref2$objects","_objectsToRender","_objects","_ref2$background","background","backgroundImage","_ref2$overlay","overlay","overlayImage","alpha","concat","filter","d","map","_object$parent","parent","set","ClippingGroup","entry","_entry$object$parent"],"mappings":";;;;AAGA,SAASA,IAAIA,CAACC,OAAuB,EAAkB;AACrD,EAAA,OAAOA,OAAO,CAACC,OAAO,CAAC,UAACC,MAAM,EAAK;IACjC,IAAI,CAACA,MAAM,CAACC,QAAQ,IAAID,MAAM,CAACE,YAAY,EAAE,EAAE;AAC7C,MAAA,OAAO,EAAE;IACX,CAAC,MAAM,IAAIF,MAAM,YAAYG,KAAK,IAAIH,MAAM,CAACC,QAAQ,KAAK,MAAM,EAAE;AAChE,MAAA,OAAOJ,IAAI,CAACG,MAAM,CAACI,UAAU,EAAE,CAAC;AAClC,IAAA,CAAC,MAAM;MACL,OAAO,CAACJ,MAAM,CAAC;AACjB,IAAA;AACF,EAAA,CAAC,CAAC;AACJ;AAEA,SAASK,UAAUA,CACjBC,GAA6B,EAC7BC,MAAc,EACdT,OAAuB,EACvB;AACAS,EAAAA,MAAM,CAACC,YAAY,CAACF,GAAG,CAAC;AAExBA,EAAAA,GAAG,CAACG,qBAAqB,GAAGF,MAAM,CAACE,qBAAqB;EACxDH,GAAG,CAACI,qBAAqB,GAAG,MAAM;AAClC;EACAJ,GAAG,CAACK,cAAc,GAAG,MAAM;AAE3BJ,EAAAA,MAAM,CAACK,iBAAiB,CAACN,GAAG,CAAC;EAE7BA,GAAG,CAACO,IAAI,EAAE;AACVP,EAAAA,GAAG,CAACQ,SAAS,CAAAC,KAAA,CAAbT,GAAG,EAAAU,kBAAA,CAAcT,MAAM,CAACU,iBAAiB,CAAA,CAAC;AAC1CnB,EAAAA,OAAO,CAACoB,OAAO,CAAC,UAAClB,MAAM,EAAA;AAAA,IAAA,OAAKA,MAAM,CAACmB,MAAM,CAACb,GAAG,CAAC;EAAA,CAAA,CAAC;EAC/CA,GAAG,CAACc,OAAO,EAAE;AAEb,EAAA,IAAMC,QAAQ,GAAGd,MAAM,CAACc,QAAQ;AAChC,EAAA,IAAIA,QAAQ,EAAE;AACZ;AACAA,IAAAA,QAAQ,CAACC,IAAI,CAAC,QAAQ,EAAEf,MAAM,CAAC;IAC/Bc,QAAQ,CAACE,WAAW,EAAE;IACtBF,QAAQ,CAACG,cAAc,GAAG,IAAI;AAC9B;IACAH,QAAQ,CAACI,WAAW,CAAC;AAAEC,MAAAA,WAAW,EAAE;AAAK,KAAC,CAAC;AAC3CnB,IAAAA,MAAM,CAACoB,oBAAoB,CAACrB,GAAG,EAAEe,QAAe,CAAC;AACnD,EAAA;AAEAd,EAAAA,MAAM,CAACqB,cAAc,CAACtB,GAAG,CAAC;AAC5B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASuB,IAAIA,CAClBvB,GAA6B,EAAAwB,IAAA,EAAAC,KAAA,EAa7B;AAAA,EAAA,IAZEC,QAAQ,GAAAF,IAAA,CAARE,QAAQ;IAAEC,OAAO,GAAAH,IAAA,CAAPG,OAAO;AAAA,EAAA,IAEjB1B,MAAM,GAAAwB,KAAA,CAANxB,MAAM;IAAA2B,aAAA,GAAAH,KAAA,CACNjC,OAAO;IAAPA,OAAO,GAAAoC,aAAA,KAAA,MAAA,GAAG3B,MAAM,CAAC4B,gBAAgB,IAAI5B,MAAM,CAAC6B,QAAQ,GAAAF,aAAA;IAAAG,gBAAA,GAAAN,KAAA,CACpDO,UAAU;AAAVA,IAAAA,UAAU,GAAAD,gBAAA,KAAA,MAAA,GAAG9B,MAAM,CAACgC,eAAe,GAAAF,gBAAA;IAAAG,aAAA,GAAAT,KAAA,CACnCU,OAAO;AAAPA,IAAAA,OAAO,GAAAD,aAAA,KAAA,MAAA,GAAGjC,MAAM,CAACmC,YAAY,GAAAF,aAAA;AAQ/B;AACA,EAAA,IAAMG,KAAK,GAAG,CAAC,GAAGV,OAAO;EACzB,IAAMb,OAAO,GAAGvB,IAAI,CAAA,EAAA,CAAA+C,MAAA,CAAA5B,kBAAA,CACflB,OAAO,CAAA,EAAAkB,kBAAA,CACN,CAACsB,UAAU,EAAEG,OAAO,CAAC,CAAoBI,MAAM,CAAC,UAACC,CAAC,EAAA;IAAA,OAAK,CAAC,CAACA,CAAC;AAAA,EAAA,CAAA,CAAC,EAChE,CAAC,CAACC,GAAG,CAAC,UAAC/C,MAAM,EAAK;IACjB,IAAI,CAACgC,QAAQ,EAAE;AAAA,MAAA,IAAAgB,cAAA;AACb;AACA,MAAA,IAAMf,QAAO,GAAGjC,MAAM,CAACiC,OAAO;MAC9BjC,MAAM,CAACiC,OAAO,IAAIU,KAAK;AACvB,MAAA,CAAAK,cAAA,GAAAhD,MAAM,CAACiD,MAAM,MAAA,IAAA,IAAAD,cAAA,KAAA,MAAA,IAAbA,cAAA,CAAeE,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;MACjC,OAAO;AAAElD,QAAAA,MAAM,EAANA,MAAM;AAAEiC,QAAAA,OAAO,EAAPA;OAAS;AAC5B,IAAA,CAAC,MAAM,IAAIjC,MAAM,CAACqB,QAAQ,YAAY8B,aAAa,EAAE;AACnD;AACAnD,MAAAA,MAAM,CAACqB,QAAQ,CAAC,cAAc,CAAC,GAAG,IAAI;MACtCrB,MAAM,CAACqB,QAAQ,CAAC6B,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;AAClClD,MAAAA,MAAM,CAACkD,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;MACzB,OAAO;AAAElD,QAAAA,MAAM,EAANA,MAAM;QAAEqB,QAAQ,EAAErB,MAAM,CAACqB;OAAU;AAC9C,IAAA;AACF,EAAA,CAAC,CAAC;;AAEF;AACAhB,EAAAA,UAAU,CAACC,GAAG,EAAEC,MAAM,EAAET,OAAO,CAAC;;AAEhC;AACAsB,EAAAA,OAAO,CAACF,OAAO,CAAC,UAACkC,KAAK,EAAK;IACzB,IAAI,CAACA,KAAK,EAAE;AACV,MAAA;AACF,IAAA;IACA,IAAIA,KAAK,CAACnB,OAAO,EAAE;AAAA,MAAA,IAAAoB,oBAAA;AACjBD,MAAAA,KAAK,CAACpD,MAAM,CAACiC,OAAO,GAAGmB,KAAK,CAACnB,OAAO;AACpC,MAAA,CAAAoB,oBAAA,GAAAD,KAAK,CAACpD,MAAM,CAACiD,MAAM,MAAA,IAAA,IAAAI,oBAAA,KAAA,MAAA,IAAnBA,oBAAA,CAAqBH,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;AACzC,IAAA,CAAC,MAAM,IAAIE,KAAK,CAAC/B,QAAQ,EAAE;AACzB+B,MAAAA,KAAK,CAAC/B,QAAQ,CAAC,cAAc,CAAC,GAAG,KAAK;MACtC+B,KAAK,CAAC/B,QAAQ,CAAC6B,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;MACjCE,KAAK,CAACpD,MAAM,CAACkD,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;AACjC,IAAA;AACF,EAAA,CAAC,CAAC;AACJ;;;;"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var messageID = -1;
|
|
2
|
+
var isImageDataTransparent = function isImageDataTransparent(imageData) {
|
|
3
|
+
return imageData.data.every(function (x, i) {
|
|
4
|
+
return i % 4 !== 3 || x === 0;
|
|
5
|
+
});
|
|
6
|
+
};
|
|
7
|
+
function isTransparent(object, worker) {
|
|
8
|
+
// should also move to offscreen
|
|
9
|
+
var canvas = object.toCanvasElement({
|
|
10
|
+
// multiplier: 0.1,
|
|
11
|
+
enableRetinaScaling: false,
|
|
12
|
+
viewportTransform: false,
|
|
13
|
+
withoutTransform: true,
|
|
14
|
+
withoutShadow: true
|
|
15
|
+
});
|
|
16
|
+
var id = ++messageID;
|
|
17
|
+
return new Promise(function (resolve, reject) {
|
|
18
|
+
var _canvas$getContext;
|
|
19
|
+
var imageData = (_canvas$getContext = canvas.getContext('2d')) === null || _canvas$getContext === void 0 ? void 0 : _canvas$getContext.getImageData(0, 0, canvas.width, canvas.height);
|
|
20
|
+
if (!imageData) {
|
|
21
|
+
reject();
|
|
22
|
+
} else if (!worker) {
|
|
23
|
+
resolve(isImageDataTransparent(imageData));
|
|
24
|
+
} else {
|
|
25
|
+
var messageHandler = function messageHandler(e) {
|
|
26
|
+
if (e.data.messageID === id) {
|
|
27
|
+
worker.removeEventListener('message', messageHandler);
|
|
28
|
+
resolve(e.data.isTransparent);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
worker.addEventListener('message', messageHandler);
|
|
32
|
+
worker.postMessage({
|
|
33
|
+
imageData: imageData,
|
|
34
|
+
messageID: id
|
|
35
|
+
}, []);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
isTransparent.installWorker = function installWorker() {
|
|
40
|
+
addEventListener('message', function (e) {
|
|
41
|
+
var _e$data = e.data,
|
|
42
|
+
imageData = _e$data.imageData,
|
|
43
|
+
messageID = _e$data.messageID;
|
|
44
|
+
postMessage({
|
|
45
|
+
isTransparent: isImageDataTransparent(imageData),
|
|
46
|
+
messageID: messageID
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { isTransparent };
|
|
52
|
+
//# sourceMappingURL=isTransparent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isTransparent.js","sources":["../../../src/isTransparent.ts"],"sourcesContent":["import { type FabricObject } from 'fabric';\n\nlet messageID = -1;\n\nconst isImageDataTransparent = (imageData: ImageData) =>\n imageData.data.every((x, i) => i % 4 !== 3 || x === 0);\n\nfunction isTransparent(object: FabricObject, worker?: Worker) {\n // should also move to offscreen\n const canvas = object.toCanvasElement({\n // multiplier: 0.1,\n enableRetinaScaling: false,\n viewportTransform: false,\n withoutTransform: true,\n withoutShadow: true,\n });\n\n const id = ++messageID;\n return new Promise<boolean>((resolve, reject) => {\n const imageData = canvas\n .getContext('2d')\n ?.getImageData(0, 0, canvas.width, canvas.height);\n\n if (!imageData) {\n reject();\n } else if (!worker) {\n resolve(isImageDataTransparent(imageData));\n } else {\n const messageHandler = (\n e: MessageEvent<{ messageID: number; isTransparent: boolean }>\n ) => {\n if (e.data.messageID === id) {\n worker.removeEventListener('message', messageHandler);\n resolve(e.data.isTransparent);\n }\n };\n worker.addEventListener('message', messageHandler);\n worker.postMessage(\n {\n imageData,\n messageID: id,\n },\n []\n );\n }\n });\n}\n\nisTransparent.installWorker = function installWorker() {\n addEventListener(\n 'message',\n (e: MessageEvent<{ imageData: ImageData; messageID: number }>) => {\n const { imageData, messageID } = e.data;\n postMessage({\n isTransparent: isImageDataTransparent(imageData),\n messageID,\n });\n }\n );\n};\n\nexport { isTransparent };\n"],"names":["messageID","isImageDataTransparent","imageData","data","every","x","i","isTransparent","object","worker","canvas","toCanvasElement","enableRetinaScaling","viewportTransform","withoutTransform","withoutShadow","id","Promise","resolve","reject","_canvas$getContext","getContext","getImageData","width","height","messageHandler","e","removeEventListener","addEventListener","postMessage","installWorker","_e$data"],"mappings":"AAEA,IAAIA,SAAS,GAAG,EAAE;AAElB,IAAMC,sBAAsB,GAAG,SAAzBA,sBAAsBA,CAAIC,SAAoB,EAAA;EAAA,OAClDA,SAAS,CAACC,IAAI,CAACC,KAAK,CAAC,UAACC,CAAC,EAAEC,CAAC,EAAA;IAAA,OAAKA,CAAC,GAAG,CAAC,KAAK,CAAC,IAAID,CAAC,KAAK,CAAC;EAAA,CAAA,CAAC;AAAA,CAAA;AAExD,SAASE,aAAaA,CAACC,MAAoB,EAAEC,MAAe,EAAE;AAC5D;AACA,EAAA,IAAMC,MAAM,GAAGF,MAAM,CAACG,eAAe,CAAC;AACpC;AACAC,IAAAA,mBAAmB,EAAE,KAAK;AAC1BC,IAAAA,iBAAiB,EAAE,KAAK;AACxBC,IAAAA,gBAAgB,EAAE,IAAI;AACtBC,IAAAA,aAAa,EAAE;AACjB,GAAC,CAAC;EAEF,IAAMC,EAAE,GAAG,EAAEhB,SAAS;AACtB,EAAA,OAAO,IAAIiB,OAAO,CAAU,UAACC,OAAO,EAAEC,MAAM,EAAK;AAAA,IAAA,IAAAC,kBAAA;AAC/C,IAAA,IAAMlB,SAAS,GAAA,CAAAkB,kBAAA,GAAGV,MAAM,CACrBW,UAAU,CAAC,IAAI,CAAC,MAAA,IAAA,IAAAD,kBAAA,KAAA,MAAA,GAAA,MAAA,GADDA,kBAAA,CAEdE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAEZ,MAAM,CAACa,KAAK,EAAEb,MAAM,CAACc,MAAM,CAAC;IAEnD,IAAI,CAACtB,SAAS,EAAE;AACdiB,MAAAA,MAAM,EAAE;AACV,IAAA,CAAC,MAAM,IAAI,CAACV,MAAM,EAAE;AAClBS,MAAAA,OAAO,CAACjB,sBAAsB,CAACC,SAAS,CAAC,CAAC;AAC5C,IAAA,CAAC,MAAM;AACL,MAAA,IAAMuB,cAAc,GAAG,SAAjBA,cAAcA,CAClBC,CAA8D,EAC3D;AACH,QAAA,IAAIA,CAAC,CAACvB,IAAI,CAACH,SAAS,KAAKgB,EAAE,EAAE;AAC3BP,UAAAA,MAAM,CAACkB,mBAAmB,CAAC,SAAS,EAAEF,cAAc,CAAC;AACrDP,UAAAA,OAAO,CAACQ,CAAC,CAACvB,IAAI,CAACI,aAAa,CAAC;AAC/B,QAAA;MACF,CAAC;AACDE,MAAAA,MAAM,CAACmB,gBAAgB,CAAC,SAAS,EAAEH,cAAc,CAAC;MAClDhB,MAAM,CAACoB,WAAW,CAChB;AACE3B,QAAAA,SAAS,EAATA,SAAS;AACTF,QAAAA,SAAS,EAAEgB;OACZ,EACD,EACF,CAAC;AACH,IAAA;AACF,EAAA,CAAC,CAAC;AACJ;AAEAT,aAAa,CAACuB,aAAa,GAAG,SAASA,aAAaA,GAAG;AACrDF,EAAAA,gBAAgB,CACd,SAAS,EACT,UAACF,CAA4D,EAAK;AAChE,IAAA,IAAAK,OAAA,GAAiCL,CAAC,CAACvB,IAAI;MAA/BD,SAAS,GAAA6B,OAAA,CAAT7B,SAAS;MAAEF,SAAS,GAAA+B,OAAA,CAAT/B,SAAS;AAC5B6B,IAAAA,WAAW,CAAC;AACVtB,MAAAA,aAAa,EAAEN,sBAAsB,CAACC,SAAS,CAAC;AAChDF,MAAAA,SAAS,EAATA;AACF,KAAC,CAAC;AACJ,EAAA,CACF,CAAC;AACH,CAAC;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { ClippingGroup } from './src/ClippingGroup';
|
|
2
|
-
export {
|
|
2
|
+
export { eraseCanvasDrawable, eraseObject, EraserBrush, type ErasingEvent, type ErasingEventType, } from './src/EraserBrush';
|
|
3
3
|
export { isTransparent } from './src/isTransparent';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FabricObject, Group, GroupProps } from 'fabric';
|
|
2
|
+
export declare class ClippingGroup extends Group {
|
|
3
|
+
static type: string;
|
|
4
|
+
private blockErasing;
|
|
5
|
+
constructor(objects: FabricObject[], options: Partial<GroupProps>);
|
|
6
|
+
drawObject(ctx: CanvasRenderingContext2D): void;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=ClippingGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClippingGroup.d.ts","sourceRoot":"","sources":["../../src/ClippingGroup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAEZ,KAAK,EACL,UAAU,EAIX,MAAM,QAAQ,CAAC;AAEhB,qBAAa,aAAc,SAAQ,KAAK;IACtC,MAAM,CAAC,IAAI,SAAc;IAEzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC;IAWjE,UAAU,CAAC,GAAG,EAAE,wBAAwB;CAwBzC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as fabric from 'fabric';
|
|
2
|
+
export type EventDetailMap = {
|
|
3
|
+
start: fabric.TEvent<fabric.TPointerEvent>;
|
|
4
|
+
move: fabric.TEvent<fabric.TPointerEvent>;
|
|
5
|
+
end: {
|
|
6
|
+
path: fabric.Path;
|
|
7
|
+
targets: fabric.FabricObject[];
|
|
8
|
+
};
|
|
9
|
+
redraw: {
|
|
10
|
+
type: 'start' | 'render';
|
|
11
|
+
};
|
|
12
|
+
cancel: never;
|
|
13
|
+
};
|
|
14
|
+
export type ErasingEventType = keyof EventDetailMap;
|
|
15
|
+
export type ErasingEvent<T extends ErasingEventType> = CustomEvent<EventDetailMap[T]>;
|
|
16
|
+
export declare function commitErasing(object: fabric.FabricObject, sourceInObjectPlane: fabric.Path): void;
|
|
17
|
+
export declare function eraseObject(object: fabric.FabricObject, source: fabric.Path): Promise<fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>>;
|
|
18
|
+
export declare function eraseCanvasDrawable(object: fabric.FabricObject, vpt: fabric.TMat2D | undefined, source: fabric.Path): Promise<fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>>;
|
|
19
|
+
/**
|
|
20
|
+
* Supports **selective** erasing: only erasable objects are affected by the eraser brush.
|
|
21
|
+
*
|
|
22
|
+
* Supports **{@link inverted}** erasing: the brush can "undo" erasing.
|
|
23
|
+
*
|
|
24
|
+
* Supports **alpha** erasing: setting the alpha channel of the `color` property controls the eraser intensity.
|
|
25
|
+
*
|
|
26
|
+
* In order to support selective erasing, the brush clips the entire canvas and
|
|
27
|
+
* masks all non-erasable objects over the erased path, see {@link draw}.
|
|
28
|
+
*
|
|
29
|
+
* If **{@link inverted}** draws all objects, erasable objects without their eraser, over the erased path.
|
|
30
|
+
* This achieves the desired effect of seeming to erase or undo erasing on erasable objects only.
|
|
31
|
+
*
|
|
32
|
+
* After erasing is done the `end` event {@link ErasingEndEvent} is fired, after which erasing will be committed to the tree.
|
|
33
|
+
* @example
|
|
34
|
+
* canvas = new Canvas();
|
|
35
|
+
* const eraser = new EraserBrush(canvas);
|
|
36
|
+
* canvas.freeDrawingBrush = eraser;
|
|
37
|
+
* canvas.isDrawingMode = true;
|
|
38
|
+
* eraser.on('start', (e) => {
|
|
39
|
+
* console.log('started erasing');
|
|
40
|
+
* // prevent erasing
|
|
41
|
+
* e.preventDefault();
|
|
42
|
+
* });
|
|
43
|
+
* eraser.on('end', (e) => {
|
|
44
|
+
* const { targets: erasedTargets, path } = e.detail;
|
|
45
|
+
* e.preventDefault(); // prevent erasing being committed to the tree
|
|
46
|
+
* eraser.commit({ targets: erasedTargets, path }); // commit manually since default was prevented
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* In case of performance issues trace {@link drawEffect} calls and consider preventing it from executing
|
|
50
|
+
* @example
|
|
51
|
+
* const eraser = new EraserBrush(canvas);
|
|
52
|
+
* eraser.on('redraw', (e) => {
|
|
53
|
+
* // prevent effect redraw on pointer down (e.g. useful if canvas didn't change)
|
|
54
|
+
* e.detail.type === 'start' && e.preventDefault());
|
|
55
|
+
* // prevent effect redraw after canvas has rendered (effect will become stale)
|
|
56
|
+
* e.detail.type === 'render' && e.preventDefault());
|
|
57
|
+
* });
|
|
58
|
+
*/
|
|
59
|
+
export declare class EraserBrush extends fabric.PencilBrush {
|
|
60
|
+
/**
|
|
61
|
+
* When set to `true` the brush will create a visual effect of undoing erasing
|
|
62
|
+
*/
|
|
63
|
+
inverted: boolean;
|
|
64
|
+
effectContext: CanvasRenderingContext2D;
|
|
65
|
+
private eventEmitter;
|
|
66
|
+
private active;
|
|
67
|
+
private _disposer?;
|
|
68
|
+
constructor(canvas: fabric.Canvas);
|
|
69
|
+
/**
|
|
70
|
+
* @returns disposer make sure to call it to avoid memory leaks
|
|
71
|
+
*/
|
|
72
|
+
on<T extends ErasingEventType>(type: T, cb: (evt: ErasingEvent<T>) => any, options?: boolean | AddEventListenerOptions): () => void;
|
|
73
|
+
drawEffect(): void;
|
|
74
|
+
/**
|
|
75
|
+
* @override
|
|
76
|
+
*/
|
|
77
|
+
_setBrushStyles(ctx?: CanvasRenderingContext2D): void;
|
|
78
|
+
/**
|
|
79
|
+
* @override strictly speaking the eraser needs a full render only if it has opacity set.
|
|
80
|
+
* However since {@link PencilBrush} is designed for subclassing that is what we have to work with.
|
|
81
|
+
*/
|
|
82
|
+
needsFullRender(): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* @override erase
|
|
85
|
+
*/
|
|
86
|
+
_render(ctx?: CanvasRenderingContext2D): void;
|
|
87
|
+
/**
|
|
88
|
+
* @override {@link drawEffect}
|
|
89
|
+
*/
|
|
90
|
+
onMouseDown(pointer: fabric.Point, context: fabric.TEvent<fabric.TPointerEvent>): void;
|
|
91
|
+
/**
|
|
92
|
+
* @override run if active
|
|
93
|
+
*/
|
|
94
|
+
onMouseMove(pointer: fabric.Point, context: fabric.TEvent<fabric.TPointerEvent>): void;
|
|
95
|
+
/**
|
|
96
|
+
* @override run if active, dispose of {@link drawEffect} listener
|
|
97
|
+
*/
|
|
98
|
+
onMouseUp(context: fabric.TEvent<fabric.TPointerEvent>): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* @override {@link fabric.PencilBrush} logic
|
|
101
|
+
*/
|
|
102
|
+
convertPointsToSVGPath(points: fabric.Point[]): fabric.util.TSimplePathData;
|
|
103
|
+
/**
|
|
104
|
+
* @override
|
|
105
|
+
*/
|
|
106
|
+
createPath(pathData: fabric.util.TSimplePathData): fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>;
|
|
107
|
+
commit({ path, targets, }: EventDetailMap['end']): Promise<Map<fabric.FabricObject, fabric.Path>>;
|
|
108
|
+
/**
|
|
109
|
+
* @override handle events
|
|
110
|
+
*/
|
|
111
|
+
_finalizeAndAddPath(): void;
|
|
112
|
+
dispose(): void;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=EraserBrush.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EraserBrush.d.ts","sourceRoot":"","sources":["../../src/EraserBrush.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Canvas, FabricObject } from 'fabric';
|
|
2
|
+
/**
|
|
3
|
+
* Prepare the pattern for the erasing brush
|
|
4
|
+
* This pattern will be drawn on the top context after clipping the main context,
|
|
5
|
+
* achieving a visual effect of erasing only erasable objects.
|
|
6
|
+
*
|
|
7
|
+
* This is designed to support erasing a collection with both erasable and non-erasable objects while maintaining object stacking.\
|
|
8
|
+
* Iterates over collections to allow nested selective erasing.\
|
|
9
|
+
* Prepares objects before rendering the pattern brush.\
|
|
10
|
+
* If brush is **NOT** inverted render all non-erasable objects.\
|
|
11
|
+
* If brush is inverted render all objects, erasable objects without their eraser.
|
|
12
|
+
* This will render the erased parts as if they were not erased in the first place, achieving an undo effect.
|
|
13
|
+
*
|
|
14
|
+
* Caveat:
|
|
15
|
+
* Does not support erasing effects of shadows
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export declare function draw(ctx: CanvasRenderingContext2D, { inverted, opacity }: {
|
|
19
|
+
inverted: boolean;
|
|
20
|
+
opacity: number;
|
|
21
|
+
}, { canvas, objects, background, overlay, }: {
|
|
22
|
+
canvas: Canvas;
|
|
23
|
+
objects?: FabricObject[];
|
|
24
|
+
background?: FabricObject;
|
|
25
|
+
overlay?: FabricObject;
|
|
26
|
+
}): void;
|
|
27
|
+
//# sourceMappingURL=ErasingEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErasingEffect.d.ts","sourceRoot":"","sources":["../../src/ErasingEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAS,MAAM,QAAQ,CAAC;AAgDrD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,wBAAwB,EAC7B,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC7D,EACE,MAAM,EACN,OAAoD,EACpD,UAAmC,EACnC,OAA6B,GAC9B,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,QAwCF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type FabricObject } from 'fabric';
|
|
2
|
+
declare function isTransparent(object: FabricObject, worker?: Worker): Promise<boolean>;
|
|
3
|
+
declare namespace isTransparent {
|
|
4
|
+
var installWorker: () => void;
|
|
5
|
+
}
|
|
6
|
+
export { isTransparent };
|
|
7
|
+
//# sourceMappingURL=isTransparent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isTransparent.d.ts","sourceRoot":"","sources":["../../src/isTransparent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,QAAQ,CAAC;AAO3C,iBAAS,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,oBAuC3D;kBAvCQ,aAAa;;;AAsDtB,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erase2d/fabric",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Fabric.js erase2d bindings",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"fabric"
|
|
28
28
|
],
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"fabric": "
|
|
30
|
+
"fabric": ">=6.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
33
33
|
"fabric": {
|
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
"@erase2d/core": "file:../core",
|
|
39
39
|
"@rollup/plugin-node-resolve": "^15.2.3"
|
|
40
40
|
},
|
|
41
|
+
"files": [
|
|
42
|
+
"/dist"
|
|
43
|
+
],
|
|
41
44
|
"module": "./dist/fabric/index.js",
|
|
42
45
|
"types": "./dist/index.d.ts",
|
|
43
46
|
"exports": {
|
package/rollup.config.mjs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { babel } from '@rollup/plugin-babel';
|
|
2
|
-
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
3
|
-
import ts from '@rollup/plugin-typescript';
|
|
4
|
-
import del from 'rollup-plugin-delete';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* https://rollupjs.org/guide/en/#configuration-files
|
|
8
|
-
* @type {import('rollup').RollupOptions}
|
|
9
|
-
*/
|
|
10
|
-
export default [
|
|
11
|
-
{
|
|
12
|
-
input: 'index.ts',
|
|
13
|
-
output: {
|
|
14
|
-
dir: 'dist',
|
|
15
|
-
format: 'es',
|
|
16
|
-
preserveModules: true,
|
|
17
|
-
entryFileNames: '[name].js',
|
|
18
|
-
sourcemap: true,
|
|
19
|
-
// plugins: [terser()],
|
|
20
|
-
},
|
|
21
|
-
plugins: [
|
|
22
|
-
del({
|
|
23
|
-
targets: ['dist/*'],
|
|
24
|
-
}),
|
|
25
|
-
|
|
26
|
-
// resolve and build `@erase2d/core`
|
|
27
|
-
nodeResolve(),
|
|
28
|
-
|
|
29
|
-
ts({
|
|
30
|
-
noForceEmit: true,
|
|
31
|
-
tsconfig: 'tsconfig.json',
|
|
32
|
-
exclude: ['**/dist', '**/**.spec.ts', '**/**.test.ts'],
|
|
33
|
-
}),
|
|
34
|
-
babel({
|
|
35
|
-
extensions: ['.ts', '.js'],
|
|
36
|
-
babelHelpers: 'bundled',
|
|
37
|
-
presets: [['@babel/env'], ['@babel/typescript']],
|
|
38
|
-
}),
|
|
39
|
-
],
|
|
40
|
-
external: ['fabric'],
|
|
41
|
-
},
|
|
42
|
-
];
|
package/tsconfig.json
DELETED
package/types/fabric.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import 'fabric';
|
|
2
|
-
|
|
3
|
-
declare module 'fabric' {
|
|
4
|
-
interface FabricObjectProps {
|
|
5
|
-
/**
|
|
6
|
-
* Indicates whether this object can be erased by the {@link EraserBrush}\
|
|
7
|
-
* The `deep` option introduces fine grained control over a {@link Group#erasable} property.\
|
|
8
|
-
* When set to `true` the eraser will erase the entire object.\
|
|
9
|
-
* When set to `deep` the eraser will erase nested objects if they are erasable,
|
|
10
|
-
* leaving the group and the other objects untouched.\
|
|
11
|
-
* When set to `false` the object and its descendants are untouched.\
|
|
12
|
-
*
|
|
13
|
-
* When using `true` on a group Consider using {@link applyEraser} on layout changes
|
|
14
|
-
* in order to propagate the eraser to descendants so that new descendants won't be affected by it.
|
|
15
|
-
*/
|
|
16
|
-
erasable: boolean | 'deep';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface FabricObject extends FabricObjectProps {}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export {};
|
package/types/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './fabric';
|