You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2018/12/12 09:43:36 UTC
[openmeetings] branch 4.0.x updated: [OPENMEETINGS-1954] fabric.js
is updated
This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch 4.0.x
in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/4.0.x by this push:
new 3872a17 [OPENMEETINGS-1954] fabric.js is updated
3872a17 is described below
commit 3872a17956cbf1bc66111bfb9ad707cd05c93d15
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Wed Dec 12 16:43:26 2018 +0700
[OPENMEETINGS-1954] fabric.js is updated
---
.../org/apache/openmeetings/web/room/wb/fabric.js | 790 ++++++++++++---------
1 file changed, 443 insertions(+), 347 deletions(-)
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
index f0f36b0..2419e14 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
@@ -2,7 +2,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '2.4.1' };
+var fabric = fabric || { version: '2.4.5' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -34,7 +34,8 @@ else {
* True when in environment that supports touch events
* @type boolean
*/
-fabric.isTouchSupported = 'ontouchstart' in fabric.window;
+fabric.isTouchSupported = 'ontouchstart' in fabric.window || 'ontouchstart' in fabric.document ||
+ (fabric.window && fabric.window.navigator && fabric.window.navigator.maxTouchPoints > 0);
/**
* True when in environment that's probably Node.js
@@ -53,7 +54,7 @@ fabric.SHARED_ATTRIBUTES = [
'transform',
'fill', 'fill-opacity', 'fill-rule',
'opacity',
- 'stroke', 'stroke-dasharray', 'stroke-linecap',
+ 'stroke', 'stroke-dasharray', 'stroke-linecap', 'stroke-dashoffset',
'stroke-linejoin', 'stroke-miterlimit',
'stroke-opacity', 'stroke-width',
'id', 'paint-order',
@@ -1082,7 +1083,7 @@ fabric.CommonMethods = {
*/
groupSVGElements: function(elements, options, path) {
var object;
- if (elements.length === 1) {
+ if (elements && elements.length === 1) {
return elements[0];
}
if (options) {
@@ -1179,7 +1180,7 @@ fabric.CommonMethods = {
* @return {CanvasElement} initialized canvas element
*/
copyCanvasElement: function(canvas) {
- var newCanvas = fabric.document.createElement('canvas');
+ var newCanvas = fabric.util.createCanvasElement();
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;
newCanvas.getContext('2d').drawImage(canvas, 0, 0);
@@ -1789,7 +1790,10 @@ fabric.CommonMethods = {
(function() {
/**
* Copies all enumerable properties of one js object to another
+ * this does not and cannot compete with generic utils.
* Does not clone or extend fabric.Object subclasses.
+ * This is mostly for internal use and has extra handling for fabricJS objects
+ * it skips the canvas property in deep cloning.
* @memberOf fabric.util.object
* @param {Object} destination Where to copy to
* @param {Object} source Where to copy from
@@ -1813,7 +1817,10 @@ fabric.CommonMethods = {
}
else if (source && typeof source === 'object') {
for (var property in source) {
- if (source.hasOwnProperty(property)) {
+ if (property === 'canvas') {
+ destination[property] = extend({ }, source[property]);
+ }
+ else if (source.hasOwnProperty(property)) {
destination[property] = extend({ }, source[property], deep);
}
}
@@ -2721,24 +2728,6 @@ fabric.CommonMethods = {
return url + (/\?/.test(url) ? '&' : '?') + param;
}
- var makeXHR = (function() {
- var factories = [
- function() { return new fabric.window.XMLHttpRequest(); },
- function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
- function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
- function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); }
- ];
- for (var i = factories.length; i--; ) {
- try {
- var req = factories[i]();
- if (req) {
- return factories[i];
- }
- }
- catch (err) { }
- }
- })();
-
function emptyFn() { }
/**
@@ -2757,7 +2746,7 @@ fabric.CommonMethods = {
var method = options.method ? options.method.toUpperCase() : 'GET',
onComplete = options.onComplete || function() { },
- xhr = makeXHR(),
+ xhr = new fabric.window.XMLHttpRequest(),
body = options.body || options.parameters;
/** @ignore */
@@ -3398,6 +3387,7 @@ if (typeof console !== 'undefined') {
'letter-spacing': 'charSpacing',
'paint-order': 'paintFirst',
'stroke-dasharray': 'strokeDashArray',
+ 'stroke-dashoffset': 'strokeDashOffset',
'stroke-linecap': 'strokeLineCap',
'stroke-linejoin': 'strokeLineJoin',
'stroke-miterlimit': 'strokeMiterLimit',
@@ -3444,9 +3434,7 @@ if (typeof console !== 'undefined') {
value = null;
}
else {
- value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
- return parseFloat(n);
- });
+ value = value.replace(/,/g, ' ').split(/\s+/).map(parseFloat);
}
}
else if (attr === 'transformMatrix') {
@@ -3489,6 +3477,9 @@ if (typeof console !== 'undefined') {
value = 'stroke';
}
}
+ else if (attr === 'href' || attr === 'xlink:href') {
+ return value;
+ }
else {
parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
}
@@ -3589,14 +3580,7 @@ if (typeof console !== 'undefined') {
}
// identity matrix
- var iMatrix = [
- 1, // a
- 0, // b
- 0, // c
- 1, // d
- 0, // e
- 0 // f
- ],
+ var iMatrix = fabric.iMatrix,
// == begin transform regexp
number = fabric.reNum,
@@ -3802,7 +3786,7 @@ if (typeof console !== 'undefined') {
/**
* @private
- * to support IE8 missing getElementById on SVGdocument
+ * to support IE8 missing getElementById on SVGdocument and on node xmlDOM
*/
function elementById(doc, id) {
var el;
@@ -3824,7 +3808,6 @@ if (typeof console !== 'undefined') {
*/
function parseUseDirectives(doc) {
var nodelist = _getMultipleNodes(doc, ['use', 'svg:use']), i = 0;
-
while (nodelist.length && i < nodelist.length) {
var el = nodelist[i],
xlink = (el.getAttribute('xlink:href') || el.getAttribute('href')).substr(1),
@@ -4076,6 +4059,28 @@ if (typeof console !== 'undefined') {
}, clone(options), reviver, parsingOptions);
};
+ function recursivelyParseGradientsXlink(doc, gradient) {
+ var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'],
+ xlinkAttr = 'xlink:href',
+ xLink = gradient.getAttribute(xlinkAttr).substr(1),
+ referencedGradient = elementById(doc, xLink);
+ if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) {
+ recursivelyParseGradientsXlink(doc, referencedGradient);
+ }
+ gradientsAttrs.forEach(function(attr) {
+ if (!gradient.hasAttribute(attr)) {
+ gradient.setAttribute(attr, referencedGradient.getAttribute(attr));
+ }
+ });
+ if (!gradient.children.length) {
+ var referenceClone = referencedGradient.cloneNode(true);
+ while (referenceClone.firstChild) {
+ gradient.appendChild(referenceClone.firstChild);
+ }
+ }
+ gradient.removeAttribute(xlinkAttr);
+ }
+
var reFontDeclaration = new RegExp(
'(normal|italic)?\\s*(normal|small-caps)?\\s*' +
'(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' +
@@ -4137,26 +4142,14 @@ if (typeof console !== 'undefined') {
'svg:linearGradient',
'svg:radialGradient'],
elList = _getMultipleNodes(doc, tagArray),
- el, j = 0, id, xlink,
- gradientDefs = { }, idsToXlinkMap = { };
+ el, j = 0, gradientDefs = { };
j = elList.length;
-
while (j--) {
el = elList[j];
- xlink = el.getAttribute('xlink:href');
- id = el.getAttribute('id');
- if (xlink) {
- idsToXlinkMap[id] = xlink.substr(1);
- }
- gradientDefs[id] = el;
- }
-
- for (id in idsToXlinkMap) {
- var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
- el = gradientDefs[id];
- while (el2.firstChild) {
- el.appendChild(el2.firstChild);
+ if (el.getAttribute('xlink:href')) {
+ recursivelyParseGradientsXlink(doc, el);
}
+ gradientDefs[el.getAttribute('id')] = el;
}
return gradientDefs;
},
@@ -4178,7 +4171,7 @@ if (typeof console !== 'undefined') {
var value,
parentAttributes = { },
- fontSize;
+ fontSize, parentFontSize;
if (typeof svgUid === 'undefined') {
svgUid = element.getAttribute('svgUid');
@@ -4200,8 +4193,11 @@ if (typeof console !== 'undefined') {
ownAttributes = extend(ownAttributes,
extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
- fontSize = (parentAttributes && parentAttributes.fontSize ) ||
- ownAttributes['font-size'] || fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ fontSize = parentFontSize = parentAttributes.fontSize || fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ if (ownAttributes['font-size']) {
+ // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers.
+ ownAttributes['font-size'] = fontSize = parseUnit(ownAttributes['font-size'], parentFontSize);
+ }
var normalizedAttr, normalizedValue, normalizedStyle = {};
for (var attr in ownAttributes) {
@@ -4466,7 +4462,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
var _options;
_this.resolveGradient(obj, 'fill');
_this.resolveGradient(obj, 'stroke');
- if (obj instanceof fabric.Image) {
+ if (obj instanceof fabric.Image && obj._originalElement) {
_options = obj.parsePreserveAspectRatioAttribute(el);
}
obj._removeTransformMatrix(_options);
@@ -6556,6 +6552,8 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
toFixed = fabric.util.toFixed,
transformPoint = fabric.util.transformPoint,
invertTransform = fabric.util.invertTransform,
+ getNodeCanvas = fabric.util.getNodeCanvas,
+ createCanvasElement = fabric.util.createCanvasElement,
CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
@@ -6601,6 +6599,9 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
* and "backgroundImageStretch" properties are deprecated since 1.3.9.
* Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
+ * since 2.4.0 image caching is active, please when putting an image as background, add to the
+ * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom
+ * vale. As an alternative you can disable image objectCaching
* @type fabric.Image
* @default
*/
@@ -6621,6 +6622,9 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* <b>Backwards incompatibility note:</b> The "overlayImageLeft"
* and "overlayImageTop" properties are deprecated since 1.3.9.
* Use {@link fabric.Image#left} and {@link fabric.Image#top}.
+ * since 2.4.0 image caching is active, please when putting an image as overlay, add to the
+ * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom
+ * vale. As an alternative you can disable image objectCaching
* @type fabric.Image
* @default
*/
@@ -6924,6 +6928,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* crossOrigin: 'anonymous'
* });
*/
+ // TODO: fix stretched examples
setBackgroundImage: function (image, callback, options) {
return this.__setBgOverlayImage('backgroundImage', image, callback, options);
},
@@ -7001,13 +7006,18 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
__setBgOverlayImage: function(property, image, callback, options) {
if (typeof image === 'string') {
fabric.util.loadImage(image, function(img) {
- img && (this[property] = new fabric.Image(img, options));
+ if (img) {
+ var instance = new fabric.Image(img, options);
+ this[property] = instance;
+ instance.canvas = this;
+ }
callback && callback(img);
}, this, options && options.crossOrigin);
}
else {
options && image.setOptions(options);
this[property] = image;
+ image && (image.canvas = this);
callback && callback(image);
}
@@ -7032,7 +7042,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* @private
*/
_createCanvasElement: function() {
- var element = fabric.util.createCanvasElement();
+ var element = createCanvasElement();
if (!element) {
throw CANVAS_INIT_ERROR;
}
@@ -7050,20 +7060,21 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* @param {Object} [options] Options object
*/
_initOptions: function (options) {
+ var lowerCanvasEl = this.lowerCanvasEl;
this._setOptions(options);
- this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
- this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
+ this.width = this.width || parseInt(lowerCanvasEl.width, 10) || 0;
+ this.height = this.height || parseInt(lowerCanvasEl.height, 10) || 0;
if (!this.lowerCanvasEl.style) {
return;
}
- this.lowerCanvasEl.width = this.width;
- this.lowerCanvasEl.height = this.height;
+ lowerCanvasEl.width = this.width;
+ lowerCanvasEl.height = this.height;
- this.lowerCanvasEl.style.width = this.width + 'px';
- this.lowerCanvasEl.style.height = this.height + 'px';
+ lowerCanvasEl.style.width = this.width + 'px';
+ lowerCanvasEl.style.height = this.height + 'px';
this.viewportTransform = this.viewportTransform.slice();
},
@@ -7816,17 +7827,20 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
*/
toSVG: function(options, reviver) {
options || (options = { });
-
+ options.reviver = reviver;
var markup = [];
this._setSVGPreamble(markup, options);
this._setSVGHeader(markup, options);
-
+ if (this.clipPath) {
+ markup.push('<g clip-path="url(#' + this.clipPath.clipPathId + ')" >\n');
+ }
this._setSVGBgOverlayColor(markup, 'backgroundColor');
this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
-
this._setSVGObjects(markup, reviver);
-
+ if (this.clipPath) {
+ markup.push('</g>\n');
+ }
this._setSVGBgOverlayColor(markup, 'overlayColor');
this._setSVGBgOverlayImage(markup, 'overlayImage', reviver);
@@ -7889,10 +7903,22 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
'<defs>\n',
this.createSVGFontFacesMarkup(),
this.createSVGRefElementsMarkup(),
+ this.createSVGClipPathMarkup(options),
'</defs>\n'
);
},
+ createSVGClipPathMarkup: function(options) {
+ var clipPath = this.clipPath;
+ if (clipPath) {
+ clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
+ return '<clipPath id="' + clipPath.clipPathId + '" >\n' +
+ this.clipPath.toClipPathSVG(options.reviver) +
+ '</clipPath>\n';
+ }
+ return '';
+ },
+
/**
* Creates markup containing SVG referenced elements like patterns, gradients etc.
* @return {String}
@@ -8311,7 +8337,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
* `null` if canvas element or context can not be initialized
*/
supports: function (methodName) {
- var el = fabric.util.createCanvasElement();
+ var el = createCanvasElement();
if (!el || !el.getContext) {
return null;
@@ -8366,11 +8392,11 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
if (fabric.isLikelyNode) {
fabric.StaticCanvas.prototype.createPNGStream = function() {
- var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
+ var impl = getNodeCanvas(this.lowerCanvasEl);
return impl && impl.createPNGStream();
};
fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
- var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
+ var impl = getNodeCanvas(this.lowerCanvasEl);
return impl && impl.createJPEGStream(opts);
};
}
@@ -9578,6 +9604,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
},
renderTopLayer: function(ctx) {
+ ctx.save();
if (this.isDrawingMode && this._isCurrentlyDrawing) {
this.freeDrawingBrush && this.freeDrawingBrush._render();
this.contextTopDirty = true;
@@ -9587,6 +9614,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this._drawSelection(ctx);
this.contextTopDirty = true;
}
+ ctx.restore();
},
/**
@@ -9824,8 +9852,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
*/
- _getActionFromCorner: function(target, corner, e) {
- if (!corner) {
+ _getActionFromCorner: function(alreadySelected, corner, e) {
+ if (!corner || !alreadySelected) {
return 'drag';
}
@@ -9848,14 +9876,14 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @param {Event} e Event object
* @param {fabric.Object} target
*/
- _setupCurrentTransform: function (e, target) {
+ _setupCurrentTransform: function (e, target, alreadySelected) {
if (!target) {
return;
}
var pointer = this.getPointer(e),
corner = target._findTargetCorner(this.getPointer(e, true)),
- action = this._getActionFromCorner(target, corner, e),
+ action = this._getActionFromCorner(alreadySelected, corner, e),
origin = this._getOriginFromCorner(target, corner);
this._currentTransform = {
@@ -10328,8 +10356,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* Method that determines what object we are clicking on
* the skipGroup parameter is for internal use, is needed for shift+click action
+ * 11/09/2018 TODO: would be cool if findTarget could discern between being a full target
+ * or the outside part of the corner.
* @param {Event} e mouse event
* @param {Boolean} skipGroup when true, activeGroup is skipped and only objects are traversed through
+ * @return {fabric.Object} the target found
*/
findTarget: function (e, skipGroup) {
if (this.skipTargetFind) {
@@ -10374,15 +10405,20 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
},
/**
+ * Checks point is inside the object.
+ * @param {Object} [pointer] x,y object of point coordinates we want to check.
+ * @param {fabric.Object} obj Object to test against
+ * @param {Object} [globalPointer] x,y object of point coordinates relative to canvas used to search per pixel target.
+ * @return {Boolean} true if point is contained within an area of given object
* @private
*/
- _checkTarget: function(pointer, obj) {
+ _checkTarget: function(pointer, obj, globalPointer) {
if (obj &&
obj.visible &&
obj.evented &&
this.containsPoint(null, obj, pointer)){
if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
- var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
+ var isTransparent = this.isTargetTransparent(obj, globalPointer.x, globalPointer.y);
if (!isTransparent) {
return true;
}
@@ -10394,20 +10430,25 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
},
/**
+ * Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted
+ * @param {Array} [objects] objects array to look into
+ * @param {Object} [pointer] x,y object of point coordinates we want to check.
+ * @return {fabric.Object} object that contains pointer
* @private
*/
_searchPossibleTargets: function(objects, pointer) {
-
// Cache all targets where their bounding box contains point.
- var target, i = objects.length, normalizedPointer, subTarget;
+ var target, i = objects.length, subTarget;
// Do not check for currently grouped objects, since we check the parent group itself.
// until we call this function specifically to search inside the activeGroup
while (i--) {
- if (this._checkTarget(pointer, objects[i])) {
+ var objToCheck = objects[i];
+ var pointerToUse = objToCheck.group && objToCheck.group.type !== 'activeSelection' ?
+ this._normalizePointer(objToCheck.group, pointer) : pointer;
+ if (this._checkTarget(pointerToUse, objToCheck, pointer)) {
target = objects[i];
if (target.subTargetCheck && target instanceof fabric.Group) {
- normalizedPointer = this._normalizePointer(target, pointer);
- subTarget = this._searchPossibleTargets(target._objects, normalizedPointer);
+ subTarget = this._searchPossibleTargets(target._objects, pointer);
subTarget && this.targets.push(subTarget);
}
break;
@@ -11492,11 +11533,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
}
if (target) {
+ var alreadySelected = target === this._activeObject;
if (target.selectable) {
this.setActiveObject(target, e);
}
if (target === this._activeObject && (target.__corner || !shouldGroup)) {
- this._setupCurrentTransform(e, target);
+ this._setupCurrentTransform(e, target, alreadySelected);
}
}
this._handleEvent(e, 'down');
@@ -11881,9 +11923,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_shouldGroup: function(e, target) {
var activeObject = this._activeObject;
-
return activeObject && this._isSelectionKeyPressed(e) && target && target.selectable && this.selection &&
- (activeObject !== target || activeObject.type === 'activeSelection');
+ (activeObject !== target || activeObject.type === 'activeSelection') && !target.onSelect({ e: e });
},
/**
@@ -11893,6 +11934,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_handleGrouping: function (e, target) {
var activeObject = this._activeObject;
+ // avoid multi select when shift click on a corner
if (activeObject.__corner) {
return;
}
@@ -11900,7 +11942,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
// if it's a group, find target again, using activeGroup objects
target = this.findTarget(e, true);
// if even object is not found or we are on activeObjectCorner, bail out
- if (!target) {
+ if (!target || !target.selectable) {
return;
}
}
@@ -11965,7 +12007,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_groupSelectedObjects: function (e) {
- var group = this._collectObjects(),
+ var group = this._collectObjects(e),
aGroup;
// do not create group for 1 element only
@@ -11983,7 +12025,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
*/
- _collectObjects: function() {
+ _collectObjects: function(e) {
var group = [],
currentObject,
x1 = this._groupSelector.ex,
@@ -11998,7 +12040,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
for (var i = this._objects.length; i--; ) {
currentObject = this._objects[i];
- if (!currentObject || !currentObject.selectable || !currentObject.visible) {
+ if (!currentObject || !currentObject.selectable || !currentObject.visible || currentObject.onSelect({ e: e })) {
continue;
}
@@ -12136,11 +12178,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
__toDataURL: function(format, quality) {
var canvasEl = this.contextContainer.canvas;
- // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
- if (format === 'jpg') {
- format = 'jpeg';
- }
-
var data = supportQuality
? canvasEl.toDataURL('image/' + format, quality)
: canvasEl.toDataURL('image/' + format);
@@ -12701,6 +12738,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
strokeDashArray: null,
/**
+ * Line offset of an object's stroke
+ * @type Number
+ * @default
+ */
+ strokeDashOffset: 0,
+
+ /**
* Line endings style of an object's stroke (one of "butt", "round", "square")
* @type String
* @default
@@ -12941,7 +12985,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
dirty: true,
/**
- * keeps the value of the last hovered coner during mouse move.
+ * keeps the value of the last hovered corner during mouse move.
* 0 is no corner, or 'mt', 'ml', 'mtr' etc..
* It should be private, but there is no harm in using it as
* a read-only property.
@@ -12951,7 +12995,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
__corner: 0,
/**
- * Determins if the fill or the stroke is drawn first (one of "fill" or "stroke")
+ * Determines if the fill or the stroke is drawn first (one of "fill" or "stroke")
* @type String
* @default
*/
@@ -12965,9 +13009,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
stateProperties: (
'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
- 'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
+ 'stroke strokeWidth strokeDashArray strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit ' +
'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
- 'skewX skewY fillRule paintFirst'
+ 'skewX skewY fillRule paintFirst clipPath'
).split(' '),
/**
@@ -12979,7 +13023,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
cacheProperties: (
'fill stroke strokeWidth strokeDashArray width height paintFirst' +
- ' strokeLineCap strokeLineJoin strokeMiterLimit backgroundColor'
+ ' strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath'
).split(' '),
/**
@@ -12992,7 +13036,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
clipPath: undefined,
/**
- * Meaningfull ONLY when the object is used as clipPath.
+ * Meaningful ONLY when the object is used as clipPath.
* if true, the clipPath will make the object clip to the outside of the clipPath
* since 2.4.0
* @type boolean
@@ -13001,7 +13045,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
inverted: false,
/**
- * Meaningfull ONLY when the object is used as clipPath.
+ * Meaningful ONLY when the object is used as clipPath.
* if true, the clipPath will have its top and left relative to canvas, and will
* not be influenced by the object transform. This will make the clipPath relative
* to the canvas, but clipping just a particular object.
@@ -13220,6 +13264,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
strokeDashArray: this.strokeDashArray ? this.strokeDashArray.concat() : this.strokeDashArray,
strokeLineCap: this.strokeLineCap,
+ strokeDashOffset: this.strokeDashOffset,
strokeLineJoin: this.strokeLineJoin,
strokeMiterLimit: toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
scaleX: toFixed(this.scaleX, NUM_FRACTION_DIGITS),
@@ -13394,7 +13439,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* Retrieves viewportTransform from Object's canvas if possible
* @method getViewportTransform
* @memberOf fabric.Object.prototype
- * @return {Boolean}
+ * @return {Array}
*/
getViewportTransform: function() {
if (this.canvas && this.canvas.viewportTransform) {
@@ -13410,7 +13455,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {Boolean}
*/
isNotVisible: function() {
- return this.opacity === 0 || (this.width === 0 && this.height === 0) || !this.visible;
+ return this.opacity === 0 ||
+ (this.width === 0 && this.height === 0 && this.strokeWidth === 0) ||
+ !this.visible;
},
/**
@@ -13609,7 +13656,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
- * Draws a background for the object big as its untrasformed dimensions
+ * Draws a background for the object big as its untransformed dimensions
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
@@ -13648,6 +13695,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (decl.stroke) {
ctx.lineWidth = decl.strokeWidth;
ctx.lineCap = decl.strokeLineCap;
+ ctx.lineDashOffset = decl.strokeDashOffset;
ctx.lineJoin = decl.strokeLineJoin;
ctx.miterLimit = decl.strokeMiterLimit;
ctx.strokeStyle = decl.stroke.toLive
@@ -13675,7 +13723,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* Sets line dash
* @param {CanvasRenderingContext2D} ctx Context to set the dash line on
* @param {Array} dashArray array representing dashes
- * @param {Function} alternative function to call if browaser does not support lineDash
+ * @param {Function} alternative function to call if browser does not support lineDash
*/
_setLineDash: function(ctx, dashArray, alternative) {
if (!dashArray) {
@@ -13842,7 +13890,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
},
/**
- * This function is an helper for svg import. it decoompose the transformMatrix
+ * This function is an helper for svg import. it decompose the transformMatrix
* and assign properties to object.
* untransformed coordinates
* @private
@@ -13907,7 +13955,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* Creates an instance of fabric.Image out of an object
* @param {Function} callback callback, invoked with an instance as a first argument
* @param {Object} [options] for clone as image, passed to toDataURL
- * @param {Boolean} [options.enableRetinaScaling] enable retina scaling for the cloned image
+ * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
+ * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
+ * @param {Number} [options.multiplier=1] Multiplier to scale by
+ * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
+ * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
+ * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
+ * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
+ * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
+ * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
+ * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
* @return {fabric.Object} thisArg
*/
cloneAsImage: function(callback, options) {
@@ -13932,42 +13989,52 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Number} [options.height] Cropping height. Introduced in v1.2.14
* @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
* @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
+ * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
* @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
*/
toDataURL: function(options) {
options || (options = { });
- var origParams = fabric.util.saveObjectTransform(this);
+ var utils = fabric.util, origParams = utils.saveObjectTransform(this),
+ originalShadow = this.shadow, abs = Math.abs;
if (options.withoutTransform) {
- fabric.util.resetObjectTransform(this);
+ utils.resetObjectTransform(this);
+ }
+ if (options.withoutShadow) {
+ this.shadow = null;
}
var el = fabric.util.createCanvasElement(),
// skip canvas zoom and calculate with setCoords now.
- boundingRect = this.getBoundingRect(true, true);
-
- el.width = boundingRect.width;
- el.height = boundingRect.height;
+ boundingRect = this.getBoundingRect(true, true),
+ shadow = this.shadow, scaling,
+ shadowOffset = { x: 0, y: 0 }, shadowBlur;
+
+ if (shadow) {
+ shadowBlur = shadow.blur;
+ scaling = this.getObjectScaling();
+ shadowOffset.x = 2 * Math.round((abs(shadow.offsetX) + shadowBlur) * abs(scaling.scaleX));
+ shadowOffset.y = 2 * Math.round((abs(shadow.offsetY) + shadowBlur) * abs(scaling.scaleY));
+ }
+ el.width = boundingRect.width + shadowOffset.x;
+ el.height = boundingRect.height + shadowOffset.y;
+ el.width += el.width % 2 ? 2 - el.width % 2 : 0;
+ el.height += el.height % 2 ? 2 - el.height % 2 : 0;
var canvas = new fabric.StaticCanvas(el, {
enableRetinaScaling: options.enableRetinaScaling,
renderOnAddRemove: false,
skipOffscreen: false,
});
- // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
- if (options.format === 'jpg') {
- options.format = 'jpeg';
- }
-
if (options.format === 'jpeg') {
canvas.backgroundColor = '#fff';
}
-
this.setPositionByOrigin(new fabric.Point(canvas.width / 2, canvas.height / 2), 'center', 'center');
var originalCanvas = this.canvas;
canvas.add(this);
var data = canvas.toDataURL(options);
+ this.shadow = originalShadow;
this.set(origParams).setCoords();
this.canvas = originalCanvas;
// canvas.dispose will call image.dispose that will nullify the elements
@@ -14020,7 +14087,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
* @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
* @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
- * @param {Object} [options.gradientTransform] transforMatrix for gradient
+ * @param {Object} [options.gradientTransform] transformMatrix for gradient
* @return {fabric.Object} thisArg
* @chainable
* @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
@@ -14247,7 +14314,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Sets canvas globalCompositeOperation for specific object
- * custom composition operation for the particular object can be specifed using globalCompositeOperation property
+ * custom composition operation for the particular object can be specified using globalCompositeOperation property
* @param {CanvasRenderingContext2D} ctx Rendering canvas context
*/
_setupCompositeOperation: function (ctx) {
@@ -15306,9 +15373,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
getSvgStyles: function(skipShadow) {
- var fillRule = this.fillRule,
+ var fillRule = this.fillRule ? this.fillRule : 'nonzero',
strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : 'none',
+ strokeDashOffset = this.strokeDashOffset ? this.strokeDashOffset : '0',
strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
@@ -15323,6 +15391,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'stroke-width: ', strokeWidth, '; ',
'stroke-dasharray: ', strokeDashArray, '; ',
'stroke-linecap: ', strokeLineCap, '; ',
+ 'stroke-dashoffset: ', strokeDashOffset, '; ',
'stroke-linejoin: ', strokeLineJoin, '; ',
'stroke-miterlimit: ', strokeMiterLimit, '; ',
fill,
@@ -15405,45 +15474,16 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/**
* Returns transform-string for svg-export
+ * @param {Boolean} use the full transform or the single object one.
* @return {String}
*/
- getSvgTransform: function() {
- var angle = this.angle,
- skewX = (this.skewX % 360),
- skewY = (this.skewY % 360),
- center = this.getCenterPoint(),
-
- NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
-
- translatePart = 'translate(' +
- toFixed(center.x, NUM_FRACTION_DIGITS) +
- ' ' +
- toFixed(center.y, NUM_FRACTION_DIGITS) +
- ')',
-
- anglePart = angle !== 0
- ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
- : '',
-
- scalePart = (this.scaleX === 1 && this.scaleY === 1)
- ? '' :
- (' scale(' +
- toFixed(this.scaleX, NUM_FRACTION_DIGITS) +
- ' ' +
- toFixed(this.scaleY, NUM_FRACTION_DIGITS) +
- ')'),
-
- skewXPart = skewX !== 0 ? ' skewX(' + toFixed(skewX, NUM_FRACTION_DIGITS) + ')' : '',
-
- skewYPart = skewY !== 0 ? ' skewY(' + toFixed(skewY, NUM_FRACTION_DIGITS) + ')' : '',
-
- flipXPart = this.flipX ? ' matrix(-1 0 0 1 0 0) ' : '',
-
- flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 0)' : '';
-
- return [
- translatePart, anglePart, scalePart, flipXPart, flipYPart, skewXPart, skewYPart
- ].join('');
+ getSvgTransform: function(full, additionalTransform) {
+ var transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(),
+ svgTransform = transform.map(function(value) {
+ return toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
+ }).join(' ');
+ return 'transform="matrix(' + svgTransform + ')' +
+ (additionalTransform || '') + this.getSvgTransformMatrix() + '" ';
},
/**
@@ -15451,7 +15491,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransformMatrix: function() {
- return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ') ' : '';
+ return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
},
_setSVGBg: function(textBgRects) {
@@ -15473,11 +15513,76 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
},
/**
+ * Returns svg representation of an instance
+ * @param {Function} [reviver] Method for further parsing of svg representation.
+ * @return {String} svg representation of an instance
+ */
+ toSVG: function(reviver) {
+ return this._createBaseSVGMarkup(this._toSVG(), { reviver: reviver });
+ },
+
+ /**
+ * Returns svg clipPath representation of an instance
+ * @param {Function} [reviver] Method for further parsing of svg representation.
+ * @return {String} svg representation of an instance
+ */
+ toClipPathSVG: function(reviver) {
+ return '\t' + this._createBaseClipPathSVGMarkup(this._toSVG(), { reviver: reviver });
+ },
+
+ /**
* @private
*/
- _createBaseSVGMarkup: function() {
- var markup = [], clipPath = this.clipPath;
+ _createBaseClipPathSVGMarkup: function(objectMarkup, options) {
+ options = options || {};
+ var reviver = options.reviver,
+ additionalTransform = options.additionalTransform || '',
+ commonPieces = [
+ this.getSvgTransform(true, additionalTransform),
+ this.getSvgCommons(),
+ ].join(''),
+ // insert commons in the markup, style and svgCommons
+ index = objectMarkup.indexOf('COMMON_PARTS');
+ objectMarkup[index] = commonPieces;
+ return reviver ? reviver(objectMarkup.join('')) : objectMarkup.join('');
+ },
+ /**
+ * @private
+ */
+ _createBaseSVGMarkup: function(objectMarkup, options) {
+ options = options || {};
+ var noStyle = options.noStyle, withShadow = options.withShadow,
+ reviver = options.reviver,
+ styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ',
+ shadowInfo = withShadow ? 'style="' + this.getSvgFilter() + '" ' : '',
+ clipPath = this.clipPath,
+ absoluteClipPath = this.clipPath && this.clipPath.absolutePositioned,
+ commonPieces, markup = [], clipPathMarkup,
+ // insert commons in the markup, style and svgCommons
+ index = objectMarkup.indexOf('COMMON_PARTS');
+ if (clipPath) {
+ clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
+ clipPathMarkup = '<clipPath id="' + clipPath.clipPathId + '" >\n' +
+ this.clipPath.toClipPathSVG(reviver) +
+ '</clipPath>\n';
+ }
+ if (absoluteClipPath) {
+ markup.push(
+ '<g ', shadowInfo, this.getSvgCommons(), ' >\n'
+ );
+ }
+ markup.push(
+ '<g ',
+ this.getSvgTransform(false),
+ !absoluteClipPath ? shadowInfo + this.getSvgCommons() : '',
+ ' >\n'
+ );
+ commonPieces = [
+ styleInfo,
+ noStyle ? '' : this.addPaintOrder(), ' '
+ ].join('');
+ objectMarkup[index] = commonPieces;
if (this.fill && this.fill.toLive) {
markup.push(this.fill.toSVG(this, false));
}
@@ -15488,14 +15593,12 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
markup.push(this.shadow.toSVG(this));
}
if (clipPath) {
- clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
- markup.push(
- '<clipPath id="' + clipPath.clipPathId + '" >\n\t',
- this.clipPath.toSVG(),
- '</clipPath>\n'
- );
+ markup.push(clipPathMarkup);
}
- return markup;
+ markup.push(objectMarkup.join(''));
+ markup.push('</g>\n');
+ absoluteClipPath && markup.push('</g>\n');
+ return reviver ? reviver(markup.join('')) : markup.join('');
},
addPaintOrder: function() {
@@ -15548,6 +15651,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
}
for (var i = 0, len = keys.length; i < len; i++) {
key = keys[i];
+ // since clipPath is in the statefull cache list and the clipPath objects
+ // would be iterated as an object, this would lead to possible infinite recursion
+ if (key === 'canvas') {
+ continue;
+ }
if (!_isEqual(origValue[key], currentValue[key])) {
return false;
}
@@ -16503,26 +16611,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
- * Returns SVG representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * Returns svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(),
- p = this.calcLinePoints();
- markup.push(
- '<line ', this.getSvgCommons(),
+ _toSVG: function() {
+ var p = this.calcLinePoints();
+ return [
+ '<line ', 'COMMON_PARTS',
'x1="', p.x1,
'" y1="', p.y1,
'" x2="', p.x2,
'" y2="', p.y2,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- this.getSvgTransformMatrix(),
- '"/>\n'
- );
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ '" />\n'
+ ];
},
/* _TO_SVG_END_ */
});
@@ -16682,26 +16784,23 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
},
/* _TO_SVG_START_ */
+
/**
* Returns svg representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(), x = 0, y = 0,
+ _toSVG: function() {
+ var svgString, x = 0, y = 0,
angle = (this.endAngle - this.startAngle) % ( 2 * pi);
if (angle === 0) {
- markup.push(
- '<circle ', this.getSvgCommons(),
+ svgString = [
+ '<circle ', 'COMMON_PARTS',
'cx="' + x + '" cy="' + y + '" ',
'r="', this.radius,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(), '"',
- this.addPaintOrder(),
- '/>\n'
- );
+ '" />\n'
+ ];
}
else {
var startX = fabric.util.cos(this.startAngle) * this.radius,
@@ -16709,20 +16808,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
endX = fabric.util.cos(this.endAngle) * this.radius,
endY = fabric.util.sin(this.endAngle) * this.radius,
largeFlag = angle > pi ? '1' : '0';
-
- markup.push(
+ svgString = [
'<path d="M ' + startX + ' ' + startY,
' A ' + this.radius + ' ' + this.radius,
' 0 ', +largeFlag + ' 1', ' ' + endX + ' ' + endY,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(), '"',
- this.addPaintOrder(),
- '/>\n'
- );
+ '"', 'COMMON_PARTS', ' />\n'
+ ];
}
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ return svgString;
},
/* _TO_SVG_END_ */
@@ -16895,31 +16988,23 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
- * Returns SVG representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * Returns svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(),
- widthBy2 = this.width / 2,
+ _toSVG: function() {
+ var widthBy2 = this.width / 2,
heightBy2 = this.height / 2,
points = [
-widthBy2 + ' ' + heightBy2,
'0 ' + -heightBy2,
widthBy2 + ' ' + heightBy2
- ]
- .join(',');
-
- markup.push(
- '<polygon ', this.getSvgCommons(),
+ ].join(',');
+ return [
+ '<polygon ', 'COMMON_PARTS',
'points="', points,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(), '"',
- this.addPaintOrder(),
- '/>'
- );
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ '" />'
+ ];
},
/* _TO_SVG_END_ */
});
@@ -17045,24 +17130,17 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup();
- markup.push(
- '<ellipse ', this.getSvgCommons(),
+ _toSVG: function() {
+ return [
+ '<ellipse ', 'COMMON_PARTS',
'cx="0" cy="0" ',
'rx="', this.rx,
'" ry="', this.ry,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- this.getSvgTransformMatrix(), '"',
- this.addPaintOrder(),
- '/>\n'
- );
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ '" />\n'
+ ];
},
/* _TO_SVG_END_ */
@@ -17208,11 +17286,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_render: function(ctx) {
- // optimize 1x1 case (used in spray brush)
- if (this.width === 1 && this.height === 1) {
- ctx.fillRect(-0.5, -0.5, 1, 1);
- return;
- }
+ // 1x1 case (used in spray brush) optimization was removed because
+ // with caching and higher zoom level this makes more damage than help
var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
@@ -17274,23 +17349,18 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2;
- markup.push(
- '<rect ', this.getSvgCommons(),
+ _toSVG: function() {
+ var x = -this.width / 2, y = -this.height / 2;
+ return [
+ '<rect ', 'COMMON_PARTS',
'x="', x, '" y="', y,
- '" rx="', this.get('rx'), '" ry="', this.get('ry'),
+ '" rx="', this.rx, '" ry="', this.ry,
'" width="', this.width, '" height="', this.height,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- this.getSvgTransformMatrix(), '"',
- this.addPaintOrder(),
- '/>\n');
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ '" />\n'
+ ];
},
/* _TO_SVG_END_ */
});
@@ -17461,12 +17531,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
+ _toSVG: function() {
var points = [], diffX = this.pathOffset.x, diffY = this.pathOffset.y,
- markup = this._createBaseSVGMarkup(),
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
for (var i = 0, len = this.points.length; i < len; i++) {
@@ -17475,17 +17544,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' '
);
}
- markup.push(
- '<', this.type, ' ', this.getSvgCommons(),
+ return [
+ '<' + this.type + ' ', 'COMMON_PARTS',
'points="', points.join(''),
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(), '"',
- this.addPaintOrder(),
- '/>\n'
- );
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ '" />\n'
+ ];
},
/* _TO_SVG_END_ */
@@ -17696,6 +17759,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
extend = fabric.util.object.extend,
_toString = Object.prototype.toString,
drawArc = fabric.util.drawArc,
+ toFixed = fabric.util.toFixed,
commandLengths = {
m: 2,
l: 2,
@@ -18154,29 +18218,37 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
* Returns svg representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var chunks = [],
- markup = this._createBaseSVGMarkup(), addTransform = '';
-
- for (var i = 0, len = this.path.length; i < len; i++) {
- chunks.push(this.path[i].join(' '));
- }
- var path = chunks.join(' ');
- addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
- markup.push(
- '<path ', this.getSvgCommons(),
+ _toSVG: function() {
+ var path = this.path.map(function(path) {
+ return path.join(' ');
+ }).join(' ');
+ return [
+ '<path ', 'COMMON_PARTS',
'd="', path,
- '" style="', this.getSvgStyles(),
- '" transform="', this.getSvgTransform(), addTransform,
- this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
- this.addPaintOrder(),
+ '" stroke-linecap="round" ',
'/>\n'
- );
+ ];
+ },
- return reviver ? reviver(markup.join('')) : markup.join('');
+ _getOffsetTransform: function() {
+ var digits = fabric.Object.NUM_FRACTION_DIGITS;
+ return ' translate(' + toFixed(-this.pathOffset.x, digits) + ', ' +
+ toFixed(-this.pathOffset.y, digits) + ')';
+ },
+
+ /**
+ * Returns svg clipPath representation of an instance
+ * @param {Function} [reviver] Method for further parsing of svg representation.
+ * @return {String} svg representation of an instance
+ */
+ toClipPathSVG: function(reviver) {
+ var additionalTransform = this._getOffsetTransform();
+ return '\t' + this._createBaseClipPathSVGMarkup(
+ this._toSVG(), { reviver: reviver, additionalTransform: additionalTransform }
+ );
},
/* _TO_SVG_END_ */
@@ -19133,24 +19205,30 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup();
- markup.push(
- '<g ', this.getSvgCommons(), 'transform="',
- /* avoiding styles intentionally */
- this.getSvgTransform(),
- this.getSvgTransformMatrix(),
- '" style="',
- this.getSvgFilter(),
- '">\n'
- );
+ var svgString = [];
for (var i = 0, len = this._objects.length; i < len; i++) {
- markup.push('\t', this._objects[i].toSVG(reviver));
+ svgString.push('\t', this._objects[i].toSVG(reviver));
}
- markup.push('</g>\n');
+ return this._createBaseSVGMarkup(
+ svgString,
+ { reviver: reviver, noStyle: true, withShadow: true });
+ },
- return reviver ? reviver(markup.join('')) : markup.join('');
+ /**
+ * Returns svg clipPath representation of an instance
+ * @param {Function} [reviver] Method for further parsing of svg representation.
+ * @return {String} svg representation of an instance
+ */
+ toClipPathSVG: function(reviver) {
+ var svgString = [];
+
+ for (var i = 0, len = this._objects.length; i < len; i++) {
+ svgString.push('\t', this._objects[i].toClipPathSVG(reviver));
+ }
+
+ return this._createBaseClipPathSVGMarkup(svgString, { reviver: reviver });
},
/* _TO_SVG_END_ */
});
@@ -19164,9 +19242,12 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
fabric.Group.fromObject = function(object, callback) {
fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
- var options = fabric.util.object.clone(object, true);
- delete options.objects;
- callback && callback(new fabric.Group(enlivenedObjects, options, true));
+ fabric.util.enlivenObjects([object.clipPath], function(enlivedClipPath) {
+ var options = fabric.util.object.clone(object, true);
+ options.clipPath = enlivedClipPath[0];
+ delete options.objects;
+ callback && callback(new fabric.Group(enlivenedObjects, options, true));
+ });
});
};
@@ -19488,12 +19569,16 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this._element = element;
this._originalElement = element;
this._initConfig(options);
- if (this.resizeFilter) {
- this.applyResizeFilters();
- }
if (this.filters.length !== 0) {
this.applyFilters();
}
+ // resizeFilters work on the already filtered copy.
+ // we need to apply resizeFilters AFTER normal filters.
+ // applyResizeFilters is run more often than normal fiters
+ // and is triggered by user interactions rather than dev code
+ if (this.resizeFilter) {
+ this.applyResizeFilters();
+ }
return this;
},
@@ -19621,53 +19706,51 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
/* _TO_SVG_START_ */
/**
- * Returns SVG representation of an instance
- * @param {Function} [reviver] Method for further parsing of svg representation.
- * @return {String} svg representation of an instance
+ * Returns svg representation of an instance
+ * @return {Array} an array of strings with the specific svg representation
+ * of the instance
*/
- toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2, clipPath = '';
+ _toSVG: function() {
+ var svgString = [], imageMarkup = [], strokeSvg,
+ x = -this.width / 2, y = -this.height / 2, clipPath = '';
if (this.hasCrop()) {
var clipPathId = fabric.Object.__uid++;
- markup.push(
+ svgString.push(
'<clipPath id="imageCrop_' + clipPathId + '">\n',
'\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n',
'</clipPath>\n'
);
clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
}
- markup.push('<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n');
- var imageMarkup = ['\t<image ', this.getSvgCommons(), 'xlink:href="', this.getSvgSrc(true),
+ imageMarkup.push('\t<image ', 'COMMON_PARTS', 'xlink:href="', this.getSvgSrc(true),
'" x="', x - this.cropX, '" y="', y - this.cropY,
- '" style="', this.getSvgStyles(),
// we're essentially moving origin of transformation from top/left corner to the center of the shape
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
// so that object's center aligns with container's left/top
'" width="', this._element.width || this._element.naturalWidth,
'" height="', this._element.height || this._element.height,
'"', clipPath,
- '></image>\n'];
- if (this.paintFirst === 'fill') {
- Array.prototype.push.apply(markup, imageMarkup);
- }
+ '></image>\n');
+
if (this.stroke || this.strokeDashArray) {
var origFill = this.fill;
this.fill = null;
- markup.push(
+ strokeSvg = [
'\t<rect ',
'x="', x, '" y="', y,
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'"/>\n'
- );
+ ];
this.fill = origFill;
}
if (this.paintFirst !== 'fill') {
- Array.prototype.push.apply(markup, imageMarkup);
+ svgString = svgString.concat(strokeSvg, imageMarkup);
}
- markup.push('</g>\n');
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ else {
+ svgString = svgString.concat(imageMarkup, strokeSvg);
+ }
+ return svgString;
},
/* _TO_SVG_END_ */
@@ -19820,6 +19903,23 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
this._renderPaintInOrder(ctx);
},
+ /**
+ * Decide if the object should cache or not. Create its own cache level
+ * objectCaching is a global flag, wins over everything
+ * needsItsOwnCache should be used when the object drawing method requires
+ * a cache step. None of the fabric classes requires it.
+ * Generally you do not cache objects in groups because the group outside is cached.
+ * This is the special image version where we would like to avoid caching where possible.
+ * Essentially images do not benefit from caching. They may require caching, and in that
+ * case we do it. Also caching an image usually ends in a loss of details.
+ * A full performance audit should be done.
+ * @return {Boolean}
+ */
+ shouldCache: function() {
+ this.ownCaching = this.objectCaching && this.needsItsOwnCache();
+ return this.ownCaching;
+ },
+
_renderFill: function(ctx) {
var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
x = -w / 2, y = -h / 2, elementToDraw = this._element;
@@ -22962,8 +23062,8 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
/**
* Resize type
- * for webgl resizyType is just lanczos, for canvas2d can be:
- * bilinear, hermite, sliceHacl, lanczos.
+ * for webgl resizeType is just lanczos, for canvas2d can be:
+ * bilinear, hermite, sliceHack, lanczos.
* @param {String} resizeType
* @default
*/
@@ -28297,12 +28397,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
- var markup = this._createBaseSVGMarkup(),
- offsets = this._getSVGLeftTopOffsets(),
- textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft);
- this._wrapSVGTextAndBg(markup, textAndBg);
-
- return reviver ? reviver(markup.join('')) : markup.join('');
+ var offsets = this._getSVGLeftTopOffsets(),
+ textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft),
+ internalMarkup = this._wrapSVGTextAndBg(textAndBg);
+ return this._createBaseSVGMarkup(
+ internalMarkup, { reviver: reviver, noStyle: true, withShadow: true });
},
/**
@@ -28319,13 +28418,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* @private
*/
- _wrapSVGTextAndBg: function(markup, textAndBg) {
- var noShadow = true, filter = this.getSvgFilter(),
- style = filter === '' ? '' : ' style="' + filter + '"',
+ _wrapSVGTextAndBg: function(textAndBg) {
+ var noShadow = true,
textDecoration = this.getSvgTextDecoration(this);
- markup.push(
- '\t<g ', this.getSvgCommons(), 'transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
- style, '>\n',
+ return [
textAndBg.textBgRects.join(''),
'\t\t<text xml:space="preserve" ',
(this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ' : ''),
@@ -28335,9 +28431,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
(textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''),
'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >',
textAndBg.textSpans.join(''),
- '</text>\n',
- '\t</g>\n'
- );
+ '</text>\n'
+ ];
},
/**
@@ -28987,3 +29082,4 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
});
})();
+