From 9f9bdec234b8b079148fb18cd647f3d39e89e4ae Mon Sep 17 00:00:00 2001 From: boomyao Date: Mon, 19 Feb 2018 14:07:18 +0800 Subject: [PATCH 1/5] add simplePencil_brush that _render not needs to redraw everything, suitable for low-profile phone. --- src/brushes/simplePencil_brush.class.js | 222 ++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/brushes/simplePencil_brush.class.js diff --git a/src/brushes/simplePencil_brush.class.js b/src/brushes/simplePencil_brush.class.js new file mode 100644 index 00000000000..4757781e999 --- /dev/null +++ b/src/brushes/simplePencil_brush.class.js @@ -0,0 +1,222 @@ +(function() { + + /** + * SimplePencilBrush class + * @class fabric.SimplePencilBrush + * @extends fabric.BaseBrush + */ + fabric.SimplePencilBrush = fabric.util.createClass(fabric.BaseBrush, { + + /** + * Constructor + * @param {fabric.Canvas} canvas + * @return {fabric.SimplePencilBrush} Instance of a pencil brush + */ + initialize: function(canvas) { + this.canvas = canvas; + this._points = []; + }, + + /** + * Inovoked on mouse down + * @param {Object} pointer + */ + onMouseDown: function(pointer) { + this._prepareForDrawing(pointer); + // capture coordinates immediately + // this allows to draw dots (when movement never occurs) + this._captureDrawingPath(pointer); + this._render(); + }, + + /** + * Inovoked on mouse move + * @param {Object} pointer + */ + onMouseMove: function(pointer) { + this._captureDrawingPath(pointer) && this._render(); + // redraw curve + // clear top canvas + }, + + /** + * Invoked on mouse up + */ + onMouseUp: function() { + this._finalizeAndAddPath(); + }, + + /** + * @private + * @param {Object} pointer Actual mouse position related to the canvas. + */ + _prepareForDrawing: function(pointer) { + + var p = new fabric.Point(pointer.x, pointer.y); + + this._reset(); + this._addPoint(p); + + this.canvas.contextTop.moveTo(p.x, p.y); + }, + + /** + * @private + * @param {fabric.Point} point Point to be added to points array + */ + _addPoint: function(point) { + if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) { + return false; + } + this._points.push(point); + return true; + }, + + /** + * Clear points array and set contextTop canvas style. + * @private + */ + _reset: function() { + this._points.length = 0; + this._curPointIdx = 0; + this._setBrushStyles(); + this._setShadow(); + }, + + /** + * @private + * @param {Object} pointer Actual mouse position related to the canvas. + */ + _captureDrawingPath: function(pointer) { + var pointerPoint = new fabric.Point(pointer.x, pointer.y); + return this._addPoint(pointerPoint); + }, + + /** + * Draw a smooth path on the topCanvas using quadraticCurveTo + * @private + */ + _render: function() { + var ctx = this.canvas.contextTop, + p1 = this._points[this._curPointIdx], + p2 = this._points[this._curPointIdx + 1]; + + 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 + //then we should be drawing a dot. A path isn't drawn between two identical dots + //that's why we set them apart a bit + if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { + var width = this.width / 1000; + p1 = new fabric.Point(p1.x, p1.y); + p2 = new fabric.Point(p2.x, p2.y); + p1.x -= width; + p2.x += width; + } + ctx.moveTo(p1.x, p1.y); + var midPoint = p1.midPointFrom(p2); + ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); + + ctx.lineTo(p2.x, p2.y); + ctx.stroke(); + ctx.restore(); + this._curPointIdx++; + }, + + /** + * Converts points to SVG path + * @param {Array} points Array of points + * @return {String} SVG path + */ + convertPointsToSVGPath: function(points) { + var path = [], i, width = this.width / 1000, + p1 = new fabric.Point(points[0].x, points[0].y), + p2 = new fabric.Point(points[1].x, points[1].y), + len = points.length, multSignX = 1, multSignY = 1, manyPoints = len > 2; + + if (manyPoints) { + multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1; + multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1; + } + path.push('M ', p1.x - multSignX * width, ' ', p1.y - multSignY * width, ' '); + for (i = 1; i < len; i++) { + if (!p1.eq(p2)) { + var midPoint = p1.midPointFrom(p2); + // p1 is our bezier control point + // midpoint is our endpoint + // start point is p(i-1) value. + path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' '); + } + p1 = points[i]; + if ((i + 1) < points.length) { + p2 = points[i + 1]; + } + } + if (manyPoints) { + multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1; + multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1; + } + path.push('L ', p1.x + multSignX * width, ' ', p1.y + multSignY * width); + return path; + }, + + /** + * Creates fabric.Path object to add on canvas + * @param {String} pathData Path data + * @return {fabric.Path} Path to add on canvas + */ + createPath: function(pathData) { + var path = new fabric.Path(pathData, { + fill: null, + stroke: this.color, + strokeWidth: this.width, + strokeLineCap: this.strokeLineCap, + strokeMiterLimit: this.strokeMiterLimit, + strokeLineJoin: this.strokeLineJoin, + strokeDashArray: this.strokeDashArray, + }); + var position = new fabric.Point(path.left + path.width / 2, path.top + path.height / 2); + position = path.translateToGivenOrigin(position, 'center', 'center', path.originX, path.originY); + path.top = position.y; + path.left = position.x; + if (this.shadow) { + this.shadow.affectStroke = true; + path.setShadow(this.shadow); + } + + return path; + }, + + /** + * On mouseup after drawing the path on contextTop canvas + * 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; + ctx.closePath(); + + var pathData = this.convertPointsToSVGPath(this._points).join(''); + if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') { + // do not create 0 width/height paths, as they are + // rendered inconsistently across browsers + // Firefox 4, for example, renders a dot, + // whereas Chrome 10 renders nothing + this.canvas.requestRenderAll(); + return; + } + + var path = this.createPath(pathData); + this.canvas.clearContext(this.canvas.contextTop); + this.canvas.add(path); + this.canvas.renderAll(); + path.setCoords(); + this._resetShadow(); + + + // fire event 'path' created + this.canvas.fire('path:created', { path: path }); + } + }); +})(); From a660f316eafbd5e5d239a960895ed1b54f234b9a Mon Sep 17 00:00:00 2001 From: boomyao Date: Mon, 19 Feb 2018 22:17:09 +0800 Subject: [PATCH 2/5] add drawdot func to pencilbrush --- src/brushes/pencil_brush.class.js | 59 +++++-- src/brushes/simplePencil_brush.class.js | 222 ------------------------ 2 files changed, 41 insertions(+), 240 deletions(-) delete mode 100644 src/brushes/simplePencil_brush.class.js diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 6beccb07d95..95734c075d3 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -17,6 +17,42 @@ this._points = []; }, + /** + * Invoked inside on mouse down and mouse move + * @param {Object} pointer + */ + drawDot: function (pointer) { + var point = new fabric.Point(pointer.x, pointer.y); + if (!this._addPoint(point)) { + return; + } + var ctx = this.canvas.contextTop, + p1 = this._points[this._curPointIdx], + p2 = this._points[this._curPointIdx + 1]; + + 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 + //then we should be drawing a dot. A path isn't drawn between two identical dots + //that's why we set them apart a bit + if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { + var width = this.width / 1000; + p1 = new fabric.Point(p1.x, p1.y); + p2 = new fabric.Point(p2.x, p2.y); + p1.x -= width; + p2.x += width; + } + ctx.moveTo(p1.x, p1.y); + var midPoint = p1.midPointFrom(p2); + ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); + + ctx.lineTo(p2.x, p2.y); + ctx.stroke(); + ctx.restore(); + this._curPointIdx++; + }, + /** * Inovoked on mouse down * @param {Object} pointer @@ -25,8 +61,7 @@ this._prepareForDrawing(pointer); // capture coordinates immediately // this allows to draw dots (when movement never occurs) - this._captureDrawingPath(pointer); - this._render(); + this.drawDot(pointer); }, /** @@ -34,11 +69,7 @@ * @param {Object} pointer */ onMouseMove: function(pointer) { - this._captureDrawingPath(pointer); - // redraw curve - // clear top canvas - this.canvas.clearContext(this.canvas.contextTop); - this._render(); + this.drawDot(pointer); }, /** @@ -68,9 +99,10 @@ */ _addPoint: function(point) { if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) { - return; + return false; } this._points.push(point); + return true; }, /** @@ -79,20 +111,11 @@ */ _reset: function() { this._points.length = 0; - + this._curPointIdx = 0; this._setBrushStyles(); this._setShadow(); }, - /** - * @private - * @param {Object} pointer Actual mouse position related to the canvas. - */ - _captureDrawingPath: function(pointer) { - var pointerPoint = new fabric.Point(pointer.x, pointer.y); - this._addPoint(pointerPoint); - }, - /** * Draw a smooth path on the topCanvas using quadraticCurveTo * @private diff --git a/src/brushes/simplePencil_brush.class.js b/src/brushes/simplePencil_brush.class.js deleted file mode 100644 index 4757781e999..00000000000 --- a/src/brushes/simplePencil_brush.class.js +++ /dev/null @@ -1,222 +0,0 @@ -(function() { - - /** - * SimplePencilBrush class - * @class fabric.SimplePencilBrush - * @extends fabric.BaseBrush - */ - fabric.SimplePencilBrush = fabric.util.createClass(fabric.BaseBrush, { - - /** - * Constructor - * @param {fabric.Canvas} canvas - * @return {fabric.SimplePencilBrush} Instance of a pencil brush - */ - initialize: function(canvas) { - this.canvas = canvas; - this._points = []; - }, - - /** - * Inovoked on mouse down - * @param {Object} pointer - */ - onMouseDown: function(pointer) { - this._prepareForDrawing(pointer); - // capture coordinates immediately - // this allows to draw dots (when movement never occurs) - this._captureDrawingPath(pointer); - this._render(); - }, - - /** - * Inovoked on mouse move - * @param {Object} pointer - */ - onMouseMove: function(pointer) { - this._captureDrawingPath(pointer) && this._render(); - // redraw curve - // clear top canvas - }, - - /** - * Invoked on mouse up - */ - onMouseUp: function() { - this._finalizeAndAddPath(); - }, - - /** - * @private - * @param {Object} pointer Actual mouse position related to the canvas. - */ - _prepareForDrawing: function(pointer) { - - var p = new fabric.Point(pointer.x, pointer.y); - - this._reset(); - this._addPoint(p); - - this.canvas.contextTop.moveTo(p.x, p.y); - }, - - /** - * @private - * @param {fabric.Point} point Point to be added to points array - */ - _addPoint: function(point) { - if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) { - return false; - } - this._points.push(point); - return true; - }, - - /** - * Clear points array and set contextTop canvas style. - * @private - */ - _reset: function() { - this._points.length = 0; - this._curPointIdx = 0; - this._setBrushStyles(); - this._setShadow(); - }, - - /** - * @private - * @param {Object} pointer Actual mouse position related to the canvas. - */ - _captureDrawingPath: function(pointer) { - var pointerPoint = new fabric.Point(pointer.x, pointer.y); - return this._addPoint(pointerPoint); - }, - - /** - * Draw a smooth path on the topCanvas using quadraticCurveTo - * @private - */ - _render: function() { - var ctx = this.canvas.contextTop, - p1 = this._points[this._curPointIdx], - p2 = this._points[this._curPointIdx + 1]; - - 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 - //then we should be drawing a dot. A path isn't drawn between two identical dots - //that's why we set them apart a bit - if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { - var width = this.width / 1000; - p1 = new fabric.Point(p1.x, p1.y); - p2 = new fabric.Point(p2.x, p2.y); - p1.x -= width; - p2.x += width; - } - ctx.moveTo(p1.x, p1.y); - var midPoint = p1.midPointFrom(p2); - ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); - - ctx.lineTo(p2.x, p2.y); - ctx.stroke(); - ctx.restore(); - this._curPointIdx++; - }, - - /** - * Converts points to SVG path - * @param {Array} points Array of points - * @return {String} SVG path - */ - convertPointsToSVGPath: function(points) { - var path = [], i, width = this.width / 1000, - p1 = new fabric.Point(points[0].x, points[0].y), - p2 = new fabric.Point(points[1].x, points[1].y), - len = points.length, multSignX = 1, multSignY = 1, manyPoints = len > 2; - - if (manyPoints) { - multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1; - multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1; - } - path.push('M ', p1.x - multSignX * width, ' ', p1.y - multSignY * width, ' '); - for (i = 1; i < len; i++) { - if (!p1.eq(p2)) { - var midPoint = p1.midPointFrom(p2); - // p1 is our bezier control point - // midpoint is our endpoint - // start point is p(i-1) value. - path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' '); - } - p1 = points[i]; - if ((i + 1) < points.length) { - p2 = points[i + 1]; - } - } - if (manyPoints) { - multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1; - multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1; - } - path.push('L ', p1.x + multSignX * width, ' ', p1.y + multSignY * width); - return path; - }, - - /** - * Creates fabric.Path object to add on canvas - * @param {String} pathData Path data - * @return {fabric.Path} Path to add on canvas - */ - createPath: function(pathData) { - var path = new fabric.Path(pathData, { - fill: null, - stroke: this.color, - strokeWidth: this.width, - strokeLineCap: this.strokeLineCap, - strokeMiterLimit: this.strokeMiterLimit, - strokeLineJoin: this.strokeLineJoin, - strokeDashArray: this.strokeDashArray, - }); - var position = new fabric.Point(path.left + path.width / 2, path.top + path.height / 2); - position = path.translateToGivenOrigin(position, 'center', 'center', path.originX, path.originY); - path.top = position.y; - path.left = position.x; - if (this.shadow) { - this.shadow.affectStroke = true; - path.setShadow(this.shadow); - } - - return path; - }, - - /** - * On mouseup after drawing the path on contextTop canvas - * 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; - ctx.closePath(); - - var pathData = this.convertPointsToSVGPath(this._points).join(''); - if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') { - // do not create 0 width/height paths, as they are - // rendered inconsistently across browsers - // Firefox 4, for example, renders a dot, - // whereas Chrome 10 renders nothing - this.canvas.requestRenderAll(); - return; - } - - var path = this.createPath(pathData); - this.canvas.clearContext(this.canvas.contextTop); - this.canvas.add(path); - this.canvas.renderAll(); - path.setCoords(); - this._resetShadow(); - - - // fire event 'path' created - this.canvas.fire('path:created', { path: path }); - } - }); -})(); From 0fa3d39d429ae5be32bc5aaae285d0de867d4758 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Mon, 19 Feb 2018 16:53:54 +0100 Subject: [PATCH 3/5] add changes --- src/brushes/pencil_brush.class.js | 56 ++++++++++++------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 95734c075d3..b3f99f41a2a 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -21,36 +21,9 @@ * Invoked inside on mouse down and mouse move * @param {Object} pointer */ - drawDot: function (pointer) { - var point = new fabric.Point(pointer.x, pointer.y); - if (!this._addPoint(point)) { - return; - } - var ctx = this.canvas.contextTop, - p1 = this._points[this._curPointIdx], - p2 = this._points[this._curPointIdx + 1]; - - 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 - //then we should be drawing a dot. A path isn't drawn between two identical dots - //that's why we set them apart a bit - if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) { - var width = this.width / 1000; - p1 = new fabric.Point(p1.x, p1.y); - p2 = new fabric.Point(p2.x, p2.y); - p1.x -= width; - p2.x += width; - } - ctx.moveTo(p1.x, p1.y); + _drawSegment: function (ctx, p1, p2) { var midPoint = p1.midPointFrom(p2); ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); - - ctx.lineTo(p2.x, p2.y); - ctx.stroke(); - ctx.restore(); - this._curPointIdx++; }, /** @@ -61,7 +34,8 @@ this._prepareForDrawing(pointer); // capture coordinates immediately // this allows to draw dots (when movement never occurs) - this.drawDot(pointer); + this._captureDrawingPath(pointer); + this._render(); }, /** @@ -69,7 +43,14 @@ * @param {Object} pointer */ onMouseMove: function(pointer) { - this.drawDot(pointer); + if (this._captureDrawingPath(pointer) && this._points.length > 2) { + var length = this._points.length, ctx = this.canvas.contextTop; + // draw the curve update + this._saveAndTransform(ctx); + this._drawSegment(ctx, this._points[length - 3], this._points[length - 2]); + ctx.stroke(); + ctx.restore(); + } }, /** @@ -89,7 +70,6 @@ this._reset(); this._addPoint(p); - this.canvas.contextTop.moveTo(p.x, p.y); }, @@ -111,11 +91,19 @@ */ _reset: function() { this._points.length = 0; - this._curPointIdx = 0; this._setBrushStyles(); this._setShadow(); }, + /** + * @private + * @param {Object} pointer Actual mouse position related to the canvas. + */ + _captureDrawingPath: function(pointer) { + var pointerPoint = new fabric.Point(pointer.x, pointer.y); + return this._addPoint(pointerPoint); + }, + /** * Draw a smooth path on the topCanvas using quadraticCurveTo * @private @@ -143,9 +131,7 @@ for (i = 1, len = this._points.length; i < len; i++) { // we pick the point between pi + 1 & pi + 2 as the // end point and p1 as our control point. - var midPoint = p1.midPointFrom(p2); - ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); - + this._drawSegment(ctx, p1, p2); p1 = this._points[i]; p2 = this._points[i + 1]; } From 7c120aaa465ea0cf4be49dda685b09526d3b98f8 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Wed, 21 Feb 2018 10:38:02 +0100 Subject: [PATCH 4/5] Update pencil_brush.class.js --- src/brushes/pencil_brush.class.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index b3f99f41a2a..68948f358e8 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -21,8 +21,9 @@ * Invoked inside on mouse down and mouse move * @param {Object} pointer */ - _drawSegment: function (ctx, p1, p2) { + _drawSegment: function (ctx, p1, p2, withBegin) { var midPoint = p1.midPointFrom(p2); + withBegin && ctx.begiPath(); ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); }, @@ -47,7 +48,7 @@ var length = this._points.length, ctx = this.canvas.contextTop; // draw the curve update this._saveAndTransform(ctx); - this._drawSegment(ctx, this._points[length - 3], this._points[length - 2]); + this._drawSegment(ctx, this._points[length - 3], this._points[length - 2], true); ctx.stroke(); ctx.restore(); } From e8d76b594d621a460db2e225eefb7a08615e355b Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Wed, 21 Feb 2018 10:56:39 +0100 Subject: [PATCH 5/5] Update pencil_brush.class.js --- src/brushes/pencil_brush.class.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/brushes/pencil_brush.class.js b/src/brushes/pencil_brush.class.js index 68948f358e8..86f7421d139 100644 --- a/src/brushes/pencil_brush.class.js +++ b/src/brushes/pencil_brush.class.js @@ -21,10 +21,10 @@ * Invoked inside on mouse down and mouse move * @param {Object} pointer */ - _drawSegment: function (ctx, p1, p2, withBegin) { + _drawSegment: function (ctx, p1, p2) { var midPoint = p1.midPointFrom(p2); - withBegin && ctx.begiPath(); ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); + return midPoint; }, /** @@ -44,11 +44,15 @@ * @param {Object} pointer */ onMouseMove: function(pointer) { - if (this._captureDrawingPath(pointer) && this._points.length > 2) { - var length = this._points.length, ctx = this.canvas.contextTop; + if (this._captureDrawingPath(pointer) && this._points.length > 1) { + var points = this._points, length = points.length, ctx = this.canvas.contextTop; // draw the curve update this._saveAndTransform(ctx); - this._drawSegment(ctx, this._points[length - 3], this._points[length - 2], true); + if (this.oldEnd) { + ctx.beginPath(); + ctx.moveTo(this.oldEnd.x, this.oldEnd.y); + } + this.oldEnd = this._drawSegment(ctx, points[length - 2], points[length - 1], true); ctx.stroke(); ctx.restore(); } @@ -58,6 +62,7 @@ * Invoked on mouse up */ onMouseUp: function() { + this.oldEnd = undefined; this._finalizeAndAddPath(); },