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.
- package/CHANGELOG.md +78 -0
- package/CONTRIBUTING.md +14 -0
- package/HEADER.js +1 -1
- package/README.md +3 -6
- package/SECURITY.md +5 -0
- package/build.js +1 -0
- package/dist/fabric.js +1160 -668
- package/dist/fabric.min.js +1 -1
- package/lib/event.js +7 -3
- package/package.json +9 -3
- package/publish-next.js +15 -0
- package/publish.js +3 -0
- package/src/brushes/base_brush.class.js +3 -5
- package/src/brushes/pattern_brush.class.js +7 -5
- package/src/brushes/pencil_brush.class.js +49 -37
- package/src/canvas.class.js +27 -57
- package/src/filters/saturate_filter.class.js +9 -1
- package/src/filters/vibrance_filter.class.js +122 -0
- package/src/mixins/animation.mixin.js +20 -25
- package/src/mixins/canvas_events.mixin.js +30 -59
- package/src/mixins/canvas_grouping.mixin.js +4 -4
- package/src/mixins/collection.mixin.js +11 -2
- package/src/mixins/eraser_brush.mixin.js +495 -452
- package/src/mixins/itext_behavior.mixin.js +7 -1
- package/src/mixins/itext_key_behavior.mixin.js +7 -1
- package/src/mixins/object_geometry.mixin.js +5 -35
- package/src/mixins/object_interactivity.mixin.js +18 -7
- package/src/mixins/object_straightening.mixin.js +4 -9
- package/src/mixins/observable.mixin.js +22 -0
- package/src/parser.js +13 -9
- package/src/shapes/circle.class.js +22 -19
- package/src/shapes/ellipse.class.js +2 -2
- package/src/shapes/group.class.js +24 -32
- package/src/shapes/image.class.js +3 -25
- package/src/shapes/itext.class.js +10 -0
- package/src/shapes/line.class.js +5 -21
- package/src/shapes/object.class.js +67 -32
- package/src/shapes/path.class.js +17 -18
- package/src/shapes/polygon.class.js +11 -10
- package/src/shapes/polyline.class.js +33 -24
- package/src/shapes/rect.class.js +0 -18
- package/src/shapes/text.class.js +120 -58
- package/src/shapes/triangle.class.js +0 -15
- package/src/static_canvas.class.js +43 -20
- package/src/util/animate.js +149 -16
- package/src/util/animate_color.js +2 -1
- package/src/util/lang_object.js +5 -1
- package/src/util/misc.js +193 -42
- package/src/util/path.js +89 -52
package/src/shapes/text.class.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
var additionalProps =
|
|
14
14
|
('fontFamily fontWeight fontSize text underline overline linethrough' +
|
|
15
|
-
' textAlign fontStyle lineHeight textBackgroundColor charSpacing styles
|
|
15
|
+
' textAlign fontStyle lineHeight textBackgroundColor charSpacing styles' +
|
|
16
|
+
' direction path pathStartOffset pathSide pathAlign').split(' ');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Text class
|
|
@@ -39,7 +40,10 @@
|
|
|
39
40
|
'charSpacing',
|
|
40
41
|
'textAlign',
|
|
41
42
|
'styles',
|
|
42
|
-
'path'
|
|
43
|
+
'path',
|
|
44
|
+
'pathStartOffset',
|
|
45
|
+
'pathSide',
|
|
46
|
+
'pathAlign'
|
|
43
47
|
],
|
|
44
48
|
|
|
45
49
|
/**
|
|
@@ -197,13 +201,55 @@
|
|
|
197
201
|
shadow: null,
|
|
198
202
|
|
|
199
203
|
/**
|
|
200
|
-
* fabric.Path that the text
|
|
201
|
-
*
|
|
204
|
+
* fabric.Path that the text should follow.
|
|
205
|
+
* since 4.6.0 the path will be drawn automatically.
|
|
206
|
+
* if you want to make the path visible, give it a stroke and strokeWidth or fill value
|
|
207
|
+
* if you want it to be hidden, assign visible = false to the path.
|
|
208
|
+
* This feature is in BETA, and SVG import/export is not yet supported.
|
|
202
209
|
* @type fabric.Path
|
|
210
|
+
* @example
|
|
211
|
+
* var textPath = new fabric.Text('Text on a path', {
|
|
212
|
+
* top: 150,
|
|
213
|
+
* left: 150,
|
|
214
|
+
* textAlign: 'center',
|
|
215
|
+
* charSpacing: -50,
|
|
216
|
+
* path: new fabric.Path('M 0 0 C 50 -100 150 -100 200 0', {
|
|
217
|
+
* strokeWidth: 1,
|
|
218
|
+
* visible: false
|
|
219
|
+
* }),
|
|
220
|
+
* pathSide: 'left',
|
|
221
|
+
* pathStartOffset: 0
|
|
222
|
+
* });
|
|
203
223
|
* @default
|
|
204
224
|
*/
|
|
205
225
|
path: null,
|
|
206
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Offset amount for text path starting position
|
|
229
|
+
* Only used when text has a path
|
|
230
|
+
* @type Number
|
|
231
|
+
* @default
|
|
232
|
+
*/
|
|
233
|
+
pathStartOffset: 0,
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Which side of the path the text should be drawn on.
|
|
237
|
+
* Only used when text has a path
|
|
238
|
+
* @type {String} 'left|right'
|
|
239
|
+
* @default
|
|
240
|
+
*/
|
|
241
|
+
pathSide: 'left',
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* How text is aligned to the path. This property determines
|
|
245
|
+
* the perpendicular position of each character relative to the path.
|
|
246
|
+
* (one of "baseline", "center", "ascender", "descender")
|
|
247
|
+
* This feature is in BETA, and its behavior may change
|
|
248
|
+
* @type String
|
|
249
|
+
* @default
|
|
250
|
+
*/
|
|
251
|
+
pathAlign: 'baseline',
|
|
252
|
+
|
|
207
253
|
/**
|
|
208
254
|
* @private
|
|
209
255
|
*/
|
|
@@ -347,6 +393,8 @@
|
|
|
347
393
|
/**
|
|
348
394
|
* Return a context for measurement of text string.
|
|
349
395
|
* if created it gets stored for reuse
|
|
396
|
+
* this is for internal use, please do not use it
|
|
397
|
+
* @private
|
|
350
398
|
* @param {String} text Text string
|
|
351
399
|
* @param {Object} [options] Options object
|
|
352
400
|
* @return {fabric.Text} thisArg
|
|
@@ -482,6 +530,8 @@
|
|
|
482
530
|
* @param {CanvasRenderingContext2D} ctx Context to render on
|
|
483
531
|
*/
|
|
484
532
|
_render: function(ctx) {
|
|
533
|
+
var path = this.path;
|
|
534
|
+
path && !path.isNotVisible() && path._render(ctx);
|
|
485
535
|
this._setTextStyles(ctx);
|
|
486
536
|
this._renderTextLinesBackground(ctx);
|
|
487
537
|
this._renderTextDecoration(ctx, 'underline');
|
|
@@ -516,7 +566,20 @@
|
|
|
516
566
|
* @param {String} [charStyle.fontStyle] Font style (italic|normal)
|
|
517
567
|
*/
|
|
518
568
|
_setTextStyles: function(ctx, charStyle, forMeasuring) {
|
|
519
|
-
ctx.textBaseline = '
|
|
569
|
+
ctx.textBaseline = 'alphabetical';
|
|
570
|
+
if (this.path) {
|
|
571
|
+
switch (this.pathAlign) {
|
|
572
|
+
case 'center':
|
|
573
|
+
ctx.textBaseline = 'middle';
|
|
574
|
+
break;
|
|
575
|
+
case 'ascender':
|
|
576
|
+
ctx.textBaseline = 'top';
|
|
577
|
+
break;
|
|
578
|
+
case 'descender':
|
|
579
|
+
ctx.textBaseline = 'bottom';
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
520
583
|
ctx.font = this._getFontDeclaration(charStyle, forMeasuring);
|
|
521
584
|
},
|
|
522
585
|
|
|
@@ -741,29 +804,15 @@
|
|
|
741
804
|
_measureLine: function(lineIndex) {
|
|
742
805
|
var width = 0, i, grapheme, line = this._textLines[lineIndex], prevGrapheme,
|
|
743
806
|
graphemeInfo, numOfSpaces = 0, lineBounds = new Array(line.length),
|
|
744
|
-
positionInPath = 0, startingPoint, totalPathLength, path = this.path
|
|
807
|
+
positionInPath = 0, startingPoint, totalPathLength, path = this.path,
|
|
808
|
+
reverse = this.pathSide === 'right';
|
|
745
809
|
|
|
746
810
|
this.__charBounds[lineIndex] = lineBounds;
|
|
747
|
-
if (path) {
|
|
748
|
-
startingPoint = fabric.util.getPointOnPath(path.path, 0, path.segmentsInfo);
|
|
749
|
-
totalPathLength = path.segmentsInfo[path.segmentsInfo.length - 1].length;
|
|
750
|
-
startingPoint.x += path.pathOffset.x;
|
|
751
|
-
startingPoint.y += path.pathOffset.y;
|
|
752
|
-
}
|
|
753
811
|
for (i = 0; i < line.length; i++) {
|
|
754
812
|
grapheme = line[i];
|
|
755
813
|
graphemeInfo = this._getGraphemeBox(grapheme, lineIndex, i, prevGrapheme);
|
|
756
|
-
if (path) {
|
|
757
|
-
if (positionInPath > totalPathLength) {
|
|
758
|
-
positionInPath %= totalPathLength;
|
|
759
|
-
}
|
|
760
|
-
// it would probably much fater to send all the grapheme position for a line
|
|
761
|
-
// and calculate path position/angle at once.
|
|
762
|
-
this._setGraphemeOnPath(positionInPath, graphemeInfo, startingPoint);
|
|
763
|
-
}
|
|
764
814
|
lineBounds[i] = graphemeInfo;
|
|
765
815
|
width += graphemeInfo.kernedWidth;
|
|
766
|
-
positionInPath += graphemeInfo.kernedWidth;
|
|
767
816
|
prevGrapheme = grapheme;
|
|
768
817
|
}
|
|
769
818
|
// this latest bound box represent the last character of the line
|
|
@@ -774,6 +823,40 @@
|
|
|
774
823
|
kernedWidth: 0,
|
|
775
824
|
height: this.fontSize
|
|
776
825
|
};
|
|
826
|
+
if (path) {
|
|
827
|
+
totalPathLength = path.segmentsInfo[path.segmentsInfo.length - 1].length;
|
|
828
|
+
startingPoint = fabric.util.getPointOnPath(path.path, 0, path.segmentsInfo);
|
|
829
|
+
startingPoint.x += path.pathOffset.x;
|
|
830
|
+
startingPoint.y += path.pathOffset.y;
|
|
831
|
+
switch (this.textAlign) {
|
|
832
|
+
case 'left':
|
|
833
|
+
positionInPath = reverse ? (totalPathLength - width) : 0;
|
|
834
|
+
break;
|
|
835
|
+
case 'center':
|
|
836
|
+
positionInPath = (totalPathLength - width) / 2;
|
|
837
|
+
break;
|
|
838
|
+
case 'right':
|
|
839
|
+
positionInPath = reverse ? 0 : (totalPathLength - width);
|
|
840
|
+
break;
|
|
841
|
+
//todo - add support for justify
|
|
842
|
+
}
|
|
843
|
+
positionInPath += this.pathStartOffset * (reverse ? -1 : 1);
|
|
844
|
+
for (i = reverse ? line.length - 1 : 0;
|
|
845
|
+
reverse ? i >= 0 : i < line.length;
|
|
846
|
+
reverse ? i-- : i++) {
|
|
847
|
+
graphemeInfo = lineBounds[i];
|
|
848
|
+
if (positionInPath > totalPathLength) {
|
|
849
|
+
positionInPath %= totalPathLength;
|
|
850
|
+
}
|
|
851
|
+
else if (positionInPath < 0) {
|
|
852
|
+
positionInPath += totalPathLength;
|
|
853
|
+
}
|
|
854
|
+
// it would probably much faster to send all the grapheme position for a line
|
|
855
|
+
// and calculate path position/angle at once.
|
|
856
|
+
this._setGraphemeOnPath(positionInPath, graphemeInfo, startingPoint);
|
|
857
|
+
positionInPath += graphemeInfo.kernedWidth;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
777
860
|
return { width: width, numOfSpaces: numOfSpaces };
|
|
778
861
|
},
|
|
779
862
|
|
|
@@ -793,7 +876,7 @@
|
|
|
793
876
|
var info = fabric.util.getPointOnPath(path.path, centerPosition, path.segmentsInfo);
|
|
794
877
|
graphemeInfo.renderLeft = info.x - startingPoint.x;
|
|
795
878
|
graphemeInfo.renderTop = info.y - startingPoint.y;
|
|
796
|
-
graphemeInfo.angle = info.angle;
|
|
879
|
+
graphemeInfo.angle = info.angle + (this.pathSide === 'right' ? Math.PI : 0);
|
|
797
880
|
},
|
|
798
881
|
|
|
799
882
|
/**
|
|
@@ -961,16 +1044,17 @@
|
|
|
961
1044
|
path = this.path,
|
|
962
1045
|
shortCut = !isJustify && this.charSpacing === 0 && this.isEmptyStyles(lineIndex) && !path,
|
|
963
1046
|
isLtr = this.direction === 'ltr', sign = this.direction === 'ltr' ? 1 : -1,
|
|
964
|
-
drawingLeft;
|
|
965
|
-
|
|
1047
|
+
drawingLeft, currentDirection = ctx.canvas.getAttribute('dir');
|
|
966
1048
|
ctx.save();
|
|
1049
|
+
if (currentDirection !== this.direction) {
|
|
1050
|
+
ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl');
|
|
1051
|
+
ctx.direction = isLtr ? 'ltr' : 'rtl';
|
|
1052
|
+
ctx.textAlign = isLtr ? 'left' : 'right';
|
|
1053
|
+
}
|
|
967
1054
|
top -= lineHeight * this._fontSizeFraction / this.lineHeight;
|
|
968
1055
|
if (shortCut) {
|
|
969
1056
|
// render all the line in one pass without checking
|
|
970
1057
|
// drawingLeft = isLtr ? left : left - this.getLineWidth(lineIndex);
|
|
971
|
-
ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl');
|
|
972
|
-
ctx.direction = isLtr ? 'ltr' : 'rtl';
|
|
973
|
-
ctx.textAlign = isLtr ? 'left' : 'right';
|
|
974
1058
|
this._renderChar(method, ctx, lineIndex, 0, line.join(''), left, top, lineHeight);
|
|
975
1059
|
ctx.restore();
|
|
976
1060
|
return;
|
|
@@ -1007,9 +1091,6 @@
|
|
|
1007
1091
|
}
|
|
1008
1092
|
else {
|
|
1009
1093
|
drawingLeft = left;
|
|
1010
|
-
ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl');
|
|
1011
|
-
ctx.direction = isLtr ? 'ltr' : 'rtl';
|
|
1012
|
-
ctx.textAlign = isLtr ? 'left' : 'right';
|
|
1013
1094
|
this._renderChar(method, ctx, lineIndex, i, charsToRender, drawingLeft, top, lineHeight);
|
|
1014
1095
|
}
|
|
1015
1096
|
charsToRender = '';
|
|
@@ -1260,19 +1341,12 @@
|
|
|
1260
1341
|
* @return {Number} Line width
|
|
1261
1342
|
*/
|
|
1262
1343
|
getLineWidth: function(lineIndex) {
|
|
1263
|
-
if (this.__lineWidths[lineIndex]) {
|
|
1344
|
+
if (this.__lineWidths[lineIndex] !== undefined) {
|
|
1264
1345
|
return this.__lineWidths[lineIndex];
|
|
1265
1346
|
}
|
|
1266
1347
|
|
|
1267
|
-
var
|
|
1268
|
-
|
|
1269
|
-
if (line === '') {
|
|
1270
|
-
width = 0;
|
|
1271
|
-
}
|
|
1272
|
-
else {
|
|
1273
|
-
lineInfo = this.measureLine(lineIndex);
|
|
1274
|
-
width = lineInfo.width;
|
|
1275
|
-
}
|
|
1348
|
+
var lineInfo = this.measureLine(lineIndex);
|
|
1349
|
+
var width = lineInfo.width;
|
|
1276
1350
|
this.__lineWidths[lineIndex] = width;
|
|
1277
1351
|
return width;
|
|
1278
1352
|
},
|
|
@@ -1462,25 +1536,13 @@
|
|
|
1462
1536
|
* @return {Object} Object representation of an instance
|
|
1463
1537
|
*/
|
|
1464
1538
|
toObject: function(propertiesToInclude) {
|
|
1465
|
-
var
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
'fontWeight',
|
|
1469
|
-
'fontFamily',
|
|
1470
|
-
'fontStyle',
|
|
1471
|
-
'lineHeight',
|
|
1472
|
-
'underline',
|
|
1473
|
-
'overline',
|
|
1474
|
-
'linethrough',
|
|
1475
|
-
'textAlign',
|
|
1476
|
-
'textBackgroundColor',
|
|
1477
|
-
'charSpacing',
|
|
1478
|
-
'path',
|
|
1479
|
-
'direction',
|
|
1480
|
-
].concat(propertiesToInclude);
|
|
1481
|
-
var obj = this.callSuper('toObject', additionalProperties);
|
|
1539
|
+
var allProperties = additionalProps.concat(propertiesToInclude);
|
|
1540
|
+
var obj = this.callSuper('toObject', allProperties);
|
|
1541
|
+
// styles will be overridden with a properly cloned structure
|
|
1482
1542
|
obj.styles = clone(this.styles, true);
|
|
1483
|
-
obj.path
|
|
1543
|
+
if (obj.path) {
|
|
1544
|
+
obj.path = this.path.toObject();
|
|
1545
|
+
}
|
|
1484
1546
|
return obj;
|
|
1485
1547
|
},
|
|
1486
1548
|
|
|
@@ -56,21 +56,6 @@
|
|
|
56
56
|
this._renderPaintInOrder(ctx);
|
|
57
57
|
},
|
|
58
58
|
|
|
59
|
-
/**
|
|
60
|
-
* @private
|
|
61
|
-
* @param {CanvasRenderingContext2D} ctx Context to render on
|
|
62
|
-
*/
|
|
63
|
-
_renderDashedStroke: function(ctx) {
|
|
64
|
-
var widthBy2 = this.width / 2,
|
|
65
|
-
heightBy2 = this.height / 2;
|
|
66
|
-
|
|
67
|
-
ctx.beginPath();
|
|
68
|
-
fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
|
|
69
|
-
fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
|
|
70
|
-
fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
|
|
71
|
-
ctx.closePath();
|
|
72
|
-
},
|
|
73
|
-
|
|
74
59
|
/* _TO_SVG_START_ */
|
|
75
60
|
/**
|
|
76
61
|
* Returns svg representation of an instance
|
|
@@ -133,8 +133,12 @@
|
|
|
133
133
|
imageSmoothingEnabled: true,
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* The transformation (
|
|
136
|
+
* The transformation (a Canvas 2D API transform matrix) which focuses the viewport
|
|
137
137
|
* @type Array
|
|
138
|
+
* @example <caption>Default transform</caption>
|
|
139
|
+
* canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
|
|
140
|
+
* @example <caption>Scale by 70% and translate toward bottom-right by 50, without skewing</caption>
|
|
141
|
+
* canvas.viewportTransform = [0.7, 0, 0, 0.7, 50, 50];
|
|
138
142
|
* @default
|
|
139
143
|
*/
|
|
140
144
|
viewportTransform: fabric.iMatrix.concat(),
|
|
@@ -228,7 +232,7 @@
|
|
|
228
232
|
* @private
|
|
229
233
|
*/
|
|
230
234
|
_isRetinaScaling: function() {
|
|
231
|
-
return (fabric.devicePixelRatio
|
|
235
|
+
return (fabric.devicePixelRatio > 1 && this.enableRetinaScaling);
|
|
232
236
|
},
|
|
233
237
|
|
|
234
238
|
/**
|
|
@@ -236,7 +240,7 @@
|
|
|
236
240
|
* @return {Number} retinaScaling if applied, otherwise 1;
|
|
237
241
|
*/
|
|
238
242
|
getRetinaScaling: function() {
|
|
239
|
-
return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
|
|
243
|
+
return this._isRetinaScaling() ? Math.max(1, fabric.devicePixelRatio) : 1;
|
|
240
244
|
},
|
|
241
245
|
|
|
242
246
|
/**
|
|
@@ -523,7 +527,7 @@
|
|
|
523
527
|
}
|
|
524
528
|
|
|
525
529
|
fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
|
|
526
|
-
|
|
530
|
+
this._originalCanvasStyle = this.lowerCanvasEl.style;
|
|
527
531
|
if (this.interactive) {
|
|
528
532
|
this._applyCanvasStyle(this.lowerCanvasEl);
|
|
529
533
|
}
|
|
@@ -603,7 +607,7 @@
|
|
|
603
607
|
}
|
|
604
608
|
}
|
|
605
609
|
if (this._isCurrentlyDrawing) {
|
|
606
|
-
this.freeDrawingBrush && this.freeDrawingBrush._setBrushStyles();
|
|
610
|
+
this.freeDrawingBrush && this.freeDrawingBrush._setBrushStyles(this.contextTop);
|
|
607
611
|
}
|
|
608
612
|
this._initRetinaScaling();
|
|
609
613
|
this.calcOffset();
|
|
@@ -670,8 +674,8 @@
|
|
|
670
674
|
},
|
|
671
675
|
|
|
672
676
|
/**
|
|
673
|
-
* Sets viewport
|
|
674
|
-
* @param {Array} vpt
|
|
677
|
+
* Sets viewport transformation of this canvas instance
|
|
678
|
+
* @param {Array} vpt a Canvas 2D API transform matrix
|
|
675
679
|
* @return {fabric.Canvas} instance
|
|
676
680
|
* @chainable true
|
|
677
681
|
*/
|
|
@@ -813,7 +817,7 @@
|
|
|
813
817
|
* @chainable
|
|
814
818
|
*/
|
|
815
819
|
clear: function () {
|
|
816
|
-
this.
|
|
820
|
+
this.remove.apply(this, this.getObjects());
|
|
817
821
|
this.backgroundImage = null;
|
|
818
822
|
this.overlayImage = null;
|
|
819
823
|
this.backgroundColor = '';
|
|
@@ -1159,7 +1163,7 @@
|
|
|
1159
1163
|
version: fabric.version,
|
|
1160
1164
|
objects: this._toObjects(methodName, propertiesToInclude),
|
|
1161
1165
|
};
|
|
1162
|
-
if (clipPath) {
|
|
1166
|
+
if (clipPath && !clipPath.excludeFromExport) {
|
|
1163
1167
|
data.clipPath = this._toObject(this.clipPath, methodName, propertiesToInclude);
|
|
1164
1168
|
}
|
|
1165
1169
|
extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
|
|
@@ -1202,24 +1206,32 @@
|
|
|
1202
1206
|
* @private
|
|
1203
1207
|
*/
|
|
1204
1208
|
__serializeBgOverlay: function(methodName, propertiesToInclude) {
|
|
1205
|
-
var data = {
|
|
1209
|
+
var data = {}, bgImage = this.backgroundImage, overlayImage = this.overlayImage,
|
|
1210
|
+
bgColor = this.backgroundColor, overlayColor = this.overlayColor;
|
|
1206
1211
|
|
|
1207
|
-
if (
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1212
|
+
if (bgColor && bgColor.toObject) {
|
|
1213
|
+
if (!bgColor.excludeFromExport) {
|
|
1214
|
+
data.background = bgColor.toObject(propertiesToInclude);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
else if (bgColor) {
|
|
1218
|
+
data.background = bgColor;
|
|
1211
1219
|
}
|
|
1212
1220
|
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1221
|
+
if (overlayColor && overlayColor.toObject) {
|
|
1222
|
+
if (!overlayColor.excludeFromExport) {
|
|
1223
|
+
data.overlay = overlayColor.toObject(propertiesToInclude);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
else if (overlayColor) {
|
|
1227
|
+
data.overlay = overlayColor;
|
|
1217
1228
|
}
|
|
1229
|
+
|
|
1218
1230
|
if (bgImage && !bgImage.excludeFromExport) {
|
|
1219
1231
|
data.backgroundImage = this._toObject(bgImage, methodName, propertiesToInclude);
|
|
1220
1232
|
}
|
|
1221
|
-
if (
|
|
1222
|
-
data.overlayImage = this._toObject(
|
|
1233
|
+
if (overlayImage && !overlayImage.excludeFromExport) {
|
|
1234
|
+
data.overlayImage = this._toObject(overlayImage, methodName, propertiesToInclude);
|
|
1223
1235
|
}
|
|
1224
1236
|
|
|
1225
1237
|
return data;
|
|
@@ -1750,6 +1762,10 @@
|
|
|
1750
1762
|
}
|
|
1751
1763
|
this.forEachObject(function(object) {
|
|
1752
1764
|
object.dispose && object.dispose();
|
|
1765
|
+
// animation module is still optional
|
|
1766
|
+
if (fabric.runningAnimations) {
|
|
1767
|
+
fabric.runningAnimations.cancelByTarget(object);
|
|
1768
|
+
}
|
|
1753
1769
|
});
|
|
1754
1770
|
this._objects = [];
|
|
1755
1771
|
if (this.backgroundImage && this.backgroundImage.dispose) {
|
|
@@ -1762,6 +1778,13 @@
|
|
|
1762
1778
|
this.overlayImage = null;
|
|
1763
1779
|
this._iTextInstances = null;
|
|
1764
1780
|
this.contextContainer = null;
|
|
1781
|
+
// restore canvas style
|
|
1782
|
+
this.lowerCanvasEl.classList.remove('lower-canvas');
|
|
1783
|
+
fabric.util.setStyle(this.lowerCanvasEl, this._originalCanvasStyle);
|
|
1784
|
+
delete this._originalCanvasStyle;
|
|
1785
|
+
// restore canvas size to original size in case retina scaling was applied
|
|
1786
|
+
this.lowerCanvasEl.setAttribute('width', this.width);
|
|
1787
|
+
this.lowerCanvasEl.setAttribute('height', this.height);
|
|
1765
1788
|
fabric.util.cleanUpJsdomNode(this.lowerCanvasEl);
|
|
1766
1789
|
this.lowerCanvasEl = undefined;
|
|
1767
1790
|
return this;
|
package/src/util/animate.js
CHANGED
|
@@ -1,4 +1,114 @@
|
|
|
1
|
-
(function() {
|
|
1
|
+
(function () {
|
|
2
|
+
|
|
3
|
+
var extend = fabric.util.object.extend,
|
|
4
|
+
clone = fabric.util.object.clone;
|
|
5
|
+
|
|
6
|
+
/**
|
|
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.
|
|
16
|
+
*
|
|
17
|
+
* @typedef {() => void} CancelFunction
|
|
18
|
+
*
|
|
19
|
+
* @typedef {Object} AnimationCurrentState
|
|
20
|
+
* @property {number} currentValue value in range [`startValue`, `endValue`]
|
|
21
|
+
* @property {number} completionRate value in range [0, 1]
|
|
22
|
+
* @property {number} durationRate value in range [0, 1]
|
|
23
|
+
*
|
|
24
|
+
* @typedef {(AnimationOptions & AnimationCurrentState & { cancel: CancelFunction }} AnimationContext
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Array holding all running animations
|
|
29
|
+
* @memberof fabric
|
|
30
|
+
* @type {AnimationContext[]}
|
|
31
|
+
*/
|
|
32
|
+
var RUNNING_ANIMATIONS = [];
|
|
33
|
+
fabric.util.object.extend(RUNNING_ANIMATIONS, {
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* cancel all running animations at the next requestAnimFrame
|
|
37
|
+
* @returns {AnimationContext[]}
|
|
38
|
+
*/
|
|
39
|
+
cancelAll: function () {
|
|
40
|
+
var animations = this.splice(0);
|
|
41
|
+
animations.forEach(function (animation) {
|
|
42
|
+
animation.cancel();
|
|
43
|
+
});
|
|
44
|
+
return animations;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* cancel all running animations attached to canvas at the next requestAnimFrame
|
|
49
|
+
* @param {fabric.Canvas} canvas
|
|
50
|
+
* @returns {AnimationContext[]}
|
|
51
|
+
*/
|
|
52
|
+
cancelByCanvas: function (canvas) {
|
|
53
|
+
if (!canvas) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
var cancelled = this.filter(function (animation) {
|
|
57
|
+
return typeof animation.target === 'object' && animation.target.canvas === canvas;
|
|
58
|
+
});
|
|
59
|
+
cancelled.forEach(function (animation) {
|
|
60
|
+
animation.cancel();
|
|
61
|
+
});
|
|
62
|
+
return cancelled;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* cancel all running animations for target at the next requestAnimFrame
|
|
67
|
+
* @param {*} target
|
|
68
|
+
* @returns {AnimationContext[]}
|
|
69
|
+
*/
|
|
70
|
+
cancelByTarget: function (target) {
|
|
71
|
+
var cancelled = this.findAnimationsByTarget(target);
|
|
72
|
+
cancelled.forEach(function (animation) {
|
|
73
|
+
animation.cancel();
|
|
74
|
+
});
|
|
75
|
+
return cancelled;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
* @param {CancelFunction} cancelFunc the function returned by animate
|
|
81
|
+
* @returns {number}
|
|
82
|
+
*/
|
|
83
|
+
findAnimationIndex: function (cancelFunc) {
|
|
84
|
+
return this.indexOf(this.findAnimation(cancelFunc));
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
*
|
|
89
|
+
* @param {CancelFunction} cancelFunc the function returned by animate
|
|
90
|
+
* @returns {AnimationContext | undefined} animation's options object
|
|
91
|
+
*/
|
|
92
|
+
findAnimation: function (cancelFunc) {
|
|
93
|
+
return this.find(function (animation) {
|
|
94
|
+
return animation.cancel === cancelFunc;
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
*
|
|
100
|
+
* @param {*} target the object that is assigned to the target property of the animation context
|
|
101
|
+
* @returns {AnimationContext[]} array of animation options object associated with target
|
|
102
|
+
*/
|
|
103
|
+
findAnimationsByTarget: function (target) {
|
|
104
|
+
if (!target) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return this.filter(function (animation) {
|
|
108
|
+
return animation.target === target;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
2
112
|
|
|
3
113
|
function noop() {
|
|
4
114
|
return false;
|
|
@@ -11,21 +121,30 @@
|
|
|
11
121
|
/**
|
|
12
122
|
* Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
|
|
13
123
|
* @memberOf fabric.util
|
|
14
|
-
* @param {
|
|
15
|
-
* @
|
|
16
|
-
* @param {Function} [options.onComplete] Callback; invoked when value change is completed
|
|
17
|
-
* @param {Number} [options.startValue=0] Starting value
|
|
18
|
-
* @param {Number} [options.endValue=100] Ending value
|
|
19
|
-
* @param {Number} [options.byValue=100] Value to modify the property by
|
|
20
|
-
* @param {Function} [options.easing] Easing function
|
|
21
|
-
* @param {Number} [options.duration=500] Duration of change (in ms)
|
|
22
|
-
* @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.
|
|
124
|
+
* @param {AnimationOptions} [options] Animation options
|
|
125
|
+
* @returns {CancelFunction} cancel function
|
|
23
126
|
*/
|
|
24
127
|
function animate(options) {
|
|
128
|
+
options || (options = {});
|
|
129
|
+
var cancel = false,
|
|
130
|
+
context,
|
|
131
|
+
removeFromRegistry = function () {
|
|
132
|
+
var index = fabric.runningAnimations.indexOf(context);
|
|
133
|
+
return index > -1 && fabric.runningAnimations.splice(index, 1)[0];
|
|
134
|
+
};
|
|
25
135
|
|
|
26
|
-
|
|
27
|
-
|
|
136
|
+
context = extend(clone(options), {
|
|
137
|
+
cancel: function () {
|
|
138
|
+
cancel = true;
|
|
139
|
+
return removeFromRegistry();
|
|
140
|
+
},
|
|
141
|
+
currentValue: 'startValue' in options ? options.startValue : 0,
|
|
142
|
+
completionRate: 0,
|
|
143
|
+
durationRate: 0
|
|
144
|
+
});
|
|
145
|
+
fabric.runningAnimations.push(context);
|
|
28
146
|
|
|
147
|
+
requestAnimFrame(function(timestamp) {
|
|
29
148
|
var start = timestamp || +new Date(),
|
|
30
149
|
duration = options.duration || 500,
|
|
31
150
|
finish = start + duration, time,
|
|
@@ -40,20 +159,31 @@
|
|
|
40
159
|
options.onStart && options.onStart();
|
|
41
160
|
|
|
42
161
|
(function tick(ticktime) {
|
|
43
|
-
// TODO: move abort call after calculation
|
|
44
|
-
// and pass (current,valuePerc, timePerc) as arguments
|
|
45
162
|
time = ticktime || +new Date();
|
|
46
163
|
var currentTime = time > finish ? duration : (time - start),
|
|
47
164
|
timePerc = currentTime / duration,
|
|
48
165
|
current = easing(currentTime, startValue, byValue, duration),
|
|
49
166
|
valuePerc = Math.abs((current - startValue) / byValue);
|
|
50
|
-
|
|
51
|
-
|
|
167
|
+
// update context
|
|
168
|
+
context.currentValue = current;
|
|
169
|
+
context.completionRate = valuePerc;
|
|
170
|
+
context.durationRate = timePerc;
|
|
171
|
+
if (cancel) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (abort(current, valuePerc, timePerc)) {
|
|
175
|
+
removeFromRegistry();
|
|
52
176
|
return;
|
|
53
177
|
}
|
|
54
178
|
if (time > finish) {
|
|
179
|
+
// update context
|
|
180
|
+
context.currentValue = endValue;
|
|
181
|
+
context.completionRate = 1;
|
|
182
|
+
context.durationRate = 1;
|
|
183
|
+
// execute callbacks
|
|
55
184
|
onChange(endValue, 1, 1);
|
|
56
185
|
onComplete(endValue, 1, 1);
|
|
186
|
+
removeFromRegistry();
|
|
57
187
|
return;
|
|
58
188
|
}
|
|
59
189
|
else {
|
|
@@ -62,6 +192,8 @@
|
|
|
62
192
|
}
|
|
63
193
|
})(start);
|
|
64
194
|
});
|
|
195
|
+
|
|
196
|
+
return context.cancel;
|
|
65
197
|
}
|
|
66
198
|
|
|
67
199
|
var _requestAnimFrame = fabric.window.requestAnimationFrame ||
|
|
@@ -93,4 +225,5 @@
|
|
|
93
225
|
fabric.util.animate = animate;
|
|
94
226
|
fabric.util.requestAnimFrame = requestAnimFrame;
|
|
95
227
|
fabric.util.cancelAnimFrame = cancelAnimFrame;
|
|
228
|
+
fabric.runningAnimations = RUNNING_ANIMATIONS;
|
|
96
229
|
})();
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* @param {Function} [options.onComplete] Callback; invoked when value change is completed
|
|
25
25
|
* @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.
|
|
26
26
|
* @param {Function} [options.abort] Additional function with logic. If returns true, onComplete is called.
|
|
27
|
+
* @returns {Function} abort function
|
|
27
28
|
*/
|
|
28
29
|
function animateColor(fromColor, toColor, duration, options) {
|
|
29
30
|
var startColor = new fabric.Color(fromColor).getSource(),
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
originalOnChange = options.onChange;
|
|
33
34
|
options = options || {};
|
|
34
35
|
|
|
35
|
-
fabric.util.animate(fabric.util.object.extend(options, {
|
|
36
|
+
return fabric.util.animate(fabric.util.object.extend(options, {
|
|
36
37
|
duration: duration || 500,
|
|
37
38
|
startValue: startColor,
|
|
38
39
|
endValue: endColor,
|