@erase2d/fabric 1.0.0

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/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Fabric Erase2d
2
+
3
+ Fabric bindings for erase2d.
4
+ Uses `PencilBrush` for the drawing interaction.
5
+
6
+ ## Caveat
7
+
8
+ Fabric has clipping limitations that must be worked around in order to support erasing.
9
+ I decided not to hack into fabric using prototype mutation to avoid coupling.
10
+
11
+ This means that an object's clip path is subject to change by `ClippingGroup` when erasing is committed.
12
+ It will replace an exiting clip path with `new ClippingGroup([existingClipPath])` if it didn't already so you can expect clip paths to change after their object was erased for the first time.
13
+ This has an advantage as well. It allows to clip an object with a number of clip paths simply by adding them to the clipping group so it can be used regardless of erasing.
14
+ Best practice might be to use `ClippingGroup` for every clip path. This will keep your object changes to a minimum.
15
+
16
+ In case you wish to remove the existing clip path but not affect erasing:
17
+
18
+ ```typescript
19
+ // check if the object was erased
20
+ object.clipPath instanceof ClippingGroup
21
+ ? object.clipPath.remove(
22
+ object.clipPath.item(0) /** the existing clip path is first */
23
+ )
24
+ : delete object.clipPath;
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ npm i fabric@beta @erase2d/fabric --save
31
+ ```
32
+
33
+ ```typescript
34
+ import { EraserBrush, ClippingGroup } from '@erase2d/fabric';
35
+ import { Circle } from 'fabric';
36
+
37
+ const circle = new Circle({ radius: 50, erasable: true });
38
+ canvas.add(circle);
39
+
40
+ const eraser = new EraserBrush(canvas);
41
+ eraser.width = 30;
42
+
43
+ eraser.on('start', (e) => {
44
+ // inform something
45
+ });
46
+
47
+ eraser.on('end', (e) => {
48
+ // prevent from committing erasing to the tree
49
+ e.preventDefault();
50
+ const isErased = e.targets.includes(circle);
51
+
52
+ // commit erasing manually
53
+ eraser.commit(e.detail);
54
+
55
+ const committedEraser = circle.clipPath instanceof ClippingGroup;
56
+ });
57
+
58
+ canvas.freeDrawingBrush = eraser;
59
+ canvas.isDrawingMode = true;
60
+ ```
61
+
62
+ ## Migrating from fabric@5
63
+
64
+ The logic has been reworked from the bottom.
65
+ `Eraser` has been removed in favor of the leaner `ClippingGroup`.
66
+ Replacing the `type` in your data should be enough.
67
+
68
+ https://github.com/ShaMan123/erase2d/blob/4c9657f8e2d6c20e7274f49a8f8e6d907f9e02e6/src/fabric/ClippingGroup.ts#L11
69
+
70
+ ```diff
71
+ - type: 'eraser'
72
+ + type: 'clipping'
73
+ ```
74
+
75
+ ## Dev
76
+
77
+ see main [`README`](../../README.md).
@@ -0,0 +1,2 @@
1
+ function t(t,e,n){return e=v(e),g(t,r()?Reflect.construct(e,n||[],v(t).constructor):e.apply(t,n))}function r(){try{var t=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(t){}return(r=function(){return!!t})()}function e(t,r){var e=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=e){var n,o,i,a,c=[],u=!0,l=!1;try{if(i=(e=e.call(t)).next,0===r){if(Object(e)!==e)return;u=!1}else for(;!(u=(n=i.call(e)).done)&&(c.push(n.value),c.length!==r);u=!0);}catch(t){l=!0,o=t}finally{try{if(!u&&null!=e.return&&(a=e.return(),Object(a)!==a))return}finally{if(l)throw o}}return c}}function n(t,r){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(t,r).enumerable}))),e.push.apply(e,n)}return e}function o(t){for(var r=1;r<arguments.length;r++){var e=null!=arguments[r]?arguments[r]:{};r%2?n(Object(e),!0).forEach((function(r){y(t,r,e[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):n(Object(e)).forEach((function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(e,r))}))}return t}function i(){i=function(){return r};var t,r={},e=Object.prototype,n=e.hasOwnProperty,o=Object.defineProperty||function(t,r,e){t[r]=e.value},a="function"==typeof Symbol?Symbol:{},c=a.iterator||"@@iterator",u=a.asyncIterator||"@@asyncIterator",l=a.toStringTag||"@@toStringTag";function f(t,r,e){return Object.defineProperty(t,r,{value:e,enumerable:!0,configurable:!0,writable:!0}),t[r]}try{f({},"")}catch(t){f=function(t,r,e){return t[r]=e}}function s(t,r,e,n){var i=r&&r.prototype instanceof g?r:g,a=Object.create(i.prototype),c=new k(n||[]);return o(a,"_invoke",{value:S(t,e,c)}),a}function p(t,r,e){try{return{type:"normal",arg:t.call(r,e)}}catch(t){return{type:"throw",arg:t}}}r.wrap=s;var y="suspendedStart",h="suspendedYield",v="executing",d="completed",b={};function g(){}function m(){}function w(){}var O={};f(O,c,(function(){return this}));var j=Object.getPrototypeOf,P=j&&j(j(I([])));P&&P!==e&&n.call(P,c)&&(O=P);var E=w.prototype=g.prototype=Object.create(O);function L(t){["next","throw","return"].forEach((function(r){f(t,r,(function(t){return this._invoke(r,t)}))}))}function x(t,r){function e(o,i,a,c){var u=p(t[o],t,i);if("throw"!==u.type){var l=u.arg,f=l.value;return f&&"object"==typeof f&&n.call(f,"__await")?r.resolve(f.__await).then((function(t){e("next",t,a,c)}),(function(t){e("throw",t,a,c)})):r.resolve(f).then((function(t){l.value=t,a(l)}),(function(t){return e("throw",t,a,c)}))}c(u.arg)}var i;o(this,"_invoke",{value:function(t,n){function o(){return new r((function(r,o){e(t,n,r,o)}))}return i=i?i.then(o,o):o()}})}function S(r,e,n){var o=y;return function(i,a){if(o===v)throw new Error("Generator is already running");if(o===d){if("throw"===i)throw a;return{value:t,done:!0}}for(n.method=i,n.arg=a;;){var c=n.delegate;if(c){var u=_(c,n);if(u){if(u===b)continue;return u}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===y)throw o=d,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=v;var l=p(r,e,n);if("normal"===l.type){if(o=n.done?d:h,l.arg===b)continue;return{value:l.arg,done:n.done}}"throw"===l.type&&(o=d,n.method="throw",n.arg=l.arg)}}}function _(r,e){var n=e.method,o=r.iterator[n];if(o===t)return e.delegate=null,"throw"===n&&r.iterator.return&&(e.method="return",e.arg=t,_(r,e),"throw"===e.method)||"return"!==n&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+n+"' method")),b;var i=p(o,r.iterator,e.arg);if("throw"===i.type)return e.method="throw",e.arg=i.arg,e.delegate=null,b;var a=i.arg;return a?a.done?(e[r.resultName]=a.value,e.next=r.nextLoc,"return"!==e.method&&(e.method="next",e.arg=t),e.delegate=null,b):a:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,b)}function T(t){var r={tryLoc:t[0]};1 in t&&(r.catchLoc=t[1]),2 in t&&(r.finallyLoc=t[2],r.afterLoc=t[3]),this.tryEntries.push(r)}function A(t){var r=t.completion||{};r.type="normal",delete r.arg,t.completion=r}function k(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(T,this),this.reset(!0)}function I(r){if(r||""===r){var e=r[c];if(e)return e.call(r);if("function"==typeof r.next)return r;if(!isNaN(r.length)){var o=-1,i=function e(){for(;++o<r.length;)if(n.call(r,o))return e.value=r[o],e.done=!1,e;return e.value=t,e.done=!0,e};return i.next=i}}throw new TypeError(typeof r+" is not iterable")}return m.prototype=w,o(E,"constructor",{value:w,configurable:!0}),o(w,"constructor",{value:m,configurable:!0}),m.displayName=f(w,l,"GeneratorFunction"),r.isGeneratorFunction=function(t){var r="function"==typeof t&&t.constructor;return!!r&&(r===m||"GeneratorFunction"===(r.displayName||r.name))},r.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,w):(t.__proto__=w,f(t,l,"GeneratorFunction")),t.prototype=Object.create(E),t},r.awrap=function(t){return{__await:t}},L(x.prototype),f(x.prototype,u,(function(){return this})),r.AsyncIterator=x,r.async=function(t,e,n,o,i){void 0===i&&(i=Promise);var a=new x(s(t,e,n,o),i);return r.isGeneratorFunction(e)?a:a.next().then((function(t){return t.done?t.value:a.next()}))},L(E),f(E,l,"Generator"),f(E,c,(function(){return this})),f(E,"toString",(function(){return"[object Generator]"})),r.keys=function(t){var r=Object(t),e=[];for(var n in r)e.push(n);return e.reverse(),function t(){for(;e.length;){var n=e.pop();if(n in r)return t.value=n,t.done=!1,t}return t.done=!0,t}},r.values=I,k.prototype={constructor:k,reset:function(r){if(this.prev=0,this.next=0,this.sent=this._sent=t,this.done=!1,this.delegate=null,this.method="next",this.arg=t,this.tryEntries.forEach(A),!r)for(var e in this)"t"===e.charAt(0)&&n.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=t)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(r){if(this.done)throw r;var e=this;function o(n,o){return c.type="throw",c.arg=r,e.next=n,o&&(e.method="next",e.arg=t),!!o}for(var i=this.tryEntries.length-1;i>=0;--i){var a=this.tryEntries[i],c=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var u=n.call(a,"catchLoc"),l=n.call(a,"finallyLoc");if(u&&l){if(this.prev<a.catchLoc)return o(a.catchLoc,!0);if(this.prev<a.finallyLoc)return o(a.finallyLoc)}else if(u){if(this.prev<a.catchLoc)return o(a.catchLoc,!0)}else{if(!l)throw new Error("try statement without catch or finally");if(this.prev<a.finallyLoc)return o(a.finallyLoc)}}}},abrupt:function(t,r){for(var e=this.tryEntries.length-1;e>=0;--e){var o=this.tryEntries[e];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev<o.finallyLoc){var i=o;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=r&&r<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=t,a.arg=r,i?(this.method="next",this.next=i.finallyLoc,b):this.complete(a)},complete:function(t,r){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&r&&(this.next=r),b},finish:function(t){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.finallyLoc===t)return this.complete(e.completion,e.afterLoc),A(e),b}},catch:function(t){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc===t){var n=e.completion;if("throw"===n.type){var o=n.arg;A(e)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(r,e,n){return this.delegate={iterator:I(r),resultName:e,nextLoc:n},"next"===this.method&&(this.arg=t),b}},r}function a(t,r){if("object"!=typeof t||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var n=e.call(t,r||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===r?String:Number)(t)}function c(t){var r=a(t,"string");return"symbol"==typeof r?r:String(r)}function u(t,r,e,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void e(t)}c.done?r(u):Promise.resolve(u).then(n,o)}function l(t){return function(){var r=this,e=arguments;return new Promise((function(n,o){var i=t.apply(r,e);function a(t){u(i,n,o,a,c,"next",t)}function c(t){u(i,n,o,a,c,"throw",t)}a(void 0)}))}}function f(t,r){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}function s(t,r){for(var e=0;e<r.length;e++){var n=r[e];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,c(n.key),n)}}function p(t,r,e){return r&&s(t.prototype,r),e&&s(t,e),Object.defineProperty(t,"prototype",{writable:!1}),t}function y(t,r,e){return(r=c(r))in t?Object.defineProperty(t,r,{value:e,enumerable:!0,configurable:!0,writable:!0}):t[r]=e,t}function h(t,r){if("function"!=typeof r&&null!==r)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(r&&r.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),r&&d(t,r)}function v(t){return v=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},v(t)}function d(t,r){return d=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,r){return t.__proto__=r,t},d(t,r)}function b(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function g(t,r){if(r&&("object"==typeof r||"function"==typeof r))return r;if(void 0!==r)throw new TypeError("Derived constructors may only return object or undefined");return b(t)}function m(t,r){for(;!Object.prototype.hasOwnProperty.call(t,r)&&null!==(t=v(t)););return t}function w(){return w="undefined"!=typeof Reflect&&Reflect.get?Reflect.get.bind():function(t,r,e){var n=m(t,r);if(n){var o=Object.getOwnPropertyDescriptor(n,r);return o.get?o.get.call(arguments.length<3?t:e):o.value}},w.apply(this,arguments)}function O(t,r){return E(t)||e(t,r)||x(t,r)||T()}function j(t){return P(t)||L(t)||x(t)||_()}function P(t){if(Array.isArray(t))return S(t)}function E(t){if(Array.isArray(t))return t}function L(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}function x(t,r){if(t){if("string"==typeof t)return S(t,r);var e=Object.prototype.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?S(t,r):void 0}}function S(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=new Array(r);e<r;e++)n[e]=t[e];return n}function _(){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 T(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}export{S as arrayLikeToArray,E as arrayWithHoles,P as arrayWithoutHoles,b as assertThisInitialized,l as asyncToGenerator,t as callSuper,f as classCallCheck,p as createClass,y as defineProperty,w as get,v as getPrototypeOf,h as inherits,r as isNativeReflectConstruct,L as iterableToArray,e as iterableToArrayLimit,T as nonIterableRest,_ as nonIterableSpread,o as objectSpread2,g as possibleConstructorReturn,i as regeneratorRuntime,d as setPrototypeOf,O as slicedToArray,m as superPropBase,j as toConsumableArray,a as toPrimitive,c as toPropertyKey,x as unsupportedIterableToArray};
2
+ //# sourceMappingURL=_rollupPluginBabelHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_rollupPluginBabelHelpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ export declare const drawImage: (destination: CanvasRenderingContext2D, source: CanvasRenderingContext2D, globalCompositeOperation?: GlobalCompositeOperation) => void;
2
+ /**
3
+ *
4
+ * @param destination context to erase
5
+ * @param source context on which the path is drawn upon
6
+ * @param erasingEffect effect to apply to {@link source} after clipping {@link destination}:
7
+ * - drawing all non erasable visuals to achieve a selective erasing effect.
8
+ * - drawing all erasable visuals without their erasers to achieve an undo erasing effect.
9
+ */
10
+ export declare const erase: (destination: CanvasRenderingContext2D, source: CanvasRenderingContext2D, erasingEffect?: CanvasRenderingContext2D) => void;
11
+ //# sourceMappingURL=erase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erase.d.ts","sourceRoot":"","sources":["../../../core/erase.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,gBACP,wBAAwB,UAC7B,wBAAwB,6BACN,wBAAwB,SASnD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,gBACH,wBAAwB,UAC7B,wBAAwB,kBAChB,wBAAwB,SAczC,CAAC"}
@@ -0,0 +1,2 @@
1
+ var e=function(e,a){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"source-over";e.save(),e.imageSmoothingEnabled=!0,e.imageSmoothingQuality="high",e.globalCompositeOperation=o,e.resetTransform(),e.drawImage(a.canvas,0,0),e.restore()},a=function(a,o,r){e(a,o,"destination-out"),r?e(o,r,"source-in"):(o.save(),o.resetTransform(),o.clearRect(0,0,o.canvas.width,o.canvas.height),o.restore())};export{e as drawImage,a as erase};
2
+ //# sourceMappingURL=erase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erase.js","sources":["../../../core/erase.ts"],"sourcesContent":["export const drawImage = (\n destination: CanvasRenderingContext2D,\n source: CanvasRenderingContext2D,\n globalCompositeOperation: GlobalCompositeOperation = 'source-over'\n) => {\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 */\nexport const erase = (\n destination: CanvasRenderingContext2D,\n source: CanvasRenderingContext2D,\n erasingEffect?: CanvasRenderingContext2D\n) => {\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"],"names":["drawImage","destination","source","globalCompositeOperation","arguments","length","undefined","save","imageSmoothingEnabled","imageSmoothingQuality","resetTransform","canvas","restore","erase","erasingEffect","clearRect","width","height"],"mappings":"AAAO,IAAMA,EAAY,SACvBC,EACAC,GAEG,IADHC,EAAkDC,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,cAErDH,EAAYM,OACZN,EAAYO,uBAAwB,EACpCP,EAAYQ,sBAAwB,OACpCR,EAAYE,yBAA2BA,EACvCF,EAAYS,iBACZT,EAAYD,UAAUE,EAAOS,OAAQ,EAAG,GACxCV,EAAYW,SACd,EAUaC,EAAQ,SACnBZ,EACAC,EACAY,GAGAd,EAAUC,EAAaC,EAAQ,mBAG3BY,EACFd,EAAUE,EAAQY,EAAe,cAEjCZ,EAAOK,OACPL,EAAOQ,iBACPR,EAAOa,UAAU,EAAG,EAAGb,EAAOS,OAAOK,MAAOd,EAAOS,OAAOM,QAC1Df,EAAOU,UAEX"}
@@ -0,0 +1,4 @@
1
+ /// <reference path="../../types/fabric.d.ts" />
2
+ export { ClippingGroup } from './src/ClippingGroup';
3
+ export { EraserBrush, type ErasingEndEvent, type ErasingEndEventDetail, eraseObject, eraseCanvasDrawable, } from './src/EraserBrush';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,WAAW,EACX,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,WAAW,EACX,mBAAmB,GACpB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export{ClippingGroup}from"./src/ClippingGroup.js";export{EraserBrush,eraseCanvasDrawable,eraseObject}from"./src/EraserBrush.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { Group, FabricObject, GroupProps } from 'fabric';
2
+ export declare class ClippingGroup extends Group {
3
+ static type: string;
4
+ static getDefaults(): Record<string, any>;
5
+ private blockErasing;
6
+ constructor(objects: FabricObject[], options: Partial<GroupProps>);
7
+ drawObject(ctx: CanvasRenderingContext2D): void;
8
+ }
9
+ //# 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,KAAK,EAIL,YAAY,EACZ,UAAU,EAEX,MAAM,QAAQ,CAAC;AAEhB,qBAAa,aAAc,SAAQ,KAAK;IACtC,MAAM,CAAC,IAAI,SAAc;IAEzB,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAUzC,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC;IAOjE,UAAU,CAAC,GAAG,EAAE,wBAAwB;CAwBzC"}
@@ -0,0 +1,2 @@
1
+ import{defineProperty as t,inherits as e,createClass as i,classCallCheck as r,callSuper as a,objectSpread2 as s,assertThisInitialized as n,get as o,getPrototypeOf as l}from"../../_virtual/_rollupPluginBabelHelpers.js";import{classRegistry as c,LayoutManager as h,FixedLayout as u,Path as f,Group as p}from"fabric";var g=function(c){function g(e,i){var o;return r(this,g),o=a(this,g,[e,s({layoutManager:new h(new u)},i)]),t(n(o),"blockErasing",!1),o}return e(g,p),i(g,[{key:"drawObject",value:function(t){var e=[],i=[];this._objects.forEach((function(t){return(t instanceof f?e:i).push(t)})),t.save(),t.fillStyle="black",t.fillRect(-this.width/2,-this.height/2,this.width,this.height),t.restore(),!this.blockErasing&&e.forEach((function(e){e.render(t)})),i.forEach((function(e){e.globalCompositeOperation=e.inverted?"destination-out":"source-in",e.render(t)}))}}],[{key:"getDefaults",value:function(){return s(s({},o(l(g),"getDefaults",this).call(this)),{},{originX:"center",originY:"center",left:0,top:0})}}]),g}();t(g,"type","clipping"),c.setClass(g);export{g as ClippingGroup};
2
+ //# sourceMappingURL=ClippingGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClippingGroup.js","sources":["../../../src/ClippingGroup.ts"],"sourcesContent":["import {\n Group,\n LayoutManager,\n FixedLayout,\n Path,\n FabricObject,\n GroupProps,\n classRegistry,\n} from 'fabric';\n\nexport class ClippingGroup extends Group {\n static type = 'clipping';\n\n static getDefaults(): Record<string, any> {\n return {\n ...super.getDefaults(),\n originX: 'center',\n originY: 'center',\n left: 0,\n top: 0,\n };\n }\n\n private blockErasing = false;\n\n constructor(objects: FabricObject[], options: Partial<GroupProps>) {\n super(objects, {\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","objects","options","_this","_classCallCheck","_callSuper","this","_objectSpread","layoutManager","LayoutManager","FixedLayout","_defineProperty","_assertThisInitialized","_inherits","Group","_createClass","key","value","ctx","paths","_objects","forEach","object","Path","push","save","fillStyle","fillRect","width","height","restore","blockErasing","path","render","globalCompositeOperation","inverted","_get","_getPrototypeOf","call","originX","originY","left","top","classRegistry","setClass"],"mappings":"0TAUaA,IAAAA,WAAaC,GAexB,SAAAD,EAAYE,EAAyBC,GAA8B,IAAAC,EAFvC,OAEuCC,OAAAL,GACjEI,EAAAE,EAAAC,KAAAP,EAAME,CAAAA,EAAOM,EAAA,CACXC,cAAe,IAAIC,EAAc,IAAIC,IAClCR,KACFS,EAAAC,EAAAT,mBANkB,GAAKA,CAO5B,CATC,OAXuBU,EAAAd,EAASe,GAoBhCC,EAAAhB,EAAA,CAAA,CAAAiB,IAAA,aAAAC,MAED,SAAWC,GACT,IAAMC,EAAgB,GAChBlB,EAA0B,GAChCK,KAAKc,SAASC,SAAQ,SAACC,GAAM,OAC1BA,aAAkBC,EAAOJ,EAAQlB,GAASuB,KAAKF,EAAO,IAGzDJ,EAAIO,OACJP,EAAIQ,UAAY,QAChBR,EAAIS,UAAUrB,KAAKsB,MAAQ,GAAItB,KAAKuB,OAAS,EAAGvB,KAAKsB,MAAOtB,KAAKuB,QACjEX,EAAIY,WAEHxB,KAAKyB,cACJZ,EAAME,SAAQ,SAACW,GACbA,EAAKC,OAAOf,EACd,IAEFjB,EAAQoB,SAAQ,SAACC,GACfA,EAAOY,yBAA2BZ,EAAOa,SACrC,kBACA,YACJb,EAAOW,OAAOf,EAChB,GACF,IAAC,CAAA,CAAAF,IAAA,cAAAC,MA1CD,WACE,OAAAV,EAAAA,EAAA6B,CAAAA,EAAAA,EAAAC,EAAAtC,GAAA,cAAAO,MAAAgC,KAAAhC,OAAA,CAAA,EAAA,CAEEiC,QAAS,SACTC,QAAS,SACTC,KAAM,EACNC,IAAK,GAET,KAAC3C,CAAA,IAmCFY,EA9CYZ,EAAa,OACV,YA+ChB4C,EAAcC,SAAS7C"}
@@ -0,0 +1,90 @@
1
+ import * as fabric from 'fabric';
2
+ export type ErasingEndEventDetail = {
3
+ path: fabric.Path;
4
+ targets: fabric.FabricObject[];
5
+ };
6
+ export type ErasingEndEvent = CustomEvent<ErasingEndEventDetail>;
7
+ type EventDetailMap = {
8
+ start: VoidFunction;
9
+ end: ErasingEndEventDetail;
10
+ };
11
+ export declare function commitErasing(object: fabric.FabricObject, sourceInObjectPlane: fabric.Path): void;
12
+ export declare function eraseObject(object: fabric.FabricObject, source: fabric.Path): Promise<fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>>;
13
+ export declare function eraseCanvasDrawable(object: fabric.FabricObject, vpt: fabric.TMat2D | undefined, source: fabric.Path): Promise<fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>>;
14
+ /**
15
+ * Supports **selective** erasing: only erasable objects are affected by the eraser brush.
16
+ *
17
+ * Supports **{@link inverted}** erasing: the brush can "undo" erasing.
18
+ *
19
+ * Supports **alpha** erasing: setting the alpha channel of the `color` property controls the eraser intensity.
20
+ *
21
+ * In order to support selective erasing, the brush clips the entire canvas and
22
+ * masks all non-erasable objects over the erased path, see {@link draw}.
23
+ *
24
+ * If **{@link inverted}** draws all objects, erasable objects without their eraser, over the erased path.
25
+ * This achieves the desired effect of seeming to erase or undo erasing on erasable objects only.
26
+ *
27
+ * After erasing is done the `end` event {@link ErasingEndEvent} is fired, after which erasing will be committed to the tree.
28
+ * @example
29
+ * canvas = new Canvas();
30
+ * const eraser = new EraserBrush(canvas);
31
+ * canvas.freeDrawingBrush = eraser;
32
+ * canvas.isDrawingMode = true;
33
+ * eraser.on('start', () => {
34
+ * console.log('started erasing');
35
+ * });
36
+ * eraser.on('end', (e) => {
37
+ * const erasedTargets = e.detail.targets;
38
+ * e.preventDefault(); // prevent erasing being committed to the tree
39
+ * eraser.commit(e.detail); // commit manually since default was prevented
40
+ * });
41
+ *
42
+ * In case of performance issues trace {@link drawEffect} calls.
43
+ */
44
+ export declare class EraserBrush extends fabric.PencilBrush {
45
+ /**
46
+ * When set to `true` the brush will create a visual effect of undoing erasing
47
+ */
48
+ inverted: boolean;
49
+ private effectContext;
50
+ private _disposer?;
51
+ private eventEmitter;
52
+ constructor(canvas: fabric.Canvas);
53
+ /**
54
+ * @returns disposer make sure to call it to avoid memory leaks
55
+ */
56
+ on<T extends keyof EventDetailMap>(type: T, cb: (evt: CustomEvent<EventDetailMap[T]>) => any, options?: boolean | AddEventListenerOptions): () => void;
57
+ drawEffect(): void;
58
+ /**
59
+ * @override
60
+ */
61
+ _setBrushStyles(ctx?: CanvasRenderingContext2D): void;
62
+ /**
63
+ * @override {@link drawEffect}
64
+ */
65
+ onMouseDown(pointer: fabric.Point, context: fabric.TEvent<fabric.TPointerEvent>): void;
66
+ /**
67
+ * @override dispose of {@link drawEffect} listener
68
+ */
69
+ onMouseUp(context: fabric.TEvent<fabric.TPointerEvent>): boolean;
70
+ /**
71
+ * @override strictly speaking the eraser needs a full render only if it has opacity set.
72
+ * However since {@link PencilBrush} is designed for subclassing that is what we have to work with.
73
+ */
74
+ needsFullRender(): boolean;
75
+ /**
76
+ * @override erase
77
+ */
78
+ _render(ctx?: CanvasRenderingContext2D): void;
79
+ /**
80
+ * @override
81
+ */
82
+ createPath(pathData: fabric.util.TSimplePathData): fabric.Path<Partial<fabric.PathProps>, fabric.SerializedPathProps, fabric.ObjectEvents>;
83
+ commit({ path, targets }: ErasingEndEventDetail): Promise<void>;
84
+ /**
85
+ * @override fire `erasing:end`
86
+ */
87
+ _finalizeAndAddPath(): void;
88
+ }
89
+ export {};
90
+ //# sourceMappingURL=EraserBrush.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EraserBrush.d.ts","sourceRoot":"","sources":["../../../src/EraserBrush.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAMjC,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,qBAAqB,CAAC,CAAC;AAEjE,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,qBAAqB,CAAC;CAC5B,CAAC;AA0CF,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,CAAC,YAAY,EAC3B,mBAAmB,EAAE,MAAM,CAAC,IAAI,QAMjC;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,CAAC,YAAY,EAC3B,MAAM,EAAE,MAAM,CAAC,IAAI,oGAMpB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,CAAC,YAAY,EAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,CAAC,IAAI,oGAyBpB;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,WAAY,SAAQ,MAAM,CAAC,WAAW;IACjD;;OAEG;IACH,QAAQ,UAAS;IAEjB,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,SAAS,CAAC,CAAe;IAEjC,OAAO,CAAC,YAAY,CAAc;gBAEtB,MAAM,EAAE,MAAM,CAAC,MAAM;IAYjC;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,cAAc,EAC/B,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAChD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB;IAO7C,UAAU;IAWV;;OAEG;IACH,eAAe,CAAC,GAAG,GAAE,wBAAiD;IAKtE;;OAEG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,CAAC,KAAK,EACrB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAC3C,IAAI;IAcP;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,OAAO;IAOhE;;;OAGG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,OAAO,CAAC,GAAG,GAAE,wBAAsD,GAAG,IAAI;IAK1E;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,eAAe;IAiB1C,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,qBAAqB;IAiCrD;;OAEG;IACH,mBAAmB,IAAI,IAAI;CA4B5B"}
@@ -0,0 +1,2 @@
1
+ import{asyncToGenerator as t,inherits as e,createClass as n,regeneratorRuntime as r,classCallCheck as a,callSuper as i,defineProperty as s,assertThisInitialized as o,get as c,getPrototypeOf as u,toConsumableArray as l,slicedToArray as h}from"../../_virtual/_rollupPluginBabelHelpers.js";import*as p from"fabric";import{Group as v}from"fabric";import{erase as f}from"../../core/erase.js";import{ClippingGroup as d}from"./ClippingGroup.js";import{draw as m}from"./ErasingEffect.js";function g(t,e){return t.flatMap((function(t){return t.erasable&&t.intersectsWithObject(e)?t instanceof v&&"deep"===t.erasable?g(t.getObjects(),e):[t]:[]}))}var y=function(t){var e=t.clipPath,n=e instanceof d?e:new d([],{width:t.width,height:t.height});if(e){var r=e.translateToOriginPoint(new p.Point,e.originX,e.originY),a=r.x,i=r.y;e.originX=e.originY="center",p.util.sendObjectToPlane(e,void 0,p.util.createTranslateMatrix(a,i)),n.add(e)}return t.clipPath=n};function w(t,e){var n=y(t);n.add(e),n.set("dirty",!0),t.set("dirty",!0)}function x(t,e){return b.apply(this,arguments)}function b(){return(b=t(r().mark((function t(e,n){var a;return r().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,n.clone();case 2:return a=t.sent,p.util.sendObjectToPlane(a,void 0,e.calcTransformMatrix()),w(e,a),t.abrupt("return",a);case 6:case"end":return t.stop()}}),t)})))).apply(this,arguments)}function k(t,e,n){return P.apply(this,arguments)}function P(){return(P=t(r().mark((function t(e,n,a){var i,s;return r().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,a.clone();case 2:return i=t.sent,s=n&&e.translateToOriginPoint(new p.Point,e.originX,e.originY),p.util.sendObjectToPlane(i,void 0,s?p.util.multiplyTransformMatrixArray([[1,0,0,1,s.x,s.y],n,[1,0,0,1,-s.x,-s.y],e.calcTransformMatrix()]):e.calcTransformMatrix()),w(e,i),t.abrupt("return",i);case 7:case"end":return t.stop()}}),t)})))).apply(this,arguments)}var T=function(v){function d(t){var e;a(this,d),e=i(this,d,[t]),s(o(e),"inverted",!1);var n=document.createElement("canvas"),r=n.getContext("2d");if(!r)throw new Error("Failed to get context");return function(t,e,n){var r=n.width,a=n.height,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;t.width=r,t.height=a,i>1&&(t.setAttribute("width",(r*i).toString()),t.setAttribute("height",(a*i).toString()),e.scale(i,i))}(n,r,t,e.canvas.getRetinaScaling()),e.effectContext=r,e.eventEmitter=new EventTarget,e}var y;return e(d,v),n(d,[{key:"on",value:function(t,e,n){var r=this;return this.eventEmitter.addEventListener(t,e,n),function(){return r.eventEmitter.removeEventListener(t,e,n)}}},{key:"drawEffect",value:function(){m(this.effectContext,{opacity:new p.Color(this.color).getAlpha(),inverted:this.inverted},{canvas:this.canvas})}},{key:"_setBrushStyles",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.canvas.contextTop;c(u(d.prototype),"_setBrushStyles",this).call(this,t),t.strokeStyle="black"}},{key:"onMouseDown",value:function(t,e){var n=this;this.drawEffect(),this._disposer=this.canvas.on("after:render",(function(t){t.ctx===n.canvas.getContext()&&(n.drawEffect(),n._render())})),this.eventEmitter.dispatchEvent(new CustomEvent("start")),c(u(d.prototype),"onMouseDown",this).call(this,t,e)}},{key:"onMouseUp",value:function(t){var e;return c(u(d.prototype),"onMouseUp",this).call(this,t),null===(e=this._disposer)||void 0===e||e.call(this),delete this._disposer,!1}},{key:"needsFullRender",value:function(){return!0}},{key:"_render",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.canvas.getTopContext();c(u(d.prototype),"_render",this).call(this,t),f(this.canvas.getContext(),t,this.effectContext)}},{key:"createPath",value:function(t){var e=c(u(d.prototype),"createPath",this).call(this,t);return e.set(this.inverted?{globalCompositeOperation:"source-over",stroke:"white"}:{globalCompositeOperation:"destination-out",stroke:"black",opacity:new p.Color(this.color).getAlpha()}),e}},{key:"commit",value:(y=t(r().mark((function e(n){var a,i;return r().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return a=n.path,i=n.targets,e.t0=Map,e.next=4,Promise.all([].concat(l(i.map(function(){var e=t(r().mark((function t(e){return r().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.t0=e,t.next=3,x(e,a);case 3:return t.t1=t.sent,t.abrupt("return",[t.t0,t.t1]);case 5:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}())),l([[this.canvas.backgroundImage,this.canvas.backgroundVpt?void 0:this.canvas.viewportTransform],[this.canvas.overlayImage,this.canvas.overlayVpt?void 0:this.canvas.viewportTransform]].filter((function(t){return h(t,1)[0]})).map(function(){var e=t(r().mark((function t(e){var n,i,s;return r().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n=h(e,2),i=n[0],s=n[1],t.t0=i,t.next=4,k(i,s,a);case 4:return t.t1=t.sent,t.abrupt("return",[t.t0,t.t1]);case 6:case"end":return t.stop()}}),t)})));return function(t){return e.apply(this,arguments)}}()))));case 4:e.t1=e.sent,new e.t0(e.t1);case 6:case"end":return e.stop()}}),e,this)}))),function(t){return y.apply(this,arguments)})},{key:"_finalizeAndAddPath",value:function(){var t=this._points;if(!(t.length<2)){var e=this.createPath(this.convertPointsToSVGPath(this.decimate?this.decimatePoints(t,this.decimate):t)),n=g(this.canvas.getObjects(),e),r=new CustomEvent("end",{detail:{path:e,targets:n},cancelable:!0});this.eventEmitter.dispatchEvent(r)&&this.commit({path:e,targets:n}),this.canvas.clearContext(this.canvas.contextTop),this.canvas.requestRenderAll(),this._resetShadow()}}}]),d}(p.PencilBrush);export{T as EraserBrush,w as commitErasing,k as eraseCanvasDrawable,x as eraseObject};
2
+ //# sourceMappingURL=EraserBrush.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EraserBrush.js","sources":["../../../src/EraserBrush.ts"],"sourcesContent":["import * as fabric from 'fabric';\nimport { FabricObject, Group, Path } from 'fabric';\nimport { erase } from '../../core/erase';\nimport { ClippingGroup } from './ClippingGroup';\nimport { draw } from './ErasingEffect';\n\nexport type ErasingEndEventDetail = {\n path: fabric.Path;\n targets: fabric.FabricObject[];\n};\n\nexport type ErasingEndEvent = CustomEvent<ErasingEndEventDetail>;\n\ntype EventDetailMap = {\n start: VoidFunction;\n end: ErasingEndEventDetail;\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 const next =\n curr instanceof ClippingGroup\n ? curr\n : new ClippingGroup([], {\n width: object.width,\n height: object.height,\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', () => {\n * console.log('started erasing');\n * });\n * eraser.on('end', (e) => {\n * const erasedTargets = e.detail.targets;\n * e.preventDefault(); // prevent erasing being committed to the tree\n * eraser.commit(e.detail); // commit manually since default was prevented\n * });\n *\n * In case of performance issues trace {@link drawEffect} calls.\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 private effectContext: CanvasRenderingContext2D;\n private _disposer?: VoidFunction;\n\n private eventEmitter: EventTarget;\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 keyof EventDetailMap>(\n type: T,\n cb: (evt: CustomEvent<EventDetailMap[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 {@link drawEffect}\n */\n onMouseDown(\n pointer: fabric.Point,\n context: fabric.TEvent<fabric.TPointerEvent>\n ): void {\n this.drawEffect();\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.drawEffect();\n this._render();\n });\n this.eventEmitter.dispatchEvent(new CustomEvent('start'));\n super.onMouseDown(pointer, context);\n }\n\n /**\n * @override dispose of {@link drawEffect} listener\n */\n onMouseUp(context: fabric.TEvent<fabric.TPointerEvent>): boolean {\n super.onMouseUp(context);\n this._disposer?.();\n delete this._disposer;\n return false;\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\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({ path, targets }: ErasingEndEventDetail) {\n 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)\n .map(async ([object, vptFlag]) => {\n return [\n object,\n await eraseCanvasDrawable(object as FabricObject, vptFlag, path),\n ] as const;\n }),\n ])\n );\n }\n\n /**\n * @override fire `erasing:end`\n */\n _finalizeAndAddPath(): void {\n const points = this['_points'];\n\n if (points.length < 2) {\n return;\n }\n\n const path = this.createPath(\n this.convertPointsToSVGPath(\n this.decimate ? this.decimatePoints(points, this.decimate) : points\n )\n );\n const targets = walk(this.canvas.getObjects(), path);\n\n const ev = new CustomEvent('end', {\n detail: {\n path,\n targets,\n },\n cancelable: true,\n });\n this.eventEmitter.dispatchEvent(ev) && this.commit({ path, targets });\n\n this.canvas.clearContext(this.canvas.contextTop);\n this.canvas.requestRenderAll();\n\n this._resetShadow();\n }\n}\n"],"names":["walk","objects","path","flatMap","object","erasable","intersectsWithObject","Group","getObjects","assertClippingGroup","curr","clipPath","next","ClippingGroup","width","height","_curr$translateToOrig","translateToOriginPoint","fabric","Point","originX","originY","x","y","util","sendObjectToPlane","undefined","createTranslateMatrix","add","commitErasing","sourceInObjectPlane","set","eraseObject","_x","_x2","_eraseObject","apply","this","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee4","source","clone","wrap","_context4","prev","sent","calcTransformMatrix","abrupt","stop","eraseCanvasDrawable","_x3","_x4","_x5","_eraseCanvasDrawable","_callee5","vpt","d","_context5","multiplyTransformMatrixArray","EraserBrush","_fabric$PencilBrush","canvas","_this","_classCallCheck","_callSuper","_defineProperty","_assertThisInitialized","el","document","createElement","ctx","getContext","Error","_ref","retinaScaling","length","setAttribute","toString","scale","setCanvasDimensions","getRetinaScaling","effectContext","eventEmitter","EventTarget","_commit","_inherits","_createClass","key","value","type","cb","options","_this2","addEventListener","removeEventListener","draw","opacity","Color","color","getAlpha","inverted","contextTop","_get","_getPrototypeOf","prototype","call","strokeStyle","pointer","context","_this3","drawEffect","_disposer","on","_ref2","_render","dispatchEvent","CustomEvent","_this$_disposer","getTopContext","erase","pathData","globalCompositeOperation","stroke","_callee3","_ref3","targets","_context3","t0","Map","Promise","all","concat","_toConsumableArray","map","_ref4","_callee","_context","t1","_x7","backgroundImage","backgroundVpt","viewportTransform","overlayImage","overlayVpt","filter","_ref5","_slicedToArray","_ref8","_callee2","_ref7","_ref9","vptFlag","_context2","_x8","_x6","points","createPath","convertPointsToSVGPath","decimate","decimatePoints","ev","detail","cancelable","commit","clearContext","requestRenderAll","_resetShadow","PencilBrush"],"mappings":"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"}
@@ -0,0 +1,24 @@
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
+ */
15
+ export declare function draw(ctx: CanvasRenderingContext2D, { inverted, opacity }: {
16
+ inverted: boolean;
17
+ opacity: number;
18
+ }, { canvas, objects, background, overlay, }: {
19
+ canvas: Canvas;
20
+ objects?: FabricObject[];
21
+ background?: FabricObject;
22
+ overlay?: FabricObject;
23
+ }): void;
24
+ //# 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,EAAS,YAAY,EAAE,MAAM,QAAQ,CAAC;AA+CrD;;;;;;;;;;;;GAYG;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,2 @@
1
+ import{toConsumableArray as t}from"../../_virtual/_rollupPluginBabelHelpers.js";import{Group as r}from"fabric";import{ClippingGroup as a}from"./ClippingGroup.js";function e(t){return t.flatMap((function(t){return!t.erasable||t.isNotVisible()?[]:t instanceof r&&"deep"===t.erasable?e(t.getObjects()):[t]}))}function o(r,o,i){var n=o.inverted,c=o.opacity,l=i.canvas,p=i.objects,s=void 0===p?l._objectsToRender||l._objects:p,u=i.background,d=void 0===u?l.backgroundImage:u,f=i.overlay,b=void 0===f?l.overlayImage:f,h=1-c,m=e([].concat(t(s),t([d,b].filter((function(t){return!!t}))))).map((function(t){if(!n){var r,e=t.opacity;return t.opacity*=h,null===(r=t.parent)||void 0===r||r.set("dirty",!0),{object:t,opacity:e}}if(t.clipPath instanceof a)return t.clipPath.blockErasing=!0,t.clipPath.set("dirty",!0),t.set("dirty",!0),{object:t,clipPath:t.clipPath}}));!function(r,a,e){a.clearContext(r),r.imageSmoothingEnabled=a.imageSmoothingEnabled,r.imageSmoothingQuality="high",r.patternQuality="best",a._renderBackground(r),r.save(),r.transform.apply(r,t(a.viewportTransform)),e.forEach((function(t){return t.render(r)})),r.restore();var o=a.clipPath;o&&(o._set("canvas",a),o.shouldCache(),o._transformDone=!0,o.renderCache({forClipping:!0}),a.drawClipPathOnCanvas(r,o)),a._renderOverlay(r)}(r,l,s),m.forEach((function(t){var r;t&&(t.opacity?(t.object.opacity=c,null===(r=t.object.parent)||void 0===r||r.set("dirty",!0)):t.clipPath&&(t.clipPath.blockErasing=!1,t.clipPath.set("dirty",!0),t.object.set("dirty",!0)))}))}export{o as draw};
2
+ //# sourceMappingURL=ErasingEffect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErasingEffect.js","sources":["../../../src/ErasingEffect.ts"],"sourcesContent":["import { Canvas, Group, FabricObject } 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 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 */\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 = 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","draw","ctx","_ref","_ref2","inverted","opacity","canvas","_ref2$objects","_objectsToRender","_objects","_ref2$background","background","backgroundImage","_ref2$overlay","overlay","overlayImage","alpha","restore","concat","_toConsumableArray","filter","d","map","_object$parent","parent","set","clipPath","ClippingGroup","clearContext","imageSmoothingEnabled","imageSmoothingQuality","patternQuality","_renderBackground","save","transform","apply","viewportTransform","forEach","render","_set","shouldCache","_transformDone","renderCache","forClipping","drawClipPathOnCanvas","_renderOverlay","drawCanvas","entry","_entry$object$parent"],"mappings":"kKAGA,SAASA,EAAKC,GACZ,OAAOA,EAAQC,SAAQ,SAACC,GACtB,OAAKA,EAAOC,UAAYD,EAAOE,eACtB,GACEF,aAAkBG,GAA6B,SAApBH,EAAOC,SACpCJ,EAAKG,EAAOI,cAEZ,CAACJ,EAEZ,GACF,CA+CO,SAASK,EACdC,EAA6BC,EAAAC,GAa7B,IAZEC,EAAQF,EAARE,SAAUC,EAAOH,EAAPG,QAEVC,EAAMH,EAANG,OAAMC,EAAAJ,EACNV,QAAAA,OAAO,IAAAc,EAAGD,EAAOE,kBAAoBF,EAAOG,SAAQF,EAAAG,EAAAP,EACpDQ,WAAAA,OAAU,IAAAD,EAAGJ,EAAOM,gBAAeF,EAAAG,EAAAV,EACnCW,QAAAA,OAAO,IAAAD,EAAGP,EAAOS,aAAYF,EASzBG,EAAQ,EAAIX,EACZY,EAAUzB,EAAI,GAAA0B,OAAAC,EACf1B,GAAO0B,EACN,CAACR,EAAYG,GAA4BM,QAAO,SAACC,GAAC,QAAOA,CAAC,OAC7DC,KAAI,SAAC3B,GACN,IAAKS,EAAU,CAAA,IAAAmB,EAEPlB,EAAUV,EAAOU,QAGvB,OAFAV,EAAOU,SAAWW,EACLO,QAAbA,EAAA5B,EAAO6B,cAAPD,IAAaA,GAAbA,EAAeE,IAAI,SAAS,GACrB,CAAE9B,OAAAA,EAAQU,QAAAA,EACnB,CAAO,GAAIV,EAAO+B,oBAAoBC,EAKpC,OAHAhC,EAAO+B,SAAuB,cAAI,EAClC/B,EAAO+B,SAASD,IAAI,SAAS,GAC7B9B,EAAO8B,IAAI,SAAS,GACb,CAAE9B,OAAAA,EAAQ+B,SAAU/B,EAAO+B,SAEtC,KA/EF,SACEzB,EACAK,EACAb,GAEAa,EAAOsB,aAAa3B,GAEpBA,EAAI4B,sBAAwBvB,EAAOuB,sBACnC5B,EAAI6B,sBAAwB,OAE5B7B,EAAI8B,eAAiB,OAErBzB,EAAO0B,kBAAkB/B,GAEzBA,EAAIgC,OACJhC,EAAIiC,UAASC,MAAblC,EAAGkB,EAAcb,EAAO8B,oBACxB3C,EAAQ4C,SAAQ,SAAC1C,GAAM,OAAKA,EAAO2C,OAAOrC,MAC1CA,EAAIgB,UAEJ,IAAMS,EAAWpB,EAAOoB,SACpBA,IAEFA,EAASa,KAAK,SAAUjC,GACxBoB,EAASc,cACTd,EAASe,gBAAiB,EAC1Bf,EAASgB,YAAY,CAAEC,aAAa,IACpCrC,EAAOsC,qBAAqB3C,EAAKyB,IAGnCpB,EAAOuC,eAAe5C,EACxB,CAoDE6C,CAAW7C,EAAKK,EAAQb,GAGxBwB,EAAQoB,SAAQ,SAACU,GAII,IAAAC,EAHdD,IAGDA,EAAM1C,SACR0C,EAAMpD,OAAOU,QAAUA,EACJ,QAAnB2C,EAAAD,EAAMpD,OAAO6B,cAAM,IAAAwB,GAAnBA,EAAqBvB,IAAI,SAAS,IACzBsB,EAAMrB,WACfqB,EAAMrB,SAAuB,cAAI,EACjCqB,EAAMrB,SAASD,IAAI,SAAS,GAC5BsB,EAAMpD,OAAO8B,IAAI,SAAS,IAE9B,GACF"}
@@ -0,0 +1,19 @@
1
+ import { FabricObject, Path, TMat2D } from 'fabric';
2
+ /**
3
+ * Utility to apply a clip path to an object, merging with its existing clip path.
4
+ * @param {FabricObject} object The eraser path in canvas coordinate plane
5
+ * @param {FabricObject} clipPath The clipPath to apply to the path
6
+ * @param {TMat2D} t The transform matrix of the object that the clip path belongs to
7
+ * @returns {Path} path with clip path
8
+ */
9
+ export declare function clipObject(object: FabricObject, clipPath: FabricObject, t: TMat2D): FabricObject<Partial<import("fabric").FabricObjectProps>, import("fabric").SerializedObjectProps, import("fabric").ObjectEvents>;
10
+ /**
11
+ * Utility to apply a clip path to a path.
12
+ * Used to preserve clipping on eraser paths in nested objects.
13
+ * Called when a group has a clip path that should be applied to the path before applying erasing on the group's objects.
14
+ * @param {Path} path The eraser path
15
+ * @param {FabricObject} object The clipPath to apply to path belongs to object
16
+ * @returns {Promise<Path>}
17
+ */
18
+ export declare function clonePathWithClipPath(path: Path, object: FabricObject & Required<Pick<FabricObject, 'clipPath'>>): Promise<FabricObject<Partial<import("fabric").FabricObjectProps>, import("fabric").SerializedObjectProps, import("fabric").ObjectEvents>>;
19
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,YAAY,EACtB,CAAC,EAAE,MAAM,oIAuBV;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,6IAShE"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@erase2d/fabric",
3
+ "version": "1.0.0",
4
+ "description": "Fabric.js erase2d bindings",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/ShaMan123/erase2d.git"
12
+ },
13
+ "author": "ShaMan123",
14
+ "license": "MIT",
15
+ "bugs": {
16
+ "url": "https://github.com/ShaMan123/erase2d/issues"
17
+ },
18
+ "homepage": "https://github.com/ShaMan123/erase2d#readme",
19
+ "keywords": [
20
+ "erase",
21
+ "erase2d",
22
+ "eraser",
23
+ "erasing",
24
+ "canvas",
25
+ "fabric"
26
+ ],
27
+ "peerDependencies": {
28
+ "fabric": "^6.0.0-beta19"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "fabric": {
32
+ "optional": false
33
+ }
34
+ },
35
+ "module": "./dist/fabric/index.js",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/fabric/index.d.ts",
39
+ "import": "./dist/fabric/index.js",
40
+ "require": null,
41
+ "default": "./dist/fabric/index.js"
42
+ }
43
+ }
44
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": ["../../tsconfig.json"],
3
+ "include": ["src", "./types/fabric.d.ts", "index.ts"],
4
+ "compilerOptions": { "outDir": "./dist" }
5
+ }
@@ -0,0 +1,20 @@
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
+ }