fabric 5.0.0-browser → 5.2.1-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.
package/lib/event.js CHANGED
@@ -237,7 +237,7 @@ var eventManager = function(target, type, listener, configure, trigger, fromOver
237
237
  }
238
238
  return createBatchCommands(events);
239
239
  } else if (type.indexOf("on") === 0) { // to support things like "onclick" instead of "click"
240
- type = type.substr(2);
240
+ type = type.slice(2);
241
241
  }
242
242
 
243
243
  // Ensure listener is a function.
@@ -1368,7 +1368,7 @@ root.gesture = function(conf) {
1368
1368
  var dx = touch.move.x - self.x;
1369
1369
  var dy = touch.move.y - self.y;
1370
1370
  var distance = Math.sqrt(dx * dx + dy * dy);
1371
- // If touch start.distance from centroid is 0, scale should not be updated.
1371
+ // If touch start.distance from centroid is 0, scale should not be updated.
1372
1372
  // This prevents dividing by 0 in cases where start.distance is oddly 0.
1373
1373
  if (start.distance !== 0) {
1374
1374
  scale += distance / start.distance;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "fabric",
3
3
  "description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
4
4
  "homepage": "http://fabricjs.com/",
5
- "version": "5.0.0-browser",
5
+ "version": "5.2.1-browser",
6
6
  "author": "Juriy Zaytsev <kangax@gmail.com>",
7
7
  "contributors": [
8
8
  {
@@ -55,7 +55,7 @@
55
55
  "test:coverage": "nyc --silent qunit test/node_test_setup.js test/lib test/unit",
56
56
  "test:visual:coverage": "nyc --silent --no-clean qunit test/node_test_setup.js test/lib test/visual",
57
57
  "coverage:report": "nyc report --reporter=lcov --reporter=text",
58
- "test": "qunit test/node_test_setup.js test/lib test/unit",
58
+ "test": "qunit --require ./test/node_test_setup.js test/lib test/unit",
59
59
  "test:visual": "qunit test/node_test_setup.js test/lib test/visual",
60
60
  "test:visual:single": "qunit test/node_test_setup.js test/lib",
61
61
  "test:all": "npm run test && npm run test:visual",
@@ -72,11 +72,12 @@
72
72
  "devDependencies": {
73
73
  "auto-changelog": "^2.3.0",
74
74
  "chalk": "^2.4.1",
75
+ "deep-object-diff": "^1.1.7",
75
76
  "eslint": "4.18.x",
76
77
  "nyc": "^15.1.0",
77
78
  "onchange": "^7.1.0",
78
79
  "pixelmatch": "^4.0.2",
79
- "qunit": "^2.13.0",
80
+ "qunit": "^2.17.2",
80
81
  "testem": "^3.2.0",
81
82
  "uglify-js": "3.3.x"
82
83
  },
@@ -507,7 +507,7 @@
507
507
  _isSelectionKeyPressed: function(e) {
508
508
  var selectionKeyPressed = false;
509
509
 
510
- if (Object.prototype.toString.call(this.selectionKey) === '[object Array]') {
510
+ if (Array.isArray(this.selectionKey)) {
511
511
  selectionKeyPressed = !!this.selectionKey.find(function(key) { return e[key] === true; });
512
512
  }
513
513
  else {
@@ -933,6 +933,14 @@
933
933
  this.contextTop = upperCanvasEl.getContext('2d');
934
934
  },
935
935
 
936
+ /**
937
+ * Returns context of top canvas where interactions are drawn
938
+ * @returns {CanvasRenderingContext2D}
939
+ */
940
+ getTopContext: function () {
941
+ return this.contextTop;
942
+ },
943
+
936
944
  /**
937
945
  * @private
938
946
  */
@@ -32,17 +32,23 @@
32
32
  /**
33
33
  * Color to make the blend operation with. default to a reddish color since black or white
34
34
  * gives always strong result.
35
+ * @type String
36
+ * @default
35
37
  **/
36
38
  color: '#F95C63',
37
39
 
38
40
  /**
39
41
  * Blend mode for the filter: one of multiply, add, diff, screen, subtract,
40
42
  * darken, lighten, overlay, exclusion, tint.
43
+ * @type String
44
+ * @default
41
45
  **/
42
46
  mode: 'multiply',
43
47
 
44
48
  /**
45
49
  * alpha value. represent the strength of the blend color operation.
50
+ * @type Number
51
+ * @default
46
52
  **/
47
53
  alpha: 1,
48
54
 
@@ -36,8 +36,9 @@
36
36
  image: null,
37
37
 
38
38
  /**
39
- * Blend mode for the filter: one of multiply, add, diff, screen, subtract,
40
- * darken, lighten, overlay, exclusion, tint.
39
+ * Blend mode for the filter (one of "multiply", "mask")
40
+ * @type String
41
+ * @default
41
42
  **/
42
43
  mode: 'multiply',
43
44
 
@@ -73,6 +73,8 @@
73
73
  * blur value, in percentage of image dimensions.
74
74
  * specific to keep the image blur constant at different resolutions
75
75
  * range between 0 and 1.
76
+ * @type Number
77
+ * @default
76
78
  */
77
79
  blur: 0,
78
80
 
@@ -66,8 +66,10 @@
66
66
  mainParameter: 'matrix',
67
67
 
68
68
  /**
69
- * Lock the colormatrix on the color part, skipping alpha, manly for non webgl scenario
69
+ * Lock the colormatrix on the color part, skipping alpha, mainly for non webgl scenario
70
70
  * to save some calculation
71
+ * @type Boolean
72
+ * @default true
71
73
  */
72
74
  colorsOnly: true,
73
75
 
@@ -26,7 +26,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
26
26
  return fabric.util.animate({
27
27
  target: this,
28
28
  startValue: object.left,
29
- endValue: this.getCenter().left,
29
+ endValue: this.getCenterPoint().x,
30
30
  duration: this.FX_DURATION,
31
31
  onChange: function(value) {
32
32
  object.set('left', value);
@@ -59,7 +59,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
59
59
  return fabric.util.animate({
60
60
  target: this,
61
61
  startValue: object.top,
62
- endValue: this.getCenter().top,
62
+ endValue: this.getCenterPoint().y,
63
63
  duration: this.FX_DURATION,
64
64
  onChange: function(value) {
65
65
  object.set('top', value);
@@ -12,6 +12,10 @@
12
12
  var _getSvgCommons = fabric.Object.prototype.getSvgCommons;
13
13
  var __createBaseClipPathSVGMarkup = fabric.Object.prototype._createBaseClipPathSVGMarkup;
14
14
  var __createBaseSVGMarkup = fabric.Object.prototype._createBaseSVGMarkup;
15
+
16
+ fabric.Object.prototype.cacheProperties.push('eraser');
17
+ fabric.Object.prototype.stateProperties.push('eraser');
18
+
15
19
  /**
16
20
  * @fires erasing:end
17
21
  */
package/src/parser.js CHANGED
@@ -78,8 +78,7 @@
78
78
  }
79
79
 
80
80
  function normalizeValue(attr, value, parentAttributes, fontSize) {
81
- var isArray = Object.prototype.toString.call(value) === '[object Array]',
82
- parsed;
81
+ var isArray = Array.isArray(value), parsed;
83
82
 
84
83
  if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
85
84
  value = '';
@@ -477,7 +476,7 @@
477
476
  return;
478
477
  }
479
478
 
480
- var xlink = xlinkAttribute.substr(1),
479
+ var xlink = xlinkAttribute.slice(1),
481
480
  x = el.getAttribute('x') || 0,
482
481
  y = el.getAttribute('y') || 0,
483
482
  el2 = elementById(doc, xlink).cloneNode(true),
@@ -749,7 +748,7 @@
749
748
  function recursivelyParseGradientsXlink(doc, gradient) {
750
749
  var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'],
751
750
  xlinkAttr = 'xlink:href',
752
- xLink = gradient.getAttribute(xlinkAttr).substr(1),
751
+ xLink = gradient.getAttribute(xlinkAttr).slice(1),
753
752
  referencedGradient = elementById(doc, xLink);
754
753
  if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) {
755
754
  recursivelyParseGradientsXlink(doc, referencedGradient);
@@ -912,11 +912,9 @@
912
912
  if (object[prop] === prototype[prop]) {
913
913
  delete object[prop];
914
914
  }
915
- var isArray = Object.prototype.toString.call(object[prop]) === '[object Array]' &&
916
- Object.prototype.toString.call(prototype[prop]) === '[object Array]';
917
-
918
915
  // basically a check for [] === []
919
- if (isArray && object[prop].length === 0 && prototype[prop].length === 0) {
916
+ if (Array.isArray(object[prop]) && Array.isArray(prototype[prop])
917
+ && object[prop].length === 0 && prototype[prop].length === 0) {
920
918
  delete object[prop];
921
919
  }
922
920
  });
@@ -1092,7 +1090,7 @@
1092
1090
 
1093
1091
  renderCache: function(options) {
1094
1092
  options = options || {};
1095
- if (!this._cacheCanvas) {
1093
+ if (!this._cacheCanvas || !this._cacheContext) {
1096
1094
  this._createCacheCanvas();
1097
1095
  }
1098
1096
  if (this.isCacheDirty()) {
@@ -1107,6 +1105,7 @@
1107
1105
  */
1108
1106
  _removeCacheCanvas: function() {
1109
1107
  this._cacheCanvas = null;
1108
+ this._cacheContext = null;
1110
1109
  this.cacheWidth = 0;
1111
1110
  this.cacheHeight = 0;
1112
1111
  },
@@ -1265,7 +1264,7 @@
1265
1264
  if (this.isNotVisible()) {
1266
1265
  return false;
1267
1266
  }
1268
- if (this._cacheCanvas && !skipCanvas && this._updateCacheCanvas()) {
1267
+ if (this._cacheCanvas && this._cacheContext && !skipCanvas && this._updateCacheCanvas()) {
1269
1268
  // in this case the context is already cleared.
1270
1269
  return true;
1271
1270
  }
@@ -1274,7 +1273,7 @@
1274
1273
  (this.clipPath && this.clipPath.absolutePositioned) ||
1275
1274
  (this.statefullCache && this.hasStateChanged('cacheProperties'))
1276
1275
  ) {
1277
- if (this._cacheCanvas && !skipCanvas) {
1276
+ if (this._cacheCanvas && this._cacheContext && !skipCanvas) {
1278
1277
  var width = this.cacheWidth / this.zoomX;
1279
1278
  var height = this.cacheHeight / this.zoomY;
1280
1279
  this._cacheContext.clearRect(-width / 2, -height / 2, width, height);
@@ -1808,7 +1807,7 @@
1808
1807
  * @return {Boolean}
1809
1808
  */
1810
1809
  isType: function(type) {
1811
- return this.type === type;
1810
+ return arguments.length > 1 ? Array.from(arguments).includes(this.type) : this.type === type;
1812
1811
  },
1813
1812
 
1814
1813
  /**
@@ -1950,6 +1949,7 @@
1950
1949
 
1951
1950
  /**
1952
1951
  * cancel instance's running animations
1952
+ * override if necessary to dispose artifacts such as `clipPath`
1953
1953
  */
1954
1954
  dispose: function () {
1955
1955
  if (fabric.runningAnimations) {
@@ -7,7 +7,6 @@
7
7
  max = fabric.util.array.max,
8
8
  extend = fabric.util.object.extend,
9
9
  clone = fabric.util.object.clone,
10
- _toString = Object.prototype.toString,
11
10
  toFixed = fabric.util.toFixed;
12
11
 
13
12
  if (fabric.Path) {
@@ -61,10 +60,8 @@
61
60
  * @param {Object} [options] Options object
62
61
  */
63
62
  _setPath: function (path, options) {
64
- var fromArray = _toString.call(path) === '[object Array]';
65
-
66
63
  this.path = fabric.util.makePathSimpler(
67
- fromArray ? path : fabric.util.parsePath(path)
64
+ Array.isArray(path) ? path : fabric.util.parsePath(path)
68
65
  );
69
66
 
70
67
  fabric.Polyline.prototype._setPositionDimensions.call(this, options || {});
@@ -1027,6 +1027,7 @@
1027
1027
  * Returns coordinates of a center of canvas.
1028
1028
  * Returned value is an object with top and left properties
1029
1029
  * @return {Object} object with "top" and "left" number values
1030
+ * @deprecated migrate to `getCenterPoint`
1030
1031
  */
1031
1032
  getCenter: function () {
1032
1033
  return {
@@ -1035,13 +1036,21 @@
1035
1036
  };
1036
1037
  },
1037
1038
 
1039
+ /**
1040
+ * Returns coordinates of a center of canvas.
1041
+ * @return {fabric.Point}
1042
+ */
1043
+ getCenterPoint: function () {
1044
+ return new fabric.Point(this.width / 2, this.height / 2);
1045
+ },
1046
+
1038
1047
  /**
1039
1048
  * Centers object horizontally in the canvas
1040
1049
  * @param {fabric.Object} object Object to center horizontally
1041
1050
  * @return {fabric.Canvas} thisArg
1042
1051
  */
1043
1052
  centerObjectH: function (object) {
1044
- return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
1053
+ return this._centerObject(object, new fabric.Point(this.getCenterPoint().x, object.getCenterPoint().y));
1045
1054
  },
1046
1055
 
1047
1056
  /**
@@ -1051,7 +1060,7 @@
1051
1060
  * @chainable
1052
1061
  */
1053
1062
  centerObjectV: function (object) {
1054
- return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
1063
+ return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenterPoint().y));
1055
1064
  },
1056
1065
 
1057
1066
  /**
@@ -1061,9 +1070,8 @@
1061
1070
  * @chainable
1062
1071
  */
1063
1072
  centerObject: function(object) {
1064
- var center = this.getCenter();
1065
-
1066
- return this._centerObject(object, new fabric.Point(center.left, center.top));
1073
+ var center = this.getCenterPoint();
1074
+ return this._centerObject(object, center);
1067
1075
  },
1068
1076
 
1069
1077
  /**
@@ -1074,7 +1082,6 @@
1074
1082
  */
1075
1083
  viewportCenterObject: function(object) {
1076
1084
  var vpCenter = this.getVpCenter();
1077
-
1078
1085
  return this._centerObject(object, vpCenter);
1079
1086
  },
1080
1087
 
@@ -1108,9 +1115,9 @@
1108
1115
  * @chainable
1109
1116
  */
1110
1117
  getVpCenter: function() {
1111
- var center = this.getCenter(),
1118
+ var center = this.getCenterPoint(),
1112
1119
  iVpt = invertTransform(this.viewportTransform);
1113
- return transformPoint({ x: center.left, y: center.top }, iVpt);
1120
+ return transformPoint(center, iVpt);
1114
1121
  },
1115
1122
 
1116
1123
  /**
@@ -1762,10 +1769,6 @@
1762
1769
  }
1763
1770
  this.forEachObject(function(object) {
1764
1771
  object.dispose && object.dispose();
1765
- // animation module is still optional
1766
- if (fabric.runningAnimations) {
1767
- fabric.runningAnimations.cancelByTarget(object);
1768
- }
1769
1772
  });
1770
1773
  this._objects = [];
1771
1774
  if (this.backgroundImage && this.backgroundImage.dispose) {
@@ -5,19 +5,34 @@
5
5
 
6
6
  /**
7
7
  * @typedef {Object} AnimationOptions
8
- * @property {Function} [options.onChange] Callback; invoked on every value change
9
- * @property {Function} [options.onComplete] Callback; invoked when value change is completed
10
- * @property {Number} [options.startValue=0] Starting value
11
- * @property {Number} [options.endValue=100] Ending value
12
- * @property {Number} [options.byValue=100] Value to modify the property by
13
- * @property {Function} [options.easing] Easing function
14
- * @property {Number} [options.duration=500] Duration of change (in ms)
15
- * @property {Function} [options.abort] Additional function with logic. If returns true, animation aborts.
8
+ * Animation of a value or list of values.
9
+ * When using lists, think of something like this:
10
+ * fabric.util.animate({
11
+ * startValue: [1, 2, 3],
12
+ * endValue: [2, 4, 6],
13
+ * onChange: function([a, b, c]) {
14
+ * canvas.zoomToPoint({x: b, y: c}, a)
15
+ * canvas.renderAll()
16
+ * }
17
+ * });
18
+ * @example
19
+ * @property {Function} [onChange] Callback; invoked on every value change
20
+ * @property {Function} [onComplete] Callback; invoked when value change is completed
21
+ * @example
22
+ * // Note: startValue, endValue, and byValue must match the type
23
+ * var animationOptions = { startValue: 0, endValue: 1, byValue: 0.25 }
24
+ * var animationOptions = { startValue: [0, 1], endValue: [1, 2], byValue: [0.25, 0.25] }
25
+ * @property {number | number[]} [startValue=0] Starting value
26
+ * @property {number | number[]} [endValue=100] Ending value
27
+ * @property {number | number[]} [byValue=100] Value to modify the property by
28
+ * @property {Function} [easing] Easing function
29
+ * @property {Number} [duration=500] Duration of change (in ms)
30
+ * @property {Function} [abort] Additional function with logic. If returns true, animation aborts.
16
31
  *
17
32
  * @typedef {() => void} CancelFunction
18
33
  *
19
34
  * @typedef {Object} AnimationCurrentState
20
- * @property {number} currentValue value in range [`startValue`, `endValue`]
35
+ * @property {number | number[]} currentValue value in range [`startValue`, `endValue`]
21
36
  * @property {number} completionRate value in range [0, 1]
22
37
  * @property {number} durationRate value in range [0, 1]
23
38
  *
@@ -122,6 +137,10 @@
122
137
  * Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
123
138
  * @memberOf fabric.util
124
139
  * @param {AnimationOptions} [options] Animation options
140
+ * @example
141
+ * // Note: startValue, endValue, and byValue must match the type
142
+ * fabric.util.animate({ startValue: 0, endValue: 1, byValue: 0.25 })
143
+ * fabric.util.animate({ startValue: [0, 1], endValue: [1, 2], byValue: [0.25, 0.25] })
125
144
  * @returns {CancelFunction} cancel function
126
145
  */
127
146
  function animate(options) {
@@ -152,9 +171,12 @@
152
171
  abort = options.abort || noop,
153
172
  onComplete = options.onComplete || noop,
154
173
  easing = options.easing || defaultEasing,
174
+ isMany = 'startValue' in options ? options.startValue.length > 0 : false,
155
175
  startValue = 'startValue' in options ? options.startValue : 0,
156
176
  endValue = 'endValue' in options ? options.endValue : 100,
157
- byValue = options.byValue || endValue - startValue;
177
+ byValue = options.byValue || (isMany ? startValue.map(function(value, i) {
178
+ return endValue[i] - startValue[i];
179
+ }) : endValue - startValue);
158
180
 
159
181
  options.onStart && options.onStart();
160
182
 
@@ -162,10 +184,13 @@
162
184
  time = ticktime || +new Date();
163
185
  var currentTime = time > finish ? duration : (time - start),
164
186
  timePerc = currentTime / duration,
165
- current = easing(currentTime, startValue, byValue, duration),
166
- valuePerc = Math.abs((current - startValue) / byValue);
187
+ current = isMany ? startValue.map(function(_value, i) {
188
+ return easing(currentTime, startValue[i], byValue[i], duration);
189
+ }) : easing(currentTime, startValue, byValue, duration),
190
+ valuePerc = isMany ? Math.abs((current[0] - startValue[0]) / byValue[0])
191
+ : Math.abs((current - startValue) / byValue);
167
192
  // update context
168
- context.currentValue = current;
193
+ context.currentValue = isMany ? current.slice() : current;
169
194
  context.completionRate = valuePerc;
170
195
  context.durationRate = timePerc;
171
196
  if (cancel) {
@@ -177,11 +202,11 @@
177
202
  }
178
203
  if (time > finish) {
179
204
  // update context
180
- context.currentValue = endValue;
205
+ context.currentValue = isMany ? endValue.slice() : endValue;
181
206
  context.completionRate = 1;
182
207
  context.durationRate = 1;
183
208
  // execute callbacks
184
- onChange(endValue, 1, 1);
209
+ onChange(isMany ? endValue.slice() : endValue, 1, 1);
185
210
  onComplete(endValue, 1, 1);
186
211
  removeFromRegistry();
187
212
  return;
package/src/util/misc.js CHANGED
@@ -681,7 +681,7 @@
681
681
  * @return {Array} properties Properties names to include
682
682
  */
683
683
  populateWithProperties: function(source, destination, properties) {
684
- if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
684
+ if (properties && Array.isArray(properties)) {
685
685
  for (var i = 0, len = properties.length; i < len; i++) {
686
686
  if (properties[i] in source) {
687
687
  destination[properties[i]] = source[properties[i]];