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,7 +8,6 @@
8
8
  toFixed = fabric.util.toFixed,
9
9
  capitalize = fabric.util.string.capitalize,
10
10
  degreesToRadians = fabric.util.degreesToRadians,
11
- supportsLineDash = fabric.StaticCanvas.supports('setLineDash'),
12
11
  objectCaching = !fabric.isLikelyNode,
13
12
  ALIASING_LIMIT = 2;
14
13
 
@@ -537,6 +536,7 @@
537
536
  /**
538
537
  * When `false`, the stoke width will scale with the object.
539
538
  * When `true`, the stroke will always match the exact pixel size entered for stroke width.
539
+ * this Property does not work on Text classes or drawing call that uses strokeText,fillText methods
540
540
  * default to false
541
541
  * @since 2.6.0
542
542
  * @type Boolean
@@ -779,6 +779,12 @@
779
779
  additionalHeight = height * 0.1;
780
780
  }
781
781
  }
782
+ if (this instanceof fabric.Text && this.path) {
783
+ shouldRedraw = true;
784
+ shouldResizeCanvas = true;
785
+ additionalWidth += this.getHeightOfLine(0) * this.zoomX;
786
+ additionalHeight += this.getHeightOfLine(0) * this.zoomY;
787
+ }
782
788
  if (shouldRedraw) {
783
789
  if (shouldResizeCanvas) {
784
790
  canvas.width = Math.ceil(width + additionalWidth);
@@ -868,7 +874,7 @@
868
874
  skewY: toFixed(this.skewY, NUM_FRACTION_DIGITS),
869
875
  };
870
876
 
871
- if (this.clipPath) {
877
+ if (this.clipPath && !this.clipPath.excludeFromExport) {
872
878
  object.clipPath = this.clipPath.toObject(propertiesToInclude);
873
879
  object.clipPath.inverted = this.clipPath.inverted;
874
880
  object.clipPath.absolutePositioned = this.clipPath.absolutePositioned;
@@ -931,6 +937,17 @@
931
937
  * @return {Object} object with scaleX and scaleY properties
932
938
  */
933
939
  getObjectScaling: function() {
940
+ // if the object is a top level one, on the canvas, we go for simple aritmetic
941
+ // otherwise the complex method with angles will return approximations and decimals
942
+ // and will likely kill the cache when not needed
943
+ // https://github.com/fabricjs/fabric.js/issues/7157
944
+ if (!this.group) {
945
+ return {
946
+ scaleX: this.scaleX,
947
+ scaleY: this.scaleY,
948
+ };
949
+ }
950
+ // if we are inside a group total zoom calculation is complex, we defer to generic matrices
934
951
  var options = fabric.util.qrDecompose(this.calcTransformMatrix());
935
952
  return { scaleX: Math.abs(options.scaleX), scaleY: Math.abs(options.scaleY) };
936
953
  },
@@ -1170,26 +1187,26 @@
1170
1187
  /**
1171
1188
  * Execute the drawing operation for an object clipPath
1172
1189
  * @param {CanvasRenderingContext2D} ctx Context to render on
1190
+ * @param {fabric.Object} clipPath
1173
1191
  */
1174
- drawClipPathOnCache: function(ctx) {
1175
- var path = this.clipPath;
1192
+ drawClipPathOnCache: function(ctx, clipPath) {
1176
1193
  ctx.save();
1177
1194
  // DEBUG: uncomment this line, comment the following
1178
1195
  // ctx.globalAlpha = 0.4
1179
- if (path.inverted) {
1196
+ if (clipPath.inverted) {
1180
1197
  ctx.globalCompositeOperation = 'destination-out';
1181
1198
  }
1182
1199
  else {
1183
1200
  ctx.globalCompositeOperation = 'destination-in';
1184
1201
  }
1185
1202
  //ctx.scale(1 / 2, 1 / 2);
1186
- if (path.absolutePositioned) {
1203
+ if (clipPath.absolutePositioned) {
1187
1204
  var m = fabric.util.invertTransform(this.calcTransformMatrix());
1188
1205
  ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
1189
1206
  }
1190
- path.transform(ctx);
1191
- ctx.scale(1 / path.zoomX, 1 / path.zoomY);
1192
- ctx.drawImage(path._cacheCanvas, -path.cacheTranslationX, -path.cacheTranslationY);
1207
+ clipPath.transform(ctx);
1208
+ ctx.scale(1 / clipPath.zoomX, 1 / clipPath.zoomY);
1209
+ ctx.drawImage(clipPath._cacheCanvas, -clipPath.cacheTranslationX, -clipPath.cacheTranslationY);
1193
1210
  ctx.restore();
1194
1211
  },
1195
1212
 
@@ -1208,22 +1225,26 @@
1208
1225
  this._renderBackground(ctx);
1209
1226
  }
1210
1227
  this._render(ctx);
1211
- this._drawClipPath(ctx);
1228
+ this._drawClipPath(ctx, this.clipPath);
1212
1229
  this.fill = originalFill;
1213
1230
  this.stroke = originalStroke;
1214
1231
  },
1215
1232
 
1216
- _drawClipPath: function(ctx) {
1217
- var path = this.clipPath;
1218
- if (!path) { return; }
1233
+ /**
1234
+ * Prepare clipPath state and cache and draw it on instance's cache
1235
+ * @param {CanvasRenderingContext2D} ctx
1236
+ * @param {fabric.Object} clipPath
1237
+ */
1238
+ _drawClipPath: function (ctx, clipPath) {
1239
+ if (!clipPath) { return; }
1219
1240
  // needed to setup a couple of variables
1220
1241
  // path canvas gets overridden with this one.
1221
1242
  // TODO find a better solution?
1222
- path.canvas = this.canvas;
1223
- path.shouldCache();
1224
- path._transformDone = true;
1225
- path.renderCache({ forClipping: true });
1226
- this.drawClipPathOnCache(ctx);
1243
+ clipPath.canvas = this.canvas;
1244
+ clipPath.shouldCache();
1245
+ clipPath._transformDone = true;
1246
+ clipPath.renderCache({ forClipping: true });
1247
+ this.drawClipPathOnCache(ctx, clipPath);
1227
1248
  },
1228
1249
 
1229
1250
  /**
@@ -1353,9 +1374,8 @@
1353
1374
  * Sets line dash
1354
1375
  * @param {CanvasRenderingContext2D} ctx Context to set the dash line on
1355
1376
  * @param {Array} dashArray array representing dashes
1356
- * @param {Function} alternative function to call if browser does not support lineDash
1357
1377
  */
1358
- _setLineDash: function(ctx, dashArray, alternative) {
1378
+ _setLineDash: function(ctx, dashArray) {
1359
1379
  if (!dashArray || dashArray.length === 0) {
1360
1380
  return;
1361
1381
  }
@@ -1363,16 +1383,12 @@
1363
1383
  if (1 & dashArray.length) {
1364
1384
  dashArray.push.apply(dashArray, dashArray);
1365
1385
  }
1366
- if (supportsLineDash) {
1367
- ctx.setLineDash(dashArray);
1368
- }
1369
- else {
1370
- alternative && alternative(ctx);
1371
- }
1386
+ ctx.setLineDash(dashArray);
1372
1387
  },
1373
1388
 
1374
1389
  /**
1375
1390
  * Renders controls and borders for the object
1391
+ * the context here is not transformed
1376
1392
  * @param {CanvasRenderingContext2D} ctx Context to render on
1377
1393
  * @param {Object} [styleOverride] properties to override the object style
1378
1394
  */
@@ -1391,12 +1407,14 @@
1391
1407
  if (!this.group) {
1392
1408
  ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
1393
1409
  }
1394
- if (styleOverride.forActiveSelection) {
1395
- ctx.rotate(degreesToRadians(options.angle));
1410
+ if (this.flipX) {
1411
+ options.angle -= 180;
1412
+ }
1413
+ ctx.rotate(degreesToRadians(this.group ? options.angle : this.angle));
1414
+ if (styleOverride.forActiveSelection || this.group) {
1396
1415
  drawBorders && this.drawBordersInGroup(ctx, options, styleOverride);
1397
1416
  }
1398
1417
  else {
1399
- ctx.rotate(degreesToRadians(this.angle));
1400
1418
  drawBorders && this.drawBorders(ctx, styleOverride);
1401
1419
  }
1402
1420
  drawControls && this.drawControls(ctx, styleOverride);
@@ -1539,7 +1557,7 @@
1539
1557
  else if (this.strokeUniform) {
1540
1558
  ctx.scale(1 / this.scaleX, 1 / this.scaleY);
1541
1559
  }
1542
- this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
1560
+ this._setLineDash(ctx, this.strokeDashArray);
1543
1561
  this._setStrokeStyles(ctx, this);
1544
1562
  ctx.stroke();
1545
1563
  ctx.restore();
@@ -1928,6 +1946,15 @@
1928
1946
  if (this.globalCompositeOperation) {
1929
1947
  ctx.globalCompositeOperation = this.globalCompositeOperation;
1930
1948
  }
1949
+ },
1950
+
1951
+ /**
1952
+ * cancel instance's running animations
1953
+ */
1954
+ dispose: function () {
1955
+ if (fabric.runningAnimations) {
1956
+ fabric.runningAnimations.cancelByTarget(this);
1957
+ }
1931
1958
  }
1932
1959
  });
1933
1960
 
@@ -1945,6 +1972,15 @@
1945
1972
  */
1946
1973
  fabric.Object.NUM_FRACTION_DIGITS = 2;
1947
1974
 
1975
+ /**
1976
+ * Defines which properties should be enlivened from the object passed to {@link fabric.Object._fromObject}
1977
+ * @static
1978
+ * @memberOf fabric.Object
1979
+ * @constant
1980
+ * @type string[]
1981
+ */
1982
+ fabric.Object.ENLIVEN_PROPS = ['clipPath'];
1983
+
1948
1984
  fabric.Object._fromObject = function(className, object, callback, extraParam) {
1949
1985
  var klass = fabric[className];
1950
1986
  object = clone(object, true);
@@ -1955,8 +1991,7 @@
1955
1991
  if (typeof patterns[1] !== 'undefined') {
1956
1992
  object.stroke = patterns[1];
1957
1993
  }
1958
- fabric.util.enlivenObjects([object.clipPath], function(enlivedProps) {
1959
- object.clipPath = enlivedProps[0];
1994
+ fabric.util.enlivenObjectEnlivables(object, object, function () {
1960
1995
  var instance = extraParam ? new klass(object[extraParam], object) : new klass(object);
1961
1996
  callback && callback(instance);
1962
1997
  });
@@ -6,6 +6,7 @@
6
6
  min = fabric.util.array.min,
7
7
  max = fabric.util.array.max,
8
8
  extend = fabric.util.object.extend,
9
+ clone = fabric.util.object.clone,
9
10
  _toString = Object.prototype.toString,
10
11
  toFixed = fabric.util.toFixed;
11
12
 
@@ -47,26 +48,26 @@
47
48
  * @param {Object} [options] Options object
48
49
  * @return {fabric.Path} thisArg
49
50
  */
50
- initialize: function(path, options) {
51
- options = options || { };
51
+ initialize: function (path, options) {
52
+ options = clone(options || {});
53
+ delete options.path;
52
54
  this.callSuper('initialize', options);
53
- if (!path) {
54
- path = [];
55
- }
55
+ this._setPath(path || [], options);
56
+ },
56
57
 
58
+ /**
59
+ * @private
60
+ * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
61
+ * @param {Object} [options] Options object
62
+ */
63
+ _setPath: function (path, options) {
57
64
  var fromArray = _toString.call(path) === '[object Array]';
58
65
 
59
- this.path = fromArray
60
- ? fabric.util.makePathSimpler(path)
61
-
62
- : fabric.util.makePathSimpler(
63
- fabric.util.parsePath(path)
64
- );
66
+ this.path = fabric.util.makePathSimpler(
67
+ fromArray ? path : fabric.util.parsePath(path)
68
+ );
65
69
 
66
- if (!this.path) {
67
- return;
68
- }
69
- fabric.Polyline.prototype._setPositionDimensions.call(this, options);
70
+ fabric.Polyline.prototype._setPositionDimensions.call(this, options || {});
70
71
  },
71
72
 
72
73
  /**
@@ -193,9 +194,7 @@
193
194
  * of the instance
194
195
  */
195
196
  _toSVG: function() {
196
- var path = this.path.map(function(path) {
197
- return path.join(' ');
198
- }).join(' ');
197
+ var path = fabric.util.joinPath(this.path);
199
198
  return [
200
199
  '<path ', 'COMMON_PARTS',
201
200
  'd="', path,
@@ -2,7 +2,8 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- var fabric = global.fabric || (global.fabric = { });
5
+ var fabric = global.fabric || (global.fabric = {}),
6
+ projectStrokeOnPoints = fabric.util.projectStrokeOnPoints;
6
7
 
7
8
  if (fabric.Polygon) {
8
9
  fabric.warn('fabric.Polygon is already defined');
@@ -24,6 +25,13 @@
24
25
  */
25
26
  type: 'polygon',
26
27
 
28
+ /**
29
+ * @private
30
+ */
31
+ _projectStrokeOnPoints: function () {
32
+ return projectStrokeOnPoints(this.points, this);
33
+ },
34
+
27
35
  /**
28
36
  * @private
29
37
  * @param {CanvasRenderingContext2D} ctx Context to render on
@@ -36,14 +44,6 @@
36
44
  this._renderPaintInOrder(ctx);
37
45
  },
38
46
 
39
- /**
40
- * @private
41
- * @param {CanvasRenderingContext2D} ctx Context to render on
42
- */
43
- _renderDashedStroke: function(ctx) {
44
- this.callSuper('_renderDashedStroke', ctx);
45
- ctx.closePath();
46
- },
47
47
  });
48
48
 
49
49
  /* _FROM_SVG_START_ */
@@ -72,9 +72,10 @@
72
72
  * @memberOf fabric.Polygon
73
73
  * @param {Object} object Object to create an instance from
74
74
  * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
75
+ * @return {void}
75
76
  */
76
77
  fabric.Polygon.fromObject = function(object, callback) {
77
- return fabric.Object._fromObject('Polygon', object, callback, 'points');
78
+ fabric.Object._fromObject('Polygon', object, callback, 'points');
78
79
  };
79
80
 
80
81
  })(typeof exports !== 'undefined' ? exports : this);
@@ -6,7 +6,8 @@
6
6
  extend = fabric.util.object.extend,
7
7
  min = fabric.util.array.min,
8
8
  max = fabric.util.array.max,
9
- toFixed = fabric.util.toFixed;
9
+ toFixed = fabric.util.toFixed,
10
+ projectStrokeOnPoints = fabric.util.projectStrokeOnPoints;
10
11
 
11
12
  if (fabric.Polyline) {
12
13
  fabric.warn('fabric.Polyline is already defined');
@@ -35,6 +36,17 @@
35
36
  */
36
37
  points: null,
37
38
 
39
+ /**
40
+ * WARNING: Feature in progress
41
+ * Calculate the exact bounding box taking in account strokeWidth on acute angles
42
+ * this will be turned to true by default on fabric 6.0
43
+ * maybe will be left in as an optimization since calculations may be slow
44
+ * @deprecated
45
+ * @type Boolean
46
+ * @default false
47
+ */
48
+ exactBoundingBox: false,
49
+
38
50
  cacheProperties: fabric.Object.prototype.cacheProperties.concat('points'),
39
51
 
40
52
  /**
@@ -63,13 +75,25 @@
63
75
  this._setPositionDimensions(options);
64
76
  },
65
77
 
78
+ /**
79
+ * @private
80
+ */
81
+ _projectStrokeOnPoints: function () {
82
+ return projectStrokeOnPoints(this.points, this, true);
83
+ },
84
+
66
85
  _setPositionDimensions: function(options) {
67
- var calcDim = this._calcDimensions(options), correctLeftTop;
68
- this.width = calcDim.width;
69
- this.height = calcDim.height;
86
+ var calcDim = this._calcDimensions(options), correctLeftTop,
87
+ correctSize = this.exactBoundingBox ? this.strokeWidth : 0;
88
+ this.width = calcDim.width - correctSize;
89
+ this.height = calcDim.height - correctSize;
70
90
  if (!options.fromSVG) {
71
91
  correctLeftTop = this.translateToGivenOrigin(
72
- { x: calcDim.left - this.strokeWidth / 2, y: calcDim.top - this.strokeWidth / 2 },
92
+ {
93
+ // this looks bad, but is one way to keep it optional for now.
94
+ x: calcDim.left - this.strokeWidth / 2 + correctSize / 2,
95
+ y: calcDim.top - this.strokeWidth / 2 + correctSize / 2
96
+ },
73
97
  'left',
74
98
  'top',
75
99
  this.originX,
@@ -83,8 +107,8 @@
83
107
  this.top = options.fromSVG ? calcDim.top : correctLeftTop.y;
84
108
  }
85
109
  this.pathOffset = {
86
- x: calcDim.left + this.width / 2,
87
- y: calcDim.top + this.height / 2
110
+ x: calcDim.left + this.width / 2 + correctSize / 2,
111
+ y: calcDim.top + this.height / 2 + correctSize / 2
88
112
  };
89
113
  },
90
114
 
@@ -100,7 +124,7 @@
100
124
  */
101
125
  _calcDimensions: function() {
102
126
 
103
- var points = this.points,
127
+ var points = this.exactBoundingBox ? this._projectStrokeOnPoints() : this.points,
104
128
  minX = min(points, 'x') || 0,
105
129
  minY = min(points, 'y') || 0,
106
130
  maxX = max(points, 'x') || 0,
@@ -112,7 +136,7 @@
112
136
  left: minX,
113
137
  top: minY,
114
138
  width: width,
115
- height: height
139
+ height: height,
116
140
  };
117
141
  },
118
142
 
@@ -186,21 +210,6 @@
186
210
  this._renderPaintInOrder(ctx);
187
211
  },
188
212
 
189
- /**
190
- * @private
191
- * @param {CanvasRenderingContext2D} ctx Context to render on
192
- */
193
- _renderDashedStroke: function(ctx) {
194
- var p1, p2;
195
-
196
- ctx.beginPath();
197
- for (var i = 0, len = this.points.length; i < len; i++) {
198
- p1 = this.points[i];
199
- p2 = this.points[i + 1] || p1;
200
- fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
201
- }
202
- },
203
-
204
213
  /**
205
214
  * Returns complexity of an instance
206
215
  * @return {Number} complexity of this instance
@@ -111,24 +111,6 @@
111
111
  this._renderPaintInOrder(ctx);
112
112
  },
113
113
 
114
- /**
115
- * @private
116
- * @param {CanvasRenderingContext2D} ctx Context to render on
117
- */
118
- _renderDashedStroke: function(ctx) {
119
- var x = -this.width / 2,
120
- y = -this.height / 2,
121
- w = this.width,
122
- h = this.height;
123
-
124
- ctx.beginPath();
125
- fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
126
- fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
127
- fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
128
- fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
129
- ctx.closePath();
130
- },
131
-
132
114
  /**
133
115
  * Returns object representation of an instance
134
116
  * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output