Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BaseBrush): clipPath #7175

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
373a450
Update base_brush.class.js
ShaMan123 Jul 1, 2021
b551683
Update circle_brush.class.js
ShaMan123 Jul 1, 2021
cc5374d
Update pencil_brush.class.js
ShaMan123 Jul 1, 2021
a824e91
BREAKING CHANGE
ShaMan123 Jul 1, 2021
3cb75e4
draw clipPath when adding a brush segment
ShaMan123 Jul 1, 2021
07fc81e
addClipPathToResult
ShaMan123 Jul 1, 2021
fc92fad
Update pencil_brush.class.js
ShaMan123 Jul 1, 2021
99ff886
JSDOC
ShaMan123 Jul 1, 2021
97ee528
lint
ShaMan123 Jul 1, 2021
24489c4
Update CHANGELOG.md
ShaMan123 Jul 1, 2021
2640b2e
BREAKING: rename `render` to `renderAll`
ShaMan123 Jul 1, 2021
9fea119
handle `absolutePositioned` case
ShaMan123 Jul 1, 2021
798199d
support `inverted` property
ShaMan123 Jul 1, 2021
8b30537
fix(absolutePositioned)
ShaMan123 Jul 1, 2021
add527c
Update eraser_brush.mixin.js
ShaMan123 Jul 1, 2021
5680f1f
support clipping eraser brush
ShaMan123 Jul 1, 2021
ceaf13d
lint
ShaMan123 Jul 1, 2021
251a5df
revert dist
ShaMan123 Jul 1, 2021
8fe3e37
Merge remote-tracking branch 'upstream/master' into brush-clip-path
ShaMan123 Jul 1, 2021
a3eb5d7
Update CHANGELOG.md
ShaMan123 Jul 1, 2021
5747e02
fix rendering overlay with clip path
ShaMan123 Jul 1, 2021
1ebccf4
Update base_brush.class.js
ShaMan123 Jul 1, 2021
368dd71
Update eraser_brush.mixin.js
ShaMan123 Jul 2, 2021
eef5936
Merge remote-tracking branch 'upstream/master' into brush-clip-path
ShaMan123 Jan 16, 2022
85a6317
Update base_brush.class.js
ShaMan123 Jan 16, 2022
c2c856e
Merge remote-tracking branch 'upstream/master' into brush-clip-path
ShaMan123 Jan 17, 2022
2442eed
Update pencil_brush.class.js
ShaMan123 Jan 17, 2022
35c1364
Merge remote-tracking branch 'upstream/master' into brush-clip-path
ShaMan123 Feb 17, 2022
f927143
Update base_brush.class.js
ShaMan123 Feb 17, 2022
d8eb923
Merge branch 'master' into pr/7175
ShaMan123 Apr 26, 2022
8e20486
update code to master
ShaMan123 Apr 26, 2022
a8c1818
async
ShaMan123 Apr 26, 2022
fddbae7
async
ShaMan123 Apr 26, 2022
c36649f
rename: BaseBrush `render` <-> `_render`
ShaMan123 Apr 26, 2022
2d4aef7
lint
ShaMan123 Apr 26, 2022
126514b
async test
ShaMan123 Apr 26, 2022
60b5829
Update base_brush.class.js
ShaMan123 Apr 26, 2022
3954fca
Merge branch 'master' into pr/7175
ShaMan123 May 1, 2022
3f0f328
fix(): `absolutePositioned`
ShaMan123 May 12, 2022
082b063
Revert whitespace
ShaMan123 May 12, 2022
b52caa0
Update .eslintrc.json
ShaMan123 May 12, 2022
0e4b9ef
Revert "Update .eslintrc.json"
ShaMan123 May 12, 2022
f6669ab
Update .eslintrc.json
ShaMan123 May 12, 2022
82c8918
Merge branch 'master' into pr/7175
ShaMan123 Jun 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"env": {
"browser": true
},
"parserOptions": {
"ecmaVersion": 2017
},
"globals": {
"Promise": true,
"define": true,
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ Use this [code](https://gist.github.com/ShaMan123/6c5c4ca2cc720a2700848a2deb6add
- docs(): Clarify viewport transformations doc [`#7401`](https://github.com/fabricjs/fabric.js/pull/7401)
- docs(): specify default value and docs for enablePointerEvents [`#7386`](https://github.com/fabricjs/fabric.js/pull/7386)
- feat(fabric.PencilBrush): add an option to draw a straight line while pressing a key [`#7034`](https://github.com/fabricjs/fabric.js/pull/7034)
- feat(fabric.BaseBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- feat(fabric.CircleBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- feat(fabric.PencilBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- feat(fabric.PatternBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- feat(fabric.SprayBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- feat(fabric.EraserBrush): added `clipPath` property [#7175](https://github.com/fabricjs/fabric.js/pull/7175).
- Breaking: renamed `fabric.SprayBrush` `render` method to `renderChunk`.
- Breaking: renamed `fabric.EraserBrush` `render` method to `renderAll`.


## [4.6.0]
Expand Down
92 changes: 87 additions & 5 deletions src/brushes/base_brush.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @type Number
* @default
*/
strokeMiterLimit: 10,
strokeMiterLimit: 10,

/**
* Stroke Dash Array.
Expand All @@ -61,9 +61,13 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @type Boolean
* @default false
*/

limitedToCanvasSize: false,

/**
* Same as fabric.Object `clipPath` property.
* The clip path is positioned relative to the top left corner of the viewport.
*/
clipPath: undefined,

/**
* Sets brush styles
Expand Down Expand Up @@ -115,16 +119,94 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype

needsFullRender: function() {
var color = new fabric.Color(this.color);
return color.getAlpha() < 1 || !!this.shadow;
return color.getAlpha() < 1 || !!this.shadow || (this.clipPath && this.clipPath.isCacheDirty());
},

/**
* needed for `absolutePositioned` `clipPath`
* @private
*/
calcTransformMatrix: function () {
return fabric.util.invertTransform(this.canvas.viewportTransform);
},

/**
* Removes brush shadow styles
* @private
* @param {CanvasRenderingContext2D} ctx
* @param {fabric.Object} clipPath
*/
_resetShadow: function() {
drawClipPathOnCache: function (ctx, clipPath) {
fabric.Object.prototype.drawClipPathOnCache.call(this, ctx, clipPath);
},

/**
* @private
* @param {CanvasRenderingContext2D} ctx
* @param {fabric.Object} clipPath
*/
_drawClipPath: function (ctx, clipPath) {
if (!clipPath) {
return;
}
clipPath.canvas = this.canvas;
var v = fabric.util.invertTransform(this.canvas.viewportTransform);
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
fabric.Object.prototype._drawClipPath.call(this, ctx, clipPath);
ctx.restore();
},

/**
* Adds the clip path to the resulting object created by the brush
* @private
* @param {fabric.Object} result
*/
_addClipPathToResult: function (result) {
if (!this.clipPath) {
return Promise.resolve();
}
var t = result.calcTransformMatrix();
if (!this.clipPath.absolutePositioned) {
t = fabric.util.multiplyTransformMatrices(this.canvas.viewportTransform, t);
}
return this.clipPath.clone(['inverted'])
.then(function (clipPath) {
var desiredTransform = fabric.util.multiplyTransformMatrices(
fabric.util.invertTransform(t),
clipPath.calcTransformMatrix()
);
fabric.util.applyTransformToObject(clipPath, desiredTransform);
result.set('clipPath', clipPath);
});
},

/**
* Subclasses should override this method
* @private
* @param {CanvasRenderingContext2D} ctx
*/
_render: function (ctx) { // eslint-disable-line no-unused-vars

},

/**
* Render the full state of the brush
* @private
*/
render: function () {
var ctx = this.canvas.contextTop;
this._saveAndTransform(ctx);
this._render(ctx);
this._drawClipPath(ctx, this.clipPath);
ctx.restore();
},

/**
* Removes brush shadow styles
* @private
* @param {CanvasRenderingContext2D} ctx
*/
_resetShadow: function(ctx) {
ctx.shadowColor = '';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
},
Expand Down
43 changes: 23 additions & 20 deletions src/brushes/circle_brush.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
ctx = this.canvas.contextTop;
this._saveAndTransform(ctx);
this.dot(ctx, point);
this._drawClipPath(ctx, this.clipPath);
ctx.restore();
},

Expand All @@ -52,17 +53,14 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
},

/**
* Render the full state of the brush
* @private
* @param {CanvasRenderingContext2D} ctx
*/
_render: function() {
var ctx = this.canvas.contextTop, i, len,
points = this.points;
this._saveAndTransform(ctx);
_render: function (ctx) {
var i, len, points = this.points;
for (i = 0, len = points.length; i < len; i++) {
this.dot(ctx, points[i]);
}
ctx.restore();
},

/**
Expand All @@ -76,7 +74,7 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
if (this.needsFullRender()) {
this.canvas.clearContext(this.canvas.contextTop);
this.addPoint(pointer);
this._render();
this.render();
}
else {
this.drawDot(pointer);
Expand All @@ -86,7 +84,14 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
/**
* Invoked on mouse up
*/
onMouseUp: function() {
onMouseUp: function () {
this._finalizeAndAddPath();
},

/**
* @private
*/
_finalizeAndAddPath: async function () {
var originalRenderOnAddRemove = this.canvas.renderOnAddRemove, i, len;
this.canvas.renderOnAddRemove = false;

Expand All @@ -104,20 +109,18 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
});

this.shadow && (circle.shadow = new fabric.Shadow(this.shadow));

circles.push(circle);
}
var group = new fabric.Group(circles);
group.canvas = this.canvas;

this.canvas.fire('before:path:created', { path: group });
this.canvas.add(group);
this.canvas.fire('path:created', { path: group });

this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
this.canvas.requestRenderAll();
var canvas = this.canvas, ctx = canvas.contextTop;
var group = new fabric.Group(circles, { canvas: canvas });
await this._addClipPathToResult(group);
canvas.fire('before:path:created', { path: group });
canvas.add(group);
canvas.fire('path:created', { path: group });
canvas.clearContext(ctx);
this._resetShadow(ctx);
canvas.renderOnAddRemove = originalRenderOnAddRemove;
canvas.requestRenderAll();
},

/**
Expand Down
4 changes: 2 additions & 2 deletions src/brushes/pattern_brush.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* Creates path
*/
createPath: function(pathData) {
var path = this.callSuper('createPath', pathData),
createPath: async function(pathData) {
var path = await this.callSuper('createPath', pathData),
topLeft = path._getLeftTopCoords().scalarAdd(path.strokeWidth / 2);

path.stroke = new fabric.Pattern({
Expand Down
33 changes: 15 additions & 18 deletions src/brushes/pencil_brush.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
// capture coordinates immediately
// this allows to draw dots (when movement never occurs)
this._captureDrawingPath(pointer);
this._render();
this.render();
},

/**
Expand All @@ -86,7 +86,7 @@
// redraw curve
// clear top canvas
this.canvas.clearContext(this.canvas.contextTop);
this._render();
this.render();
}
else {
var points = this._points, length = points.length, ctx = this.canvas.contextTop;
Expand All @@ -98,6 +98,7 @@
}
this.oldEnd = this._drawSegment(ctx, points[length - 2], points[length - 1], true);
ctx.stroke();
this._drawClipPath(ctx, this.clipPath);
ctx.restore();
}
}
Expand Down Expand Up @@ -175,7 +176,6 @@
p1 = this._points[0],
p2 = this._points[1];
ctx = ctx || this.canvas.contextTop;
this._saveAndTransform(ctx);
ctx.beginPath();
//if we only have 2 points in the path and they are the same
//it means that the user only clicked the canvas without moving the mouse
Expand All @@ -202,7 +202,6 @@
// the bezier control point
ctx.lineTo(p1.x, p1.y);
ctx.stroke();
ctx.restore();
},

/**
Expand Down Expand Up @@ -230,7 +229,7 @@
* @param {(string|number)[][]} pathData Path data
* @return {fabric.Path} Path to add on canvas
*/
createPath: function(pathData) {
createPath: async function(pathData) {
var path = new fabric.Path(pathData, {
fill: null,
stroke: this.color,
Expand All @@ -244,7 +243,7 @@
this.shadow.affectStroke = true;
path.shadow = new fabric.Shadow(this.shadow);
}

await this._addClipPathToResult(path);
return path;
},

Expand Down Expand Up @@ -278,8 +277,8 @@
* we use the points captured to create an new fabric path object
* and add it to the fabric canvas.
*/
_finalizeAndAddPath: function() {
var ctx = this.canvas.contextTop;
_finalizeAndAddPath: async function() {
var canvas = this.canvas, ctx = canvas.contextTop;
ctx.closePath();
if (this.decimate) {
this._points = this.decimatePoints(this._points, this.decimate);
Expand All @@ -290,21 +289,19 @@
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
// whereas Chrome 10 renders nothing
this.canvas.requestRenderAll();
canvas.requestRenderAll();
return;
}

var path = this.createPath(pathData);
this.canvas.clearContext(this.canvas.contextTop);
this.canvas.fire('before:path:created', { path: path });
this.canvas.add(path);
this.canvas.requestRenderAll();
var path = await this.createPath(pathData);
canvas.clearContext(ctx);
canvas.fire('before:path:created', { path: path });
canvas.add(path);
canvas.requestRenderAll();
path.setCoords();
this._resetShadow();

this._resetShadow(ctx);

// fire event 'path' created
this.canvas.fire('path:created', { path: path });
canvas.fire('path:created', { path: path });
}
});
})();
Loading