fabric 4.3.0 → 4.4.0
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 +22 -0
- package/HEADER.js +1 -1
- package/README.md +3 -1
- package/dist/fabric.js +312 -101
- package/dist/fabric.min.js +1 -1
- package/{.travis.yml → old-travis-reference.yml} +0 -5
- package/package.json +6 -4
- package/src/brushes/base_brush.class.js +18 -0
- package/src/brushes/circle_brush.class.js +3 -0
- package/src/brushes/pencil_brush.class.js +3 -0
- package/src/brushes/spray_brush.class.js +3 -0
- package/src/canvas.class.js +45 -28
- package/src/controls.actions.js +1 -1
- package/src/controls.render.js +1 -1
- package/src/mixins/canvas_events.mixin.js +17 -11
- package/src/shapes/group.class.js +34 -31
- package/src/shapes/image.class.js +15 -2
- package/src/shapes/itext.class.js +3 -2
- package/src/shapes/object.class.js +12 -2
- package/src/shapes/text.class.js +38 -13
- package/src/static_canvas.class.js +10 -1
- package/src/util/misc.js +53 -0
- package/src/util/path.js +55 -8
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": "4.
|
|
5
|
+
"version": "4.4.0",
|
|
6
6
|
"author": "Juriy Zaytsev <kangax@gmail.com>",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|
|
@@ -47,7 +47,10 @@
|
|
|
47
47
|
"build_with_gestures": "node build.js modules=ALL exclude=accessors",
|
|
48
48
|
"build_export": "npm run build:fast && npm run export_dist_to_site",
|
|
49
49
|
"test:single": "qunit test/node_test_setup.js test/lib",
|
|
50
|
-
"test": "nyc qunit test/node_test_setup.js test/lib test/unit",
|
|
50
|
+
"test:coverage": "nyc --silent qunit test/node_test_setup.js test/lib test/unit",
|
|
51
|
+
"test:visual:coverage": "nyc --silent --no-clean qunit test/node_test_setup.js test/lib test/visual",
|
|
52
|
+
"coverage:report": "nyc report --reporter=lcov --reporter=text",
|
|
53
|
+
"test": "qunit test/node_test_setup.js test/lib test/unit",
|
|
51
54
|
"test:visual": "qunit test/node_test_setup.js test/lib test/visual",
|
|
52
55
|
"test:visual:single": "qunit test/node_test_setup.js test/lib",
|
|
53
56
|
"test:all": "npm run test && npm run test:visual",
|
|
@@ -58,7 +61,6 @@
|
|
|
58
61
|
"export_tests_to_site": "cp test/unit/*.js ../fabricjs.com/test/unit && cp -r test/visual/* ../fabricjs.com/test/visual && cp -r test/fixtures/* ../fabricjs.com/test/fixtures && cp -r test/lib/* ../fabricjs.com/test/lib",
|
|
59
62
|
"all": "npm run build && npm run test && npm run test:visual && npm run lint && npm run lint_tests && npm run export_dist_to_site && npm run export_tests_to_site",
|
|
60
63
|
"testem": "testem .",
|
|
61
|
-
"testem:visual": "testem --file testem-visual.json",
|
|
62
64
|
"testem:ci": "testem ci"
|
|
63
65
|
},
|
|
64
66
|
"optionalDependencies": {
|
|
@@ -71,7 +73,7 @@
|
|
|
71
73
|
"nyc": "^15.1.0",
|
|
72
74
|
"onchange": "^3.x.x",
|
|
73
75
|
"pixelmatch": "^4.0.2",
|
|
74
|
-
"qunit": "2.
|
|
76
|
+
"qunit": "^2.13.0",
|
|
75
77
|
"testem": "^3.2.0",
|
|
76
78
|
"uglify-js": "3.3.x"
|
|
77
79
|
},
|
|
@@ -56,6 +56,15 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
|
|
|
56
56
|
*/
|
|
57
57
|
strokeDashArray: null,
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* When `true`, the free drawing is limited to the whiteboard size. Default to false.
|
|
61
|
+
* @type Boolean
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
limitedToCanvasSize: false,
|
|
66
|
+
|
|
67
|
+
|
|
59
68
|
/**
|
|
60
69
|
* Sets brush styles
|
|
61
70
|
* @private
|
|
@@ -120,5 +129,14 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
|
|
|
120
129
|
|
|
121
130
|
ctx.shadowColor = '';
|
|
122
131
|
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check is pointer is outside canvas boundaries
|
|
136
|
+
* @param {Object} pointer
|
|
137
|
+
* @private
|
|
138
|
+
*/
|
|
139
|
+
_isOutSideCanvas: function(pointer) {
|
|
140
|
+
return pointer.x < 0 || pointer.x > this.canvas.getWidth() || pointer.y < 0 || pointer.y > this.canvas.getHeight();
|
|
123
141
|
}
|
|
124
142
|
});
|
|
@@ -70,6 +70,9 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
|
|
|
70
70
|
* @param {Object} pointer
|
|
71
71
|
*/
|
|
72
72
|
onMouseMove: function(pointer) {
|
|
73
|
+
if (this.limitedToCanvasSize === true && this._isOutSideCanvas(pointer)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
73
76
|
if (this.needsFullRender()) {
|
|
74
77
|
this.canvas.clearContext(this.canvas.contextTop);
|
|
75
78
|
this.addPoint(pointer);
|
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
if (!this.canvas._isMainEvent(options.e)) {
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
+
if (this.limitedToCanvasSize === true && this._isOutSideCanvas(pointer)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
59
62
|
if (this._captureDrawingPath(pointer) && this._points.length > 1) {
|
|
60
63
|
if (this.needsFullRender()) {
|
|
61
64
|
// redraw curve
|
|
@@ -74,6 +74,9 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
|
|
|
74
74
|
* @param {Object} pointer
|
|
75
75
|
*/
|
|
76
76
|
onMouseMove: function(pointer) {
|
|
77
|
+
if (this.limitedToCanvasSize === true && this._isOutSideCanvas(pointer)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
77
80
|
this.addSprayChunk(pointer);
|
|
78
81
|
this.render(this.sprayChunkPoints);
|
|
79
82
|
},
|
package/src/canvas.class.js
CHANGED
|
@@ -497,13 +497,6 @@
|
|
|
497
497
|
target.render(ctx);
|
|
498
498
|
ctx.restore();
|
|
499
499
|
|
|
500
|
-
target === this._activeObject && target._renderControls(ctx, {
|
|
501
|
-
hasBorders: false,
|
|
502
|
-
transparentCorners: false
|
|
503
|
-
}, {
|
|
504
|
-
hasBorders: false,
|
|
505
|
-
});
|
|
506
|
-
|
|
507
500
|
target.selectionBackgroundColor = originalColor;
|
|
508
501
|
|
|
509
502
|
var isTransparent = fabric.util.isTransparent(
|
|
@@ -765,18 +758,19 @@
|
|
|
765
758
|
activeObject = this._activeObject,
|
|
766
759
|
aObjects = this.getActiveObjects(),
|
|
767
760
|
activeTarget, activeTargetSubs,
|
|
768
|
-
isTouch = isTouchEvent(e)
|
|
761
|
+
isTouch = isTouchEvent(e),
|
|
762
|
+
shouldLookForActive = (aObjects.length > 1 && !skipGroup) || aObjects.length === 1;
|
|
769
763
|
|
|
770
764
|
// first check current group (if one exists)
|
|
771
765
|
// active group does not check sub targets like normal groups.
|
|
772
766
|
// if active group just exits.
|
|
773
767
|
this.targets = [];
|
|
774
768
|
|
|
775
|
-
if
|
|
769
|
+
// if we hit the corner of an activeObject, let's return that.
|
|
770
|
+
if (shouldLookForActive && activeObject._findTargetCorner(pointer, isTouch)) {
|
|
776
771
|
return activeObject;
|
|
777
772
|
}
|
|
778
|
-
|
|
779
|
-
if (aObjects.length === 1 && activeObject._findTargetCorner(pointer, isTouch)) {
|
|
773
|
+
if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([activeObject], pointer)) {
|
|
780
774
|
return activeObject;
|
|
781
775
|
}
|
|
782
776
|
if (aObjects.length === 1 &&
|
|
@@ -812,7 +806,7 @@
|
|
|
812
806
|
obj.evented &&
|
|
813
807
|
// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
|
|
814
808
|
// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
|
|
815
|
-
|
|
809
|
+
obj.containsPoint(pointer)
|
|
816
810
|
) {
|
|
817
811
|
if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
|
|
818
812
|
var isTransparent = this.isTargetTransparent(obj, globalPointer.x, globalPointer.y);
|
|
@@ -1088,38 +1082,50 @@
|
|
|
1088
1082
|
*/
|
|
1089
1083
|
_fireSelectionEvents: function(oldObjects, e) {
|
|
1090
1084
|
var somethingChanged = false, objects = this.getActiveObjects(),
|
|
1091
|
-
added = [], removed = []
|
|
1085
|
+
added = [], removed = [];
|
|
1092
1086
|
oldObjects.forEach(function(oldObject) {
|
|
1093
1087
|
if (objects.indexOf(oldObject) === -1) {
|
|
1094
1088
|
somethingChanged = true;
|
|
1095
|
-
oldObject.fire('deselected',
|
|
1089
|
+
oldObject.fire('deselected', {
|
|
1090
|
+
e: e,
|
|
1091
|
+
target: oldObject
|
|
1092
|
+
});
|
|
1096
1093
|
removed.push(oldObject);
|
|
1097
1094
|
}
|
|
1098
1095
|
});
|
|
1099
1096
|
objects.forEach(function(object) {
|
|
1100
1097
|
if (oldObjects.indexOf(object) === -1) {
|
|
1101
1098
|
somethingChanged = true;
|
|
1102
|
-
object.fire('selected',
|
|
1099
|
+
object.fire('selected', {
|
|
1100
|
+
e: e,
|
|
1101
|
+
target: object
|
|
1102
|
+
});
|
|
1103
1103
|
added.push(object);
|
|
1104
1104
|
}
|
|
1105
1105
|
});
|
|
1106
1106
|
if (oldObjects.length > 0 && objects.length > 0) {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1107
|
+
somethingChanged && this.fire('selection:updated', {
|
|
1108
|
+
e: e,
|
|
1109
|
+
selected: added,
|
|
1110
|
+
deselected: removed,
|
|
1111
|
+
// added for backward compatibility
|
|
1112
|
+
// deprecated
|
|
1113
|
+
updated: added[0] || removed[0],
|
|
1114
|
+
target: this._activeObject,
|
|
1115
|
+
});
|
|
1113
1116
|
}
|
|
1114
1117
|
else if (objects.length > 0) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1118
|
+
this.fire('selection:created', {
|
|
1119
|
+
e: e,
|
|
1120
|
+
selected: added,
|
|
1121
|
+
target: this._activeObject,
|
|
1122
|
+
});
|
|
1119
1123
|
}
|
|
1120
1124
|
else if (oldObjects.length > 0) {
|
|
1121
|
-
|
|
1122
|
-
|
|
1125
|
+
this.fire('selection:cleared', {
|
|
1126
|
+
e: e,
|
|
1127
|
+
deselected: removed,
|
|
1128
|
+
});
|
|
1123
1129
|
}
|
|
1124
1130
|
},
|
|
1125
1131
|
|
|
@@ -1138,6 +1144,10 @@
|
|
|
1138
1144
|
},
|
|
1139
1145
|
|
|
1140
1146
|
/**
|
|
1147
|
+
* This is a private method for now.
|
|
1148
|
+
* This is supposed to be equivalent to setActiveObject but without firing
|
|
1149
|
+
* any event. There is commitment to have this stay this way.
|
|
1150
|
+
* This is the functional part of setActiveObject.
|
|
1141
1151
|
* @private
|
|
1142
1152
|
* @param {Object} object to set as active
|
|
1143
1153
|
* @param {Event} [e] Event (passed along when firing "object:selected")
|
|
@@ -1158,6 +1168,13 @@
|
|
|
1158
1168
|
},
|
|
1159
1169
|
|
|
1160
1170
|
/**
|
|
1171
|
+
* This is a private method for now.
|
|
1172
|
+
* This is supposed to be equivalent to discardActiveObject but without firing
|
|
1173
|
+
* any events. There is commitment to have this stay this way.
|
|
1174
|
+
* This is the functional part of discardActiveObject.
|
|
1175
|
+
* @param {Event} [e] Event (passed along when firing "object:deselected")
|
|
1176
|
+
* @param {Object} object to set as active
|
|
1177
|
+
* @return {Boolean} true if the selection happened
|
|
1161
1178
|
* @private
|
|
1162
1179
|
*/
|
|
1163
1180
|
_discardActiveObject: function(e, object) {
|
|
@@ -1268,7 +1285,7 @@
|
|
|
1268
1285
|
layoutProps.forEach(function(prop) {
|
|
1269
1286
|
originalValues[prop] = instance[prop];
|
|
1270
1287
|
});
|
|
1271
|
-
this._activeObject.
|
|
1288
|
+
fabric.util.addTransformToObject(instance, this._activeObject.calcOwnMatrix());
|
|
1272
1289
|
return originalValues;
|
|
1273
1290
|
}
|
|
1274
1291
|
else {
|
package/src/controls.actions.js
CHANGED
package/src/controls.render.js
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
), xSizeBy2 = xSize / 2, ySizeBy2 = ySize / 2;
|
|
79
79
|
ctx.save();
|
|
80
80
|
ctx.fillStyle = styleOverride.cornerColor || fabricObject.cornerColor;
|
|
81
|
-
ctx.strokeStyle = styleOverride.
|
|
81
|
+
ctx.strokeStyle = styleOverride.cornerStrokeColor || fabricObject.cornerStrokeColor;
|
|
82
82
|
// this is still wrong
|
|
83
83
|
ctx.lineWidth = 1;
|
|
84
84
|
ctx.translate(left, top);
|
|
@@ -451,15 +451,21 @@
|
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
if (target) {
|
|
454
|
-
|
|
455
|
-
this.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
454
|
+
if (target.selectable && target !== this._activeObject && target.activeOn === 'up') {
|
|
455
|
+
this.setActiveObject(target, e);
|
|
456
|
+
shouldRender = true;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
var corner = target._findTargetCorner(
|
|
460
|
+
this.getPointer(e, true),
|
|
461
|
+
fabric.util.isTouchEvent(e)
|
|
462
|
+
);
|
|
463
|
+
var control = target.controls[corner],
|
|
464
|
+
mouseUpHandler = control && control.getMouseUpHandler(e, target, control);
|
|
465
|
+
if (mouseUpHandler) {
|
|
466
|
+
var pointer = this.getPointer(e);
|
|
467
|
+
mouseUpHandler(e, transform, pointer.x, pointer.y);
|
|
468
|
+
}
|
|
463
469
|
}
|
|
464
470
|
target.isMoving = false;
|
|
465
471
|
}
|
|
@@ -715,7 +721,7 @@
|
|
|
715
721
|
|
|
716
722
|
if (target) {
|
|
717
723
|
var alreadySelected = target === this._activeObject;
|
|
718
|
-
if (target.selectable) {
|
|
724
|
+
if (target.selectable && target.activeOn === 'down') {
|
|
719
725
|
this.setActiveObject(target, e);
|
|
720
726
|
}
|
|
721
727
|
var corner = target._findTargetCorner(
|
|
@@ -924,7 +930,6 @@
|
|
|
924
930
|
transform = this._currentTransform;
|
|
925
931
|
|
|
926
932
|
transform.reset = false;
|
|
927
|
-
transform.target.isMoving = true;
|
|
928
933
|
transform.shiftKey = e.shiftKey;
|
|
929
934
|
transform.altKey = e[this.centeredKey];
|
|
930
935
|
|
|
@@ -948,6 +953,7 @@
|
|
|
948
953
|
actionPerformed = actionHandler(e, transform, x, y);
|
|
949
954
|
}
|
|
950
955
|
if (action === 'drag' && actionPerformed) {
|
|
956
|
+
transform.target.isMoving = true;
|
|
951
957
|
this.setCursor(transform.target.moveCursor || this.moveCursor);
|
|
952
958
|
}
|
|
953
959
|
transform.actionPerformed = transform.actionPerformed || actionPerformed;
|
|
@@ -156,17 +156,27 @@
|
|
|
156
156
|
* @chainable
|
|
157
157
|
*/
|
|
158
158
|
addWithUpdate: function(object) {
|
|
159
|
+
var nested = !!this.group;
|
|
159
160
|
this._restoreObjectsState();
|
|
160
161
|
fabric.util.resetObjectTransform(this);
|
|
161
162
|
if (object) {
|
|
163
|
+
if (nested) {
|
|
164
|
+
// if this group is inside another group, we need to pre transform the object
|
|
165
|
+
fabric.util.removeTransformFromObject(object, this.group.calcTransformMatrix());
|
|
166
|
+
}
|
|
162
167
|
this._objects.push(object);
|
|
163
168
|
object.group = this;
|
|
164
169
|
object._set('canvas', this.canvas);
|
|
165
170
|
}
|
|
166
171
|
this._calcBounds();
|
|
167
172
|
this._updateObjectsCoords();
|
|
168
|
-
this.setCoords();
|
|
169
173
|
this.dirty = true;
|
|
174
|
+
if (nested) {
|
|
175
|
+
this.group.addWithUpdate();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.setCoords();
|
|
179
|
+
}
|
|
170
180
|
return this;
|
|
171
181
|
},
|
|
172
182
|
|
|
@@ -357,12 +367,21 @@
|
|
|
357
367
|
|
|
358
368
|
/**
|
|
359
369
|
* Restores original state of each of group objects (original state is that which was before group was created).
|
|
370
|
+
* if the nested boolean is true, the original state will be restored just for the
|
|
371
|
+
* first group and not for all the group chain
|
|
360
372
|
* @private
|
|
373
|
+
* @param {Boolean} nested tell the function to restore object state up to the parent group and not more
|
|
361
374
|
* @return {fabric.Group} thisArg
|
|
362
375
|
* @chainable
|
|
363
376
|
*/
|
|
364
377
|
_restoreObjectsState: function() {
|
|
365
|
-
this.
|
|
378
|
+
var groupMatrix = this.calcOwnMatrix();
|
|
379
|
+
this._objects.forEach(function(object) {
|
|
380
|
+
// instead of using _this = this;
|
|
381
|
+
fabric.util.addTransformToObject(object, groupMatrix);
|
|
382
|
+
delete object.group;
|
|
383
|
+
object.setCoords();
|
|
384
|
+
});
|
|
366
385
|
return this;
|
|
367
386
|
},
|
|
368
387
|
|
|
@@ -371,37 +390,20 @@
|
|
|
371
390
|
* i.e. it tells you what would happen if the supplied object was in
|
|
372
391
|
* the group, and then the group was destroyed. It mutates the supplied
|
|
373
392
|
* object.
|
|
393
|
+
* Warning: this method is not useful anymore, it has been kept to no break the api.
|
|
394
|
+
* is not used in the fabricJS codebase
|
|
395
|
+
* this method will be reduced to using the utility.
|
|
396
|
+
* @private
|
|
397
|
+
* @deprecated
|
|
374
398
|
* @param {fabric.Object} object
|
|
399
|
+
* @param {Array} parentMatrix parent transformation
|
|
375
400
|
* @return {fabric.Object} transformedObject
|
|
376
401
|
*/
|
|
377
|
-
realizeTransform: function(object) {
|
|
378
|
-
|
|
379
|
-
options = fabric.util.qrDecompose(matrix),
|
|
380
|
-
center = new fabric.Point(options.translateX, options.translateY);
|
|
381
|
-
object.flipX = false;
|
|
382
|
-
object.flipY = false;
|
|
383
|
-
object.set('scaleX', options.scaleX);
|
|
384
|
-
object.set('scaleY', options.scaleY);
|
|
385
|
-
object.skewX = options.skewX;
|
|
386
|
-
object.skewY = options.skewY;
|
|
387
|
-
object.angle = options.angle;
|
|
388
|
-
object.setPositionByOrigin(center, 'center', 'center');
|
|
402
|
+
realizeTransform: function(object, parentMatrix) {
|
|
403
|
+
fabric.util.addTransformToObject(object, parentMatrix);
|
|
389
404
|
return object;
|
|
390
405
|
},
|
|
391
406
|
|
|
392
|
-
/**
|
|
393
|
-
* Restores original state of a specified object in group
|
|
394
|
-
* @private
|
|
395
|
-
* @param {fabric.Object} object
|
|
396
|
-
* @return {fabric.Group} thisArg
|
|
397
|
-
*/
|
|
398
|
-
_restoreObjectState: function(object) {
|
|
399
|
-
this.realizeTransform(object);
|
|
400
|
-
delete object.group;
|
|
401
|
-
object.setCoords();
|
|
402
|
-
return this;
|
|
403
|
-
},
|
|
404
|
-
|
|
405
407
|
/**
|
|
406
408
|
* Destroys a group (restoring state of its objects)
|
|
407
409
|
* @return {fabric.Group} thisArg
|
|
@@ -474,19 +476,20 @@
|
|
|
474
476
|
_calcBounds: function(onlyWidthHeight) {
|
|
475
477
|
var aX = [],
|
|
476
478
|
aY = [],
|
|
477
|
-
o, prop,
|
|
479
|
+
o, prop, coords,
|
|
478
480
|
props = ['tr', 'br', 'bl', 'tl'],
|
|
479
481
|
i = 0, iLen = this._objects.length,
|
|
480
482
|
j, jLen = props.length;
|
|
481
483
|
|
|
482
484
|
for ( ; i < iLen; ++i) {
|
|
483
485
|
o = this._objects[i];
|
|
484
|
-
|
|
486
|
+
coords = o.calcACoords();
|
|
485
487
|
for (j = 0; j < jLen; j++) {
|
|
486
488
|
prop = props[j];
|
|
487
|
-
aX.push(
|
|
488
|
-
aY.push(
|
|
489
|
+
aX.push(coords[prop].x);
|
|
490
|
+
aY.push(coords[prop].y);
|
|
489
491
|
}
|
|
492
|
+
o.aCoords = coords;
|
|
490
493
|
}
|
|
491
494
|
|
|
492
495
|
this._getBounds(aX, aY, onlyWidthHeight);
|
|
@@ -92,6 +92,15 @@
|
|
|
92
92
|
*/
|
|
93
93
|
stateProperties: fabric.Object.prototype.stateProperties.concat('cropX', 'cropY'),
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* List of properties to consider when checking if cache needs refresh
|
|
97
|
+
* Those properties are checked by statefullCache ON ( or lazy mode if we want ) or from single
|
|
98
|
+
* calls to Object.set(key, value). If the key is in this list, the object is marked as dirty
|
|
99
|
+
* and refreshed at the next render
|
|
100
|
+
* @type Array
|
|
101
|
+
*/
|
|
102
|
+
cacheProperties: fabric.Object.prototype.cacheProperties.concat('cropX', 'cropY'),
|
|
103
|
+
|
|
95
104
|
/**
|
|
96
105
|
* key used to retrieve the texture representing this image
|
|
97
106
|
* @since 2.0.0
|
|
@@ -127,7 +136,11 @@
|
|
|
127
136
|
|
|
128
137
|
/**
|
|
129
138
|
* Constructor
|
|
130
|
-
*
|
|
139
|
+
* Image can be initialized with any canvas drawable or a string.
|
|
140
|
+
* The string should be a url and will be loaded as an image.
|
|
141
|
+
* Canvas and Image element work out of the box, while videos require extra code to work.
|
|
142
|
+
* Please check video element events for seeking.
|
|
143
|
+
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | String} element Image element
|
|
131
144
|
* @param {Object} [options] Options object
|
|
132
145
|
* @param {function} [callback] callback function to call after eventual filters applied.
|
|
133
146
|
* @return {fabric.Image} thisArg
|
|
@@ -552,7 +565,7 @@
|
|
|
552
565
|
sH = min(h * scaleY, elHeight - sY),
|
|
553
566
|
x = -w / 2, y = -h / 2,
|
|
554
567
|
maxDestW = min(w, elWidth / scaleX - cropX),
|
|
555
|
-
maxDestH = min(h, elHeight /
|
|
568
|
+
maxDestH = min(h, elHeight / scaleY - cropY);
|
|
556
569
|
|
|
557
570
|
elementToDraw && ctx.drawImage(elementToDraw, sX, sY, sW, sH, x, y, maxDestW, maxDestH);
|
|
558
571
|
},
|
|
@@ -471,8 +471,9 @@
|
|
|
471
471
|
* High level function to know the color of the cursor.
|
|
472
472
|
* the currentChar is the one that precedes the cursor
|
|
473
473
|
* Returns color (fill) of char at the current cursor
|
|
474
|
-
*
|
|
475
|
-
*
|
|
474
|
+
* if the text object has a pattern or gradient for filler, it will return that.
|
|
475
|
+
* Unused by the library, is for the end user
|
|
476
|
+
* @return {String | fabric.Gradient | fabric.Pattern} Character color (fill)
|
|
476
477
|
*/
|
|
477
478
|
getCurrentCharColor: function() {
|
|
478
479
|
var cp = this._getCurrentCharIndex();
|
|
@@ -571,6 +571,17 @@
|
|
|
571
571
|
*/
|
|
572
572
|
paintFirst: 'fill',
|
|
573
573
|
|
|
574
|
+
/**
|
|
575
|
+
* When 'down', object is set to active on mousedown/touchstart
|
|
576
|
+
* When 'up', object is set to active on mouseup/touchend
|
|
577
|
+
* Experimental. Let's see if this breaks anything before supporting officially
|
|
578
|
+
* @private
|
|
579
|
+
* since 4.4.0
|
|
580
|
+
* @type String
|
|
581
|
+
* @default 'down'
|
|
582
|
+
*/
|
|
583
|
+
activeOn: 'down',
|
|
584
|
+
|
|
574
585
|
/**
|
|
575
586
|
* List of properties to consider when checking if state
|
|
576
587
|
* of an object is changed (fabric.Object#hasStateChanged)
|
|
@@ -839,7 +850,7 @@
|
|
|
839
850
|
strokeLineCap: this.strokeLineCap,
|
|
840
851
|
strokeDashOffset: this.strokeDashOffset,
|
|
841
852
|
strokeLineJoin: this.strokeLineJoin,
|
|
842
|
-
|
|
853
|
+
strokeUniform: this.strokeUniform,
|
|
843
854
|
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
|
|
844
855
|
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
|
|
845
856
|
scaleY: toFixed(this.scaleY, NUM_FRACTION_DIGITS),
|
|
@@ -991,7 +1002,6 @@
|
|
|
991
1002
|
this.group.set('dirty', true);
|
|
992
1003
|
}
|
|
993
1004
|
}
|
|
994
|
-
|
|
995
1005
|
return this;
|
|
996
1006
|
},
|
|
997
1007
|
|
package/src/shapes/text.class.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
var
|
|
14
|
-
'fontFamily fontWeight fontSize text underline overline linethrough' +
|
|
15
|
-
' textAlign fontStyle lineHeight textBackgroundColor charSpacing styles path'.split(' ');
|
|
13
|
+
var additionalProps =
|
|
14
|
+
('fontFamily fontWeight fontSize text underline overline linethrough' +
|
|
15
|
+
' textAlign fontStyle lineHeight textBackgroundColor charSpacing styles path').split(' ');
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Text class
|
|
@@ -172,13 +172,13 @@
|
|
|
172
172
|
* as well as for history (undo/redo) purposes
|
|
173
173
|
* @type Array
|
|
174
174
|
*/
|
|
175
|
-
stateProperties: fabric.Object.prototype.stateProperties.concat(
|
|
175
|
+
stateProperties: fabric.Object.prototype.stateProperties.concat(additionalProps),
|
|
176
176
|
|
|
177
177
|
/**
|
|
178
178
|
* List of properties to consider when checking if cache needs refresh
|
|
179
179
|
* @type Array
|
|
180
180
|
*/
|
|
181
|
-
cacheProperties: fabric.Object.prototype.cacheProperties.concat(
|
|
181
|
+
cacheProperties: fabric.Object.prototype.cacheProperties.concat(additionalProps),
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
184
|
* When defined, an object is rendered via stroke and this property specifies its color.
|
|
@@ -196,6 +196,14 @@
|
|
|
196
196
|
*/
|
|
197
197
|
shadow: null,
|
|
198
198
|
|
|
199
|
+
/**
|
|
200
|
+
* fabric.Path that the text can follow.
|
|
201
|
+
* This feature is in BETA.
|
|
202
|
+
* @type fabric.Path
|
|
203
|
+
* @default
|
|
204
|
+
*/
|
|
205
|
+
path: null,
|
|
206
|
+
|
|
199
207
|
/**
|
|
200
208
|
* @private
|
|
201
209
|
*/
|
|
@@ -727,6 +735,8 @@
|
|
|
727
735
|
if (positionInPath > totalPathLength) {
|
|
728
736
|
positionInPath %= totalPathLength;
|
|
729
737
|
}
|
|
738
|
+
// it would probably much fater to send all the grapheme position for a line
|
|
739
|
+
// and calculate path position/angle at once.
|
|
730
740
|
this._setGraphemeOnPath(positionInPath, graphemeInfo, startingPoint);
|
|
731
741
|
}
|
|
732
742
|
lineBounds[i] = graphemeInfo;
|
|
@@ -758,11 +768,10 @@
|
|
|
758
768
|
path = this.path;
|
|
759
769
|
|
|
760
770
|
// we are at currentPositionOnPath. we want to know what point on the path is.
|
|
761
|
-
var
|
|
762
|
-
|
|
763
|
-
graphemeInfo.
|
|
764
|
-
graphemeInfo.
|
|
765
|
-
graphemeInfo.angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
|
771
|
+
var info = fabric.util.getPointOnPath(path.path, centerPosition, path.segmentsInfo);
|
|
772
|
+
graphemeInfo.renderLeft = info.x - startingPoint.x;
|
|
773
|
+
graphemeInfo.renderTop = info.y - startingPoint.y;
|
|
774
|
+
graphemeInfo.angle = info.angle;
|
|
766
775
|
},
|
|
767
776
|
|
|
768
777
|
/**
|
|
@@ -1302,13 +1311,15 @@
|
|
|
1302
1311
|
(currentDecoration !== lastDecoration || currentFill !== lastFill || _size !== size || _dy !== dy)
|
|
1303
1312
|
&& boxWidth > 0
|
|
1304
1313
|
) {
|
|
1305
|
-
lastDecoration && lastFill
|
|
1314
|
+
if (lastDecoration && lastFill) {
|
|
1315
|
+
ctx.fillStyle = lastFill;
|
|
1306
1316
|
ctx.fillRect(
|
|
1307
1317
|
leftOffset + lineLeftOffset + boxStart,
|
|
1308
1318
|
top + this.offsets[type] * size + dy,
|
|
1309
1319
|
boxWidth,
|
|
1310
1320
|
this.fontSize / 15
|
|
1311
1321
|
);
|
|
1322
|
+
}
|
|
1312
1323
|
boxStart = charBox.left;
|
|
1313
1324
|
boxWidth = charBox.width;
|
|
1314
1325
|
lastDecoration = currentDecoration;
|
|
@@ -1411,9 +1422,11 @@
|
|
|
1411
1422
|
'textAlign',
|
|
1412
1423
|
'textBackgroundColor',
|
|
1413
1424
|
'charSpacing',
|
|
1425
|
+
'path'
|
|
1414
1426
|
].concat(propertiesToInclude);
|
|
1415
1427
|
var obj = this.callSuper('toObject', additionalProperties);
|
|
1416
1428
|
obj.styles = clone(this.styles, true);
|
|
1429
|
+
obj.path = this.path && this.path.toObject();
|
|
1417
1430
|
return obj;
|
|
1418
1431
|
},
|
|
1419
1432
|
|
|
@@ -1569,11 +1582,23 @@
|
|
|
1569
1582
|
* Returns fabric.Text instance from an object representation
|
|
1570
1583
|
* @static
|
|
1571
1584
|
* @memberOf fabric.Text
|
|
1572
|
-
* @param {Object} object Object to create an instance from
|
|
1585
|
+
* @param {Object} object plain js Object to create an instance from
|
|
1573
1586
|
* @param {Function} [callback] Callback to invoke when an fabric.Text instance is created
|
|
1574
1587
|
*/
|
|
1575
1588
|
fabric.Text.fromObject = function(object, callback) {
|
|
1576
|
-
|
|
1589
|
+
var objectCopy = clone(object), path = object.path;
|
|
1590
|
+
delete objectCopy.path;
|
|
1591
|
+
return fabric.Object._fromObject('Text', objectCopy, function(textInstance) {
|
|
1592
|
+
if (path) {
|
|
1593
|
+
fabric.Object._fromObject('Path', path, function(pathInstance) {
|
|
1594
|
+
textInstance.set('path', pathInstance);
|
|
1595
|
+
callback(textInstance);
|
|
1596
|
+
}, 'path');
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
callback(textInstance);
|
|
1600
|
+
}
|
|
1601
|
+
}, 'text');
|
|
1577
1602
|
};
|
|
1578
1603
|
|
|
1579
1604
|
fabric.Text.genericFonts = ['sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'];
|