fabric 4.5.1-browser → 5.0.0-browser

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/CONTRIBUTING.md +14 -0
  3. package/HEADER.js +1 -1
  4. package/README.md +3 -6
  5. package/SECURITY.md +5 -0
  6. package/build.js +1 -0
  7. package/dist/fabric.js +1160 -668
  8. package/dist/fabric.min.js +1 -1
  9. package/lib/event.js +7 -3
  10. package/package.json +9 -3
  11. package/publish-next.js +15 -0
  12. package/publish.js +3 -0
  13. package/src/brushes/base_brush.class.js +3 -5
  14. package/src/brushes/pattern_brush.class.js +7 -5
  15. package/src/brushes/pencil_brush.class.js +49 -37
  16. package/src/canvas.class.js +27 -57
  17. package/src/filters/saturate_filter.class.js +9 -1
  18. package/src/filters/vibrance_filter.class.js +122 -0
  19. package/src/mixins/animation.mixin.js +20 -25
  20. package/src/mixins/canvas_events.mixin.js +30 -59
  21. package/src/mixins/canvas_grouping.mixin.js +4 -4
  22. package/src/mixins/collection.mixin.js +11 -2
  23. package/src/mixins/eraser_brush.mixin.js +495 -452
  24. package/src/mixins/itext_behavior.mixin.js +7 -1
  25. package/src/mixins/itext_key_behavior.mixin.js +7 -1
  26. package/src/mixins/object_geometry.mixin.js +5 -35
  27. package/src/mixins/object_interactivity.mixin.js +18 -7
  28. package/src/mixins/object_straightening.mixin.js +4 -9
  29. package/src/mixins/observable.mixin.js +22 -0
  30. package/src/parser.js +13 -9
  31. package/src/shapes/circle.class.js +22 -19
  32. package/src/shapes/ellipse.class.js +2 -2
  33. package/src/shapes/group.class.js +24 -32
  34. package/src/shapes/image.class.js +3 -25
  35. package/src/shapes/itext.class.js +10 -0
  36. package/src/shapes/line.class.js +5 -21
  37. package/src/shapes/object.class.js +67 -32
  38. package/src/shapes/path.class.js +17 -18
  39. package/src/shapes/polygon.class.js +11 -10
  40. package/src/shapes/polyline.class.js +33 -24
  41. package/src/shapes/rect.class.js +0 -18
  42. package/src/shapes/text.class.js +120 -58
  43. package/src/shapes/triangle.class.js +0 -15
  44. package/src/static_canvas.class.js +43 -20
  45. package/src/util/animate.js +149 -16
  46. package/src/util/animate_color.js +2 -1
  47. package/src/util/lang_object.js +5 -1
  48. package/src/util/misc.js +193 -42
  49. package/src/util/path.js +89 -52
@@ -8,6 +8,7 @@
8
8
  * @memberOf fabric.util.object
9
9
  * @param {Object} destination Where to copy to
10
10
  * @param {Object} source Where to copy from
11
+ * @param {Boolean} [deep] Whether to extend nested objects
11
12
  * @return {Object}
12
13
  */
13
14
 
@@ -53,11 +54,14 @@
53
54
 
54
55
  /**
55
56
  * Creates an empty object and copies all enumerable properties of another object to it
57
+ * This method is mostly for internal use, and not intended for duplicating shapes in canvas.
56
58
  * @memberOf fabric.util.object
57
- * TODO: this function return an empty object if you try to clone null
58
59
  * @param {Object} object Object to clone
60
+ * @param {Boolean} [deep] Whether to clone nested objects
59
61
  * @return {Object}
60
62
  */
63
+
64
+ //TODO: this function return an empty object if you try to clone null
61
65
  function clone(object, deep) {
62
66
  return extend({ }, object, deep);
63
67
  }
package/src/util/misc.js CHANGED
@@ -139,6 +139,135 @@
139
139
  };
140
140
  },
141
141
 
142
+ /**
143
+ * Creates a vetor from points represented as a point
144
+ * @static
145
+ * @memberOf fabric.util
146
+ *
147
+ * @typedef {Object} Point
148
+ * @property {number} x
149
+ * @property {number} y
150
+ *
151
+ * @param {Point} from
152
+ * @param {Point} to
153
+ * @returns {Point} vector
154
+ */
155
+ createVector: function (from, to) {
156
+ return new fabric.Point(to.x - from.x, to.y - from.y);
157
+ },
158
+
159
+ /**
160
+ * Calculates angle between 2 vectors using dot product
161
+ * @static
162
+ * @memberOf fabric.util
163
+ * @param {Point} a
164
+ * @param {Point} b
165
+ * @returns the angle in radian between the vectors
166
+ */
167
+ calcAngleBetweenVectors: function (a, b) {
168
+ return Math.acos((a.x * b.x + a.y * b.y) / (Math.hypot(a.x, a.y) * Math.hypot(b.x, b.y)));
169
+ },
170
+
171
+ /**
172
+ * @static
173
+ * @memberOf fabric.util
174
+ * @param {Point} v
175
+ * @returns {Point} vector representing the unit vector of pointing to the direction of `v`
176
+ */
177
+ getHatVector: function (v) {
178
+ return new fabric.Point(v.x, v.y).multiply(1 / Math.hypot(v.x, v.y));
179
+ },
180
+
181
+ /**
182
+ * @static
183
+ * @memberOf fabric.util
184
+ * @param {Point} A
185
+ * @param {Point} B
186
+ * @param {Point} C
187
+ * @returns {{ vector: Point, angle: number }} vector representing the bisector of A and A's angle
188
+ */
189
+ getBisector: function (A, B, C) {
190
+ var AB = fabric.util.createVector(A, B), AC = fabric.util.createVector(A, C);
191
+ var alpha = fabric.util.calcAngleBetweenVectors(AB, AC);
192
+ // check if alpha is relative to AB->BC
193
+ var ro = fabric.util.calcAngleBetweenVectors(fabric.util.rotateVector(AB, alpha), AC);
194
+ var phi = alpha * (ro === 0 ? 1 : -1) / 2;
195
+ return {
196
+ vector: fabric.util.getHatVector(fabric.util.rotateVector(AB, phi)),
197
+ angle: alpha
198
+ };
199
+ },
200
+
201
+ /**
202
+ * Project stroke width on points returning 2 projections for each point as follows:
203
+ * - `miter`: 2 points corresponding to the outer boundary and the inner boundary of stroke.
204
+ * - `bevel`: 2 points corresponding to the bevel boundaries, tangent to the bisector.
205
+ * - `round`: same as `bevel`
206
+ * Used to calculate object's bounding box
207
+ * @static
208
+ * @memberOf fabric.util
209
+ * @param {Point[]} points
210
+ * @param {Object} options
211
+ * @param {number} options.strokeWidth
212
+ * @param {'miter'|'bevel'|'round'} options.strokeLineJoin
213
+ * @param {number} options.strokeMiterLimit https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit
214
+ * @param {boolean} options.strokeUniform
215
+ * @param {number} options.scaleX
216
+ * @param {number} options.scaleY
217
+ * @param {boolean} [openPath] whether the shape is open or not, affects the calculations of the first and last points
218
+ * @returns {fabric.Point[]} array of size 2n/4n of all suspected points
219
+ */
220
+ projectStrokeOnPoints: function (points, options, openPath) {
221
+ var coords = [], s = options.strokeWidth / 2,
222
+ strokeUniformScalar = options.strokeUniform ?
223
+ new fabric.Point(1 / options.scaleX, 1 / options.scaleY) : new fabric.Point(1, 1),
224
+ getStrokeHatVector = function (v) {
225
+ var scalar = s / (Math.hypot(v.x, v.y));
226
+ return new fabric.Point(v.x * scalar * strokeUniformScalar.x, v.y * scalar * strokeUniformScalar.y);
227
+ };
228
+ if (points.length <= 1) {return coords;}
229
+ points.forEach(function (p, index) {
230
+ var A = new fabric.Point(p.x, p.y), B, C;
231
+ if (index === 0) {
232
+ C = points[index + 1];
233
+ B = openPath ? getStrokeHatVector(fabric.util.createVector(C, A)).addEquals(A) : points[points.length - 1];
234
+ }
235
+ else if (index === points.length - 1) {
236
+ B = points[index - 1];
237
+ C = openPath ? getStrokeHatVector(fabric.util.createVector(B, A)).addEquals(A) : points[0];
238
+ }
239
+ else {
240
+ B = points[index - 1];
241
+ C = points[index + 1];
242
+ }
243
+ var bisector = fabric.util.getBisector(A, B, C),
244
+ bisectorVector = bisector.vector,
245
+ alpha = bisector.angle,
246
+ scalar,
247
+ miterVector;
248
+ if (options.strokeLineJoin === 'miter') {
249
+ scalar = -s / Math.sin(alpha / 2);
250
+ miterVector = new fabric.Point(
251
+ bisectorVector.x * scalar * strokeUniformScalar.x,
252
+ bisectorVector.y * scalar * strokeUniformScalar.y
253
+ );
254
+ if (Math.hypot(miterVector.x, miterVector.y) / s <= options.strokeMiterLimit) {
255
+ coords.push(A.add(miterVector));
256
+ coords.push(A.subtract(miterVector));
257
+ return;
258
+ }
259
+ }
260
+ scalar = -s * Math.SQRT2;
261
+ miterVector = new fabric.Point(
262
+ bisectorVector.x * scalar * strokeUniformScalar.x,
263
+ bisectorVector.y * scalar * strokeUniformScalar.y
264
+ );
265
+ coords.push(A.add(miterVector));
266
+ coords.push(A.subtract(miterVector));
267
+ });
268
+ return coords;
269
+ },
270
+
142
271
  /**
143
272
  * Apply transform t to point p
144
273
  * @static
@@ -451,6 +580,25 @@
451
580
  });
452
581
  },
453
582
 
583
+ /**
584
+ * Creates corresponding fabric instances residing in an object, e.g. `clipPath`
585
+ * @see {@link fabric.Object.ENLIVEN_PROPS}
586
+ * @param {Object} object
587
+ * @param {Object} [context] assign enlived props to this object (pass null to skip this)
588
+ * @param {(objects:fabric.Object[]) => void} callback
589
+ */
590
+ enlivenObjectEnlivables: function (object, context, callback) {
591
+ var enlivenProps = fabric.Object.ENLIVEN_PROPS.filter(function (key) { return !!object[key]; });
592
+ fabric.util.enlivenObjects(enlivenProps.map(function (key) { return object[key]; }), function (enlivedProps) {
593
+ var objects = {};
594
+ enlivenProps.forEach(function (key, index) {
595
+ objects[key] = enlivedProps[index];
596
+ context && (context[key] = enlivedProps[index]);
597
+ });
598
+ callback && callback(objects);
599
+ });
600
+ },
601
+
454
602
  /**
455
603
  * Create and wait for loading of patterns
456
604
  * @static
@@ -542,46 +690,6 @@
542
690
  }
543
691
  },
544
692
 
545
- /**
546
- * Draws a dashed line between two points
547
- *
548
- * This method is used to draw dashed line around selection area.
549
- * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
550
- *
551
- * @param {CanvasRenderingContext2D} ctx context
552
- * @param {Number} x start x coordinate
553
- * @param {Number} y start y coordinate
554
- * @param {Number} x2 end x coordinate
555
- * @param {Number} y2 end y coordinate
556
- * @param {Array} da dash array pattern
557
- */
558
- drawDashedLine: function(ctx, x, y, x2, y2, da) {
559
- var dx = x2 - x,
560
- dy = y2 - y,
561
- len = sqrt(dx * dx + dy * dy),
562
- rot = atan2(dy, dx),
563
- dc = da.length,
564
- di = 0,
565
- draw = true;
566
-
567
- ctx.save();
568
- ctx.translate(x, y);
569
- ctx.moveTo(0, 0);
570
- ctx.rotate(rot);
571
-
572
- x = 0;
573
- while (len > x) {
574
- x += da[di++ % dc];
575
- if (x > len) {
576
- x = len;
577
- }
578
- ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
579
- draw = !draw;
580
- }
581
-
582
- ctx.restore();
583
- },
584
-
585
693
  /**
586
694
  * Creates canvas element
587
695
  * @static
@@ -709,7 +817,7 @@
709
817
  * @param {Boolean} [options.flipX]
710
818
  * @param {Boolean} [options.flipY]
711
819
  * @param {Number} [options.skewX]
712
- * @param {Number} [options.skewX]
820
+ * @param {Number} [options.skewY]
713
821
  * @return {Number[]} transform matrix
714
822
  */
715
823
  calcDimensionsMatrix: function(options) {
@@ -1063,6 +1171,49 @@
1063
1171
  x: bbox.width,
1064
1172
  y: bbox.height,
1065
1173
  };
1066
- }
1174
+ },
1175
+
1176
+ /**
1177
+ * Merges 2 clip paths into one visually equal clip path
1178
+ *
1179
+ * **IMPORTANT**:\
1180
+ * Does **NOT** clone the arguments, clone them proir if necessary.
1181
+ *
1182
+ * Creates a wrapper (group) that contains one clip path and is clipped by the other so content is kept where both overlap.
1183
+ * Use this method if both the clip paths may have nested clip paths of their own, so assigning one to the other's clip path property is not possible.
1184
+ *
1185
+ * In order to handle the `inverted` property we follow logic described in the following cases:\
1186
+ * **(1)** both clip paths are inverted - the clip paths pass the inverted prop to the wrapper and loose it themselves.\
1187
+ * **(2)** one is inverted and the other isn't - the wrapper shouldn't become inverted and the inverted clip path must clip the non inverted one to produce an identical visual effect.\
1188
+ * **(3)** both clip paths are not inverted - wrapper and clip paths remain unchanged.
1189
+ *
1190
+ * @memberOf fabric.util
1191
+ * @param {fabric.Object} c1
1192
+ * @param {fabric.Object} c2
1193
+ * @returns {fabric.Object} merged clip path
1194
+ */
1195
+ mergeClipPaths: function (c1, c2) {
1196
+ var a = c1, b = c2;
1197
+ if (a.inverted && !b.inverted) {
1198
+ // case (2)
1199
+ a = c2;
1200
+ b = c1;
1201
+ }
1202
+ // `b` becomes `a`'s clip path so we transform `b` to `a` coordinate plane
1203
+ fabric.util.applyTransformToObject(
1204
+ b,
1205
+ fabric.util.multiplyTransformMatrices(
1206
+ fabric.util.invertTransform(a.calcTransformMatrix()),
1207
+ b.calcTransformMatrix()
1208
+ )
1209
+ );
1210
+ // assign the `inverted` prop to the wrapping group
1211
+ var inverted = a.inverted && b.inverted;
1212
+ if (inverted) {
1213
+ // case (1)
1214
+ a.inverted = b.inverted = false;
1215
+ }
1216
+ return new fabric.Group([a], { clipPath: b, inverted: inverted });
1217
+ },
1067
1218
  };
1068
1219
  })(typeof exports !== 'undefined' ? exports : this);
package/src/util/path.js CHANGED
@@ -486,8 +486,8 @@
486
486
  // with 100 segemnts. This will good enough to calculate the length of the curve
487
487
  function pathIterator(iterator, x1, y1) {
488
488
  var tempP = { x: x1, y: y1 }, p, tmpLen = 0, perc;
489
- for (perc = 0.01; perc <= 1; perc += 0.01) {
490
- p = iterator(perc);
489
+ for (perc = 1; perc <= 100; perc += 1) {
490
+ p = iterator(perc / 100);
491
491
  tmpLen += calcLineLength(tempP.x, tempP.y, p.x, p.y);
492
492
  tempP = p;
493
493
  }
@@ -507,15 +507,15 @@
507
507
  p, nextLen, nextStep = 0.01, angleFinder = segInfo.angleFinder, lastPerc;
508
508
  // nextStep > 0.0001 covers 0.00015625 that 1/64th of 1/100
509
509
  // the path
510
- while (tmpLen < distance && perc <= 1 && nextStep > 0.0001) {
510
+ while (tmpLen < distance && nextStep > 0.0001) {
511
511
  p = iterator(perc);
512
512
  lastPerc = perc;
513
513
  nextLen = calcLineLength(tempP.x, tempP.y, p.x, p.y);
514
514
  // compare tmpLen each cycle with distance, decide next perc to test.
515
515
  if ((nextLen + tmpLen) > distance) {
516
516
  // we discard this step and we make smaller steps.
517
- nextStep /= 2;
518
517
  perc -= nextStep;
518
+ nextStep /= 2;
519
519
  }
520
520
  else {
521
521
  tempP = p;
@@ -661,6 +661,18 @@
661
661
  }
662
662
  }
663
663
 
664
+ /**
665
+ *
666
+ * @param {string} pathString
667
+ * @return {(string|number)[][]} An array of SVG path commands
668
+ * @example <caption>Usage</caption>
669
+ * parsePath('M 3 4 Q 3 5 2 1 4 0 Q 9 12 2 1 4 0') === [
670
+ * ['M', 3, 4],
671
+ * ['Q', 3, 5, 2, 1, 4, 0],
672
+ * ['Q', 9, 12, 2, 1, 4, 0],
673
+ * ];
674
+ *
675
+ */
664
676
  function parsePath(pathString) {
665
677
  var result = [],
666
678
  coords = [],
@@ -730,63 +742,88 @@
730
742
  };
731
743
 
732
744
  /**
733
- * Calculate bounding box of a elliptic-arc
734
- * @deprecated
735
- * @param {Number} fx start point of arc
736
- * @param {Number} fy
737
- * @param {Number} rx horizontal radius
738
- * @param {Number} ry vertical radius
739
- * @param {Number} rot angle of horizontal axis
740
- * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
741
- * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
742
- * @param {Number} tx end point of arc
743
- * @param {Number} ty
745
+ *
746
+ * Converts points to a smooth SVG path
747
+ * @param {{ x: number,y: number }[]} points Array of points
748
+ * @param {number} [correction] Apply a correction to the path (usually we use `width / 1000`). If value is undefined 0 is used as the correction value.
749
+ * @return {(string|number)[][]} An array of SVG path commands
744
750
  */
745
- function getBoundsOfArc(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
746
-
747
- var fromX = 0, fromY = 0, bound, bounds = [],
748
- segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
749
-
750
- for (var i = 0, len = segs.length; i < len; i++) {
751
- bound = getBoundsOfCurve(fromX, fromY, segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5], segs[i][6]);
752
- bounds.push({ x: bound[0].x + fx, y: bound[0].y + fy });
753
- bounds.push({ x: bound[1].x + fx, y: bound[1].y + fy });
754
- fromX = segs[i][5];
755
- fromY = segs[i][6];
751
+ function getSmoothPathFromPoints(points, correction) {
752
+ var path = [], i,
753
+ p1 = new fabric.Point(points[0].x, points[0].y),
754
+ p2 = new fabric.Point(points[1].x, points[1].y),
755
+ len = points.length, multSignX = 1, multSignY = 0, manyPoints = len > 2;
756
+ correction = correction || 0;
757
+
758
+ if (manyPoints) {
759
+ multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1;
760
+ multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1;
756
761
  }
757
- return bounds;
758
- };
759
-
762
+ path.push(['M', p1.x - multSignX * correction, p1.y - multSignY * correction]);
763
+ for (i = 1; i < len; i++) {
764
+ if (!p1.eq(p2)) {
765
+ var midPoint = p1.midPointFrom(p2);
766
+ // p1 is our bezier control point
767
+ // midpoint is our endpoint
768
+ // start point is p(i-1) value.
769
+ path.push(['Q', p1.x, p1.y, midPoint.x, midPoint.y]);
770
+ }
771
+ p1 = points[i];
772
+ if ((i + 1) < points.length) {
773
+ p2 = points[i + 1];
774
+ }
775
+ }
776
+ if (manyPoints) {
777
+ multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1;
778
+ multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1;
779
+ }
780
+ path.push(['L', p1.x + multSignX * correction, p1.y + multSignY * correction]);
781
+ return path;
782
+ }
760
783
  /**
761
- * Draws arc
762
- * @deprecated
763
- * @param {CanvasRenderingContext2D} ctx
764
- * @param {Number} fx
765
- * @param {Number} fy
766
- * @param {Array} coords coords of the arc, without the front 'A/a'
784
+ * Transform a path by transforming each segment.
785
+ * it has to be a simplified path or it won't work.
786
+ * WARNING: this depends from pathOffset for correct operation
787
+ * @param {Array} path fabricJS parsed and simplified path commands
788
+ * @param {Array} transform matrix that represent the transformation
789
+ * @param {Object} [pathOffset] the fabric.Path pathOffset
790
+ * @param {Number} pathOffset.x
791
+ * @param {Number} pathOffset.y
792
+ * @returns {Array} the transformed path
767
793
  */
768
- function drawArc(ctx, fx, fy, coords) {
769
- coords = coords.slice(0).unshift('X'); // command A or a does not matter
770
- var beziers = fromArcToBeziers(fx, fy, coords);
771
- beziers.forEach(function(bezier) {
772
- ctx.bezierCurveTo.apply(ctx, bezier.slice(1));
794
+ function transformPath(path, transform, pathOffset) {
795
+ if (pathOffset) {
796
+ transform = fabric.util.multiplyTransformMatrices(
797
+ transform,
798
+ [1, 0, 0, 1, -pathOffset.x, -pathOffset.y]
799
+ );
800
+ }
801
+ return path.map(function(pathSegment) {
802
+ var newSegment = pathSegment.slice(0), point = {};
803
+ for (var i = 1; i < pathSegment.length - 1; i += 2) {
804
+ point.x = pathSegment[i];
805
+ point.y = pathSegment[i + 1];
806
+ point = fabric.util.transformPoint(point, transform);
807
+ newSegment[i] = point.x;
808
+ newSegment[i + 1] = point.y;
809
+ }
810
+ return newSegment;
773
811
  });
774
- };
812
+ }
775
813
 
814
+ /**
815
+ * Join path commands to go back to svg format
816
+ * @param {Array} pathData fabricJS parsed path commands
817
+ * @return {String} joined path 'M 0 0 L 20 30'
818
+ */
819
+ fabric.util.joinPath = function(pathData) {
820
+ return pathData.map(function (segment) { return segment.join(' '); }).join(' ');
821
+ };
776
822
  fabric.util.parsePath = parsePath;
777
823
  fabric.util.makePathSimpler = makePathSimpler;
824
+ fabric.util.getSmoothPathFromPoints = getSmoothPathFromPoints;
778
825
  fabric.util.getPathSegmentsInfo = getPathSegmentsInfo;
779
- fabric.util.fromArcToBeziers = fromArcToBeziers;
780
- /**
781
- * Typo of `fromArcToBeziers` kept for not breaking the api once corrected.
782
- * Will be removed in fabric 5.0
783
- * @deprecated
784
- */
785
- fabric.util.fromArcToBeizers = fromArcToBeziers;
786
826
  fabric.util.getBoundsOfCurve = getBoundsOfCurve;
787
827
  fabric.util.getPointOnPath = getPointOnPath;
788
- // kept because we do not want to make breaking changes.
789
- // but useless and deprecated.
790
- fabric.util.getBoundsOfArc = getBoundsOfArc;
791
- fabric.util.drawArc = drawArc;
828
+ fabric.util.transformPath = transformPath;
792
829
  })();