mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-03-16 22:10:32 +00:00
parent
5138496163
commit
90a4df78e7
5 changed files with 367 additions and 147 deletions
|
|
@ -1,3 +1,6 @@
|
|||
**Version 2.4.0**
|
||||
- Add: Add clipPath support to canvas and svg import/export. Low compatibility yet.
|
||||
|
||||
**Version 2.3.6**
|
||||
- Fix: Make image.class aware of naturalWidth and naturalHeight. [#5178](https://github.com/fabricjs/fabric.js/pull/5178)
|
||||
- Fix: Make 2 finger events works again [#5177](https://github.com/fabricjs/fabric.js/pull/5177)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
|
||||
|
||||
var fabric = fabric || { version: '2.3.6' };
|
||||
var fabric = fabric || { version: '2.4.0' };
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.fabric = fabric;
|
||||
}
|
||||
|
|
|
|||
505
dist/fabric.js
vendored
505
dist/fabric.js
vendored
|
|
@ -1,7 +1,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.3.6' };
|
||||
var fabric = fabric || { version: '2.4.0' };
|
||||
if (typeof exports !== 'undefined') {
|
||||
exports.fabric = fabric;
|
||||
}
|
||||
|
|
@ -48,15 +48,15 @@ fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
|
|||
* @type array
|
||||
*/
|
||||
fabric.SHARED_ATTRIBUTES = [
|
||||
"display",
|
||||
"transform",
|
||||
"fill", "fill-opacity", "fill-rule",
|
||||
"opacity",
|
||||
"stroke", "stroke-dasharray", "stroke-linecap",
|
||||
"stroke-linejoin", "stroke-miterlimit",
|
||||
"stroke-opacity", "stroke-width",
|
||||
"id", "paint-order",
|
||||
"instantiated_by_use"
|
||||
'display',
|
||||
'transform',
|
||||
'fill', 'fill-opacity', 'fill-rule',
|
||||
'opacity',
|
||||
'stroke', 'stroke-dasharray', 'stroke-linecap',
|
||||
'stroke-linejoin', 'stroke-miterlimit',
|
||||
'stroke-opacity', 'stroke-width',
|
||||
'id', 'paint-order',
|
||||
'instantiated_by_use', 'clip-path'
|
||||
];
|
||||
/* _FROM_SVG_END_ */
|
||||
|
||||
|
|
@ -1171,6 +1171,20 @@ fabric.CommonMethods = {
|
|||
return fabric.document.createElement('canvas');
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a canvas element that is a copy of another and is also painted
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @return {CanvasElement} initialized canvas element
|
||||
*/
|
||||
copyCanvasElement: function(canvas) {
|
||||
var newCanvas = fabric.document.createElement('canvas');
|
||||
newCanvas.width = canvas.width;
|
||||
newCanvas.height = canvas.height;
|
||||
newCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
||||
return newCanvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates image element (works on client and node)
|
||||
* @static
|
||||
|
|
@ -3360,10 +3374,10 @@ if (typeof console !== 'undefined') {
|
|||
multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
|
||||
|
||||
svgValidTagNames = ['path', 'circle', 'polygon', 'polyline', 'ellipse', 'rect', 'line',
|
||||
'image', 'text', 'linearGradient', 'radialGradient', 'stop'],
|
||||
'image', 'text'],
|
||||
svgViewBoxElements = ['symbol', 'image', 'marker', 'pattern', 'view', 'svg'],
|
||||
svgInvalidAncestors = ['pattern', 'defs', 'symbol', 'metadata', 'clipPath', 'mask', 'desc'],
|
||||
svgValidParents = ['symbol', 'g', 'a', 'svg'],
|
||||
svgValidParents = ['symbol', 'g', 'a', 'svg', 'clipPath', 'defs'],
|
||||
|
||||
attributesMap = {
|
||||
cx: 'left',
|
||||
|
|
@ -3390,7 +3404,9 @@ if (typeof console !== 'undefined') {
|
|||
'stroke-width': 'strokeWidth',
|
||||
'text-decoration': 'textDecoration',
|
||||
'text-anchor': 'textAnchor',
|
||||
opacity: 'opacity'
|
||||
opacity: 'opacity',
|
||||
'clip-path': 'clipPath',
|
||||
'clip-rule': 'clipRule',
|
||||
},
|
||||
|
||||
colorAttributes = {
|
||||
|
|
@ -3405,6 +3421,7 @@ if (typeof console !== 'undefined') {
|
|||
|
||||
fabric.cssRules = { };
|
||||
fabric.gradientDefs = { };
|
||||
fabric.clipPaths = { };
|
||||
|
||||
function normalizeAttr(attr) {
|
||||
// transform attribute names
|
||||
|
|
@ -3962,7 +3979,7 @@ if (typeof console !== 'undefined') {
|
|||
scaleY + ' ' +
|
||||
(minX * scaleX + widthDiff) + ' ' +
|
||||
(minY * scaleY + heightDiff) + ') ';
|
||||
|
||||
parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix);
|
||||
if (element.nodeName === 'svg') {
|
||||
el = element.ownerDocument.createElement('g');
|
||||
// element.firstChild != null
|
||||
|
|
@ -3975,7 +3992,6 @@ if (typeof console !== 'undefined') {
|
|||
el = element;
|
||||
matrix = el.getAttribute('transform') + matrix;
|
||||
}
|
||||
|
||||
el.setAttribute('transform', matrix);
|
||||
return parsedDim;
|
||||
}
|
||||
|
|
@ -4036,13 +4052,24 @@ if (typeof console !== 'undefined') {
|
|||
callback && callback([], {});
|
||||
return;
|
||||
}
|
||||
|
||||
var clipPaths = { };
|
||||
descendants.filter(function(el) {
|
||||
return el.nodeName.replace('svg:', '') === 'clipPath';
|
||||
}).forEach(function(el) {
|
||||
clipPaths[el.id] = fabric.util.toArray(el.getElementsByTagName('*')).filter(function(el) {
|
||||
return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', ''));
|
||||
});
|
||||
});
|
||||
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
|
||||
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
|
||||
fabric.clipPaths[svgUid] = clipPaths;
|
||||
// Precedence of rules: style > class > attribute
|
||||
fabric.parseElements(elements, function(instances, elements) {
|
||||
if (callback) {
|
||||
callback(instances, options, elements, descendants);
|
||||
delete fabric.gradientDefs[svgUid];
|
||||
delete fabric.cssRules[svgUid];
|
||||
delete fabric.clipPaths[svgUid];
|
||||
}
|
||||
}, clone(options), reviver, parsingOptions);
|
||||
};
|
||||
|
|
@ -4395,82 +4422,130 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
|
|||
this.regexUrl = /^url\(['"]?#([^'"]+)['"]?\)/g;
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.parse = function() {
|
||||
this.instances = new Array(this.elements.length);
|
||||
this.numElements = this.elements.length;
|
||||
|
||||
this.createObjects();
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.createObjects = function() {
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
this.elements[i].setAttribute('svgUid', this.svgUid);
|
||||
(function(_obj, i) {
|
||||
setTimeout(function() {
|
||||
_obj.createObject(_obj.elements[i], i);
|
||||
}, 0);
|
||||
})(this, i);
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.createObject = function(el, index) {
|
||||
var klass = fabric[fabric.util.string.capitalize(el.tagName.replace('svg:', ''))];
|
||||
if (klass && klass.fromElement) {
|
||||
try {
|
||||
this._createObject(klass, el, index);
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.checkIfDone();
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
|
||||
klass.fromElement(el, this.createCallback(index, el), this.options);
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.createCallback = function(index, el) {
|
||||
var _this = this;
|
||||
return function(obj) {
|
||||
var _options;
|
||||
_this.resolveGradient(obj, 'fill');
|
||||
_this.resolveGradient(obj, 'stroke');
|
||||
if (obj instanceof fabric.Image) {
|
||||
_options = obj.parsePreserveAspectRatioAttribute(el);
|
||||
}
|
||||
obj._removeTransformMatrix(_options);
|
||||
_this.reviver && _this.reviver(el, obj);
|
||||
_this.instances[index] = obj;
|
||||
_this.checkIfDone();
|
||||
(function(proto) {
|
||||
proto.parse = function() {
|
||||
this.instances = new Array(this.elements.length);
|
||||
this.numElements = this.elements.length;
|
||||
this.createObjects();
|
||||
};
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
|
||||
|
||||
var instanceFillValue = obj[property];
|
||||
if (!(/^url\(/).test(instanceFillValue)) {
|
||||
return;
|
||||
}
|
||||
var gradientId = this.regexUrl.exec(instanceFillValue)[1];
|
||||
this.regexUrl.lastIndex = 0;
|
||||
if (fabric.gradientDefs[this.svgUid][gradientId]) {
|
||||
obj.set(property,
|
||||
fabric.Gradient.fromElement(fabric.gradientDefs[this.svgUid][gradientId], obj));
|
||||
}
|
||||
};
|
||||
|
||||
fabric.ElementsParser.prototype.checkIfDone = function() {
|
||||
if (--this.numElements === 0) {
|
||||
this.instances = this.instances.filter(function(el) {
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
return el != null;
|
||||
proto.createObjects = function() {
|
||||
var _this = this;
|
||||
this.elements.forEach(function(element, i) {
|
||||
element.setAttribute('svgUid', _this.svgUid);
|
||||
_this.createObject(element, i);
|
||||
});
|
||||
this.callback(this.instances, this.elements);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
proto.findTag = function(el) {
|
||||
return fabric[fabric.util.string.capitalize(el.tagName.replace('svg:', ''))];
|
||||
};
|
||||
|
||||
proto.createObject = function(el, index) {
|
||||
var klass = this.findTag(el);
|
||||
if (klass && klass.fromElement) {
|
||||
try {
|
||||
klass.fromElement(el, this.createCallback(index, el), this.options);
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log(err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.checkIfDone();
|
||||
}
|
||||
};
|
||||
|
||||
proto.createCallback = function(index, el) {
|
||||
var _this = this;
|
||||
return function(obj) {
|
||||
var _options;
|
||||
_this.resolveGradient(obj, 'fill');
|
||||
_this.resolveGradient(obj, 'stroke');
|
||||
if (obj instanceof fabric.Image) {
|
||||
_options = obj.parsePreserveAspectRatioAttribute(el);
|
||||
}
|
||||
obj._removeTransformMatrix(_options);
|
||||
_this.resolveClipPath(obj);
|
||||
_this.reviver && _this.reviver(el, obj);
|
||||
_this.instances[index] = obj;
|
||||
_this.checkIfDone();
|
||||
};
|
||||
};
|
||||
|
||||
proto.extractPropertyDefinition = function(obj, property, storage) {
|
||||
var value = obj[property];
|
||||
if (!(/^url\(/).test(value)) {
|
||||
return;
|
||||
}
|
||||
var id = this.regexUrl.exec(value)[1];
|
||||
this.regexUrl.lastIndex = 0;
|
||||
return fabric[storage][this.svgUid][id];
|
||||
};
|
||||
|
||||
proto.resolveGradient = function(obj, property) {
|
||||
var gradientDef = this.extractPropertyDefinition(obj, property, 'gradientDefs');
|
||||
if (gradientDef) {
|
||||
obj.set(property, fabric.Gradient.fromElement(gradientDef, obj));
|
||||
}
|
||||
};
|
||||
|
||||
proto.createClipPathCallback = function(obj, container) {
|
||||
return function(_newObj) {
|
||||
_newObj._removeTransformMatrix();
|
||||
_newObj.fillRule = _newObj.clipRule;
|
||||
container.push(_newObj);
|
||||
};
|
||||
};
|
||||
|
||||
proto.resolveClipPath = function(obj) {
|
||||
var clipPath = this.extractPropertyDefinition(obj, 'clipPath', 'clipPaths'),
|
||||
element, klass, objTransformInv, container, gTransform, options;
|
||||
if (clipPath) {
|
||||
container = [];
|
||||
objTransformInv = fabric.util.invertTransform(obj.calcTransformMatrix());
|
||||
for (var i = 0; i < clipPath.length; i++) {
|
||||
element = clipPath[i];
|
||||
klass = this.findTag(element);
|
||||
klass.fromElement(
|
||||
element,
|
||||
this.createClipPathCallback(obj, container),
|
||||
this.options
|
||||
);
|
||||
}
|
||||
if (container.length === 1) {
|
||||
clipPath = container[0];
|
||||
}
|
||||
else {
|
||||
clipPath = new fabric.Group(container);
|
||||
}
|
||||
gTransform = fabric.util.multiplyTransformMatrices(
|
||||
objTransformInv,
|
||||
clipPath.calcTransformMatrix()
|
||||
);
|
||||
var options = fabric.util.qrDecompose(gTransform);
|
||||
clipPath.flipX = false;
|
||||
clipPath.flipY = false;
|
||||
clipPath.set('scaleX', options.scaleX);
|
||||
clipPath.set('scaleY', options.scaleY);
|
||||
clipPath.angle = options.angle;
|
||||
clipPath.skewX = options.skewX;
|
||||
clipPath.skewY = 0;
|
||||
clipPath.setPositionByOrigin({ x: options.translateX, y: options.translateY }, 'center', 'center');
|
||||
obj.clipPath = clipPath;
|
||||
}
|
||||
};
|
||||
|
||||
proto.checkIfDone = function() {
|
||||
if (--this.numElements === 0) {
|
||||
this.instances = this.instances.filter(function(el) {
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
return el != null;
|
||||
});
|
||||
this.callback(this.instances, this.elements);
|
||||
}
|
||||
};
|
||||
})(fabric.ElementsParser.prototype);
|
||||
|
||||
|
||||
(function(global) {
|
||||
|
|
@ -6668,6 +6743,15 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
|
|||
*/
|
||||
skipOffscreen: true,
|
||||
|
||||
/**
|
||||
* a fabricObject that, without stroke define a clipping area with their shape. filled in black
|
||||
* the clipPath object gets used when the canvas has rendered, and the context is placed in the
|
||||
* top left corner of the canvas.
|
||||
* clipPath will clip away controls, if you do not want this to happen use controlsAboveOverlay = true
|
||||
* @type fabric.Object
|
||||
*/
|
||||
clipPath: undefined,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {HTMLElement | String} el <canvas> element to initialize instance on
|
||||
|
|
@ -7363,11 +7447,11 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
|
|||
* @chainable
|
||||
*/
|
||||
renderCanvas: function(ctx, objects) {
|
||||
var v = this.viewportTransform;
|
||||
var v = this.viewportTransform, path = this.clipPath;
|
||||
this.cancelRequestedRender();
|
||||
this.calcViewportBoundaries();
|
||||
this.clearContext(ctx);
|
||||
this.fire('before:render');
|
||||
this.fire('before:render', { ctx: ctx, });
|
||||
if (this.clipTo) {
|
||||
fabric.util.clipContext(this, ctx);
|
||||
}
|
||||
|
|
@ -7384,11 +7468,38 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
|
|||
if (this.clipTo) {
|
||||
ctx.restore();
|
||||
}
|
||||
if (path) {
|
||||
if (path.isCacheDirty()) {
|
||||
// needed to setup a couple of variables
|
||||
path.shouldCache();
|
||||
path.canvas = this;
|
||||
path._transformDone = true;
|
||||
path.renderCache({ forClipping: true });
|
||||
}
|
||||
this.drawClipPathOnCanvas(ctx);
|
||||
}
|
||||
this._renderOverlay(ctx);
|
||||
if (this.controlsAboveOverlay && this.interactive) {
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
this.fire('after:render');
|
||||
this.fire('after:render', { ctx: ctx, });
|
||||
},
|
||||
|
||||
/**
|
||||
* Paint the cached clipPath on the lowerCanvasEl
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
drawClipPathOnCanvas: function(ctx) {
|
||||
var v = this.viewportTransform, path = this.clipPath;
|
||||
ctx.save();
|
||||
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
|
||||
// DEBUG: uncomment this line, comment the following
|
||||
// ctx.globalAlpha = 0.4
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
path.transform(ctx);
|
||||
ctx.scale(1 / path.zoomX, 1 / path.zoomY);
|
||||
ctx.drawImage(path._cacheCanvas, -path.cacheTranslationX, -path.cacheTranslationY);
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -7585,11 +7696,13 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
|
|||
*/
|
||||
_toObjectMethod: function (methodName, propertiesToInclude) {
|
||||
|
||||
var data = {
|
||||
var clipPath = this.clipPath, data = {
|
||||
version: fabric.version,
|
||||
objects: this._toObjects(methodName, propertiesToInclude)
|
||||
objects: this._toObjects(methodName, propertiesToInclude),
|
||||
};
|
||||
|
||||
if (clipPath) {
|
||||
clipPath = clipPath.toObject(propertiesToInclude);
|
||||
}
|
||||
extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
|
||||
|
||||
fabric.util.populateWithProperties(this, data, propertiesToInclude);
|
||||
|
|
@ -12867,6 +12980,36 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
' strokeLineCap strokeLineJoin strokeMiterLimit backgroundColor'
|
||||
).split(' '),
|
||||
|
||||
/**
|
||||
* a fabricObject that, without stroke define a clipping area with their shape. filled in black
|
||||
* the clipPath object gets used when the object has rendered, and the context is placed in the center
|
||||
* of the object cacheCanvas.
|
||||
* If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center'
|
||||
* @type fabric.Object
|
||||
*/
|
||||
clipPath: undefined,
|
||||
|
||||
/**
|
||||
* Meaningfull 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
|
||||
* @default false
|
||||
*/
|
||||
inverted: false,
|
||||
|
||||
/**
|
||||
* Meaningfull 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.
|
||||
* WARNING this is beta, this feature may change or be renamed.
|
||||
* since 2.4.0
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
absolutePositioned: false,
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} [options] Options object
|
||||
|
|
@ -12939,8 +13082,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
* Return the dimension and the zoom level needed to create a cache canvas
|
||||
* big enough to host the object to be cached.
|
||||
* @private
|
||||
* @param {Object} dim.x width of object to be cached
|
||||
* @param {Object} dim.y height of object to be cached
|
||||
* @return {Object}.x width of object to be cached
|
||||
* @return {Object}.y height of object to be cached
|
||||
* @return {Object}.width width of canvas
|
||||
* @return {Object}.height height of canvas
|
||||
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
|
||||
|
|
@ -12972,9 +13115,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
* @return {Boolean} true if the canvas has been resized
|
||||
*/
|
||||
_updateCacheCanvas: function() {
|
||||
if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
|
||||
var target = this.canvas._currentTransform.target,
|
||||
action = this.canvas._currentTransform.action;
|
||||
var targetCanvas = this.canvas;
|
||||
if (this.noScaleCache && targetCanvas && targetCanvas._currentTransform) {
|
||||
var target = targetCanvas._currentTransform.target,
|
||||
action = targetCanvas._currentTransform.action;
|
||||
if (this === target && action.slice && action.slice(0, 5) === 'scale') {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -13091,9 +13235,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
globalCompositeOperation: this.globalCompositeOperation,
|
||||
transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : null,
|
||||
skewX: toFixed(this.skewX, NUM_FRACTION_DIGITS),
|
||||
skewY: toFixed(this.skewY, NUM_FRACTION_DIGITS)
|
||||
skewY: toFixed(this.skewY, NUM_FRACTION_DIGITS),
|
||||
};
|
||||
|
||||
if (this.clipPath) {
|
||||
object.clipPath = this.clipPath.toObject(propertiesToInclude);
|
||||
object.clipPath.inverted = this.clipPath.inverted;
|
||||
object.clipPath.absolutePositioned = this.clipPath.absolutePositioned;
|
||||
}
|
||||
|
||||
fabric.util.populateWithProperties(this, object, propertiesToInclude);
|
||||
if (!this.includeDefaultValues) {
|
||||
object = this._removeDefaultValues(object);
|
||||
|
|
@ -13284,15 +13434,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
}
|
||||
this.clipTo && fabric.util.clipContext(this, ctx);
|
||||
if (this.shouldCache()) {
|
||||
if (!this._cacheCanvas) {
|
||||
this._createCacheCanvas();
|
||||
|
||||
}
|
||||
if (this.isCacheDirty()) {
|
||||
this.statefullCache && this.saveState({ propertySet: 'cacheProperties' });
|
||||
this.drawObject(this._cacheContext);
|
||||
this.dirty = false;
|
||||
}
|
||||
this.renderCache();
|
||||
this.drawCacheOnCanvas(ctx);
|
||||
}
|
||||
else {
|
||||
|
|
@ -13307,6 +13449,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
ctx.restore();
|
||||
},
|
||||
|
||||
renderCache: function(options) {
|
||||
options = options || {};
|
||||
if (!this._cacheCanvas) {
|
||||
this._createCacheCanvas();
|
||||
}
|
||||
if (this.isCacheDirty(false)) {
|
||||
this.statefullCache && this.saveState({ propertySet: 'cacheProperties' });
|
||||
this.drawObject(this._cacheContext, options.forClipping);
|
||||
this.dirty = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove cacheCanvas and its dimensions from the objects
|
||||
*/
|
||||
|
|
@ -13328,6 +13482,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') {
|
||||
return true;
|
||||
}
|
||||
if (this.clipPath) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
|
@ -13354,15 +13511,61 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
return !!this.shadow && (this.shadow.offsetX !== 0 || this.shadow.offsetY !== 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute the drawing operation for an object clipPath
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
drawClipPathOnCache: function(ctx) {
|
||||
var path = this.clipPath;
|
||||
ctx.save();
|
||||
// DEBUG: uncomment this line, comment the following
|
||||
// ctx.globalAlpha = 0.4
|
||||
if (path.inverted) {
|
||||
ctx.globalCompositeOperation = 'destination-out';
|
||||
}
|
||||
else {
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
}
|
||||
//ctx.scale(1 / 2, 1 / 2);
|
||||
if (path.absolutePositioned) {
|
||||
var m = fabric.util.invertTransform(this.calcTransformMatrix());
|
||||
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
path.transform(ctx);
|
||||
ctx.scale(1 / path.zoomX, 1 / path.zoomY);
|
||||
ctx.drawImage(path._cacheCanvas, -path.cacheTranslationX, -path.cacheTranslationY);
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute the drawing operation for an object on a specified context
|
||||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
drawObject: function(ctx) {
|
||||
this._renderBackground(ctx);
|
||||
this._setStrokeStyles(ctx, this);
|
||||
this._setFillStyles(ctx, this);
|
||||
drawObject: function(ctx, forClipping) {
|
||||
|
||||
if (forClipping) {
|
||||
this._setClippingProperties(ctx);
|
||||
}
|
||||
else {
|
||||
this._renderBackground(ctx);
|
||||
this._setStrokeStyles(ctx, this);
|
||||
this._setFillStyles(ctx, this);
|
||||
}
|
||||
this._render(ctx);
|
||||
this._drawClipPath(ctx);
|
||||
},
|
||||
|
||||
_drawClipPath: function(ctx) {
|
||||
var path = this.clipPath;
|
||||
if (!path) { return; }
|
||||
// needed to setup a couple of variables
|
||||
// path canvas gets overridden with this one.
|
||||
// TODO find a better solution?
|
||||
path.canvas = this.canvas;
|
||||
path.shouldCache();
|
||||
path._transformDone = true;
|
||||
path.renderCache({ forClipping: true });
|
||||
this.drawClipPathOnCache(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -13388,7 +13591,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
return true;
|
||||
}
|
||||
else {
|
||||
if (this.dirty || (this.statefullCache && this.hasStateChanged('cacheProperties'))) {
|
||||
if (this.dirty ||
|
||||
(this.clipPath && this.clipPath.absolutePositioned) ||
|
||||
(this.statefullCache && this.hasStateChanged('cacheProperties'))
|
||||
) {
|
||||
if (this._cacheCanvas && !skipCanvas) {
|
||||
var width = this.cacheWidth / this.zoomX;
|
||||
var height = this.cacheHeight / this.zoomY;
|
||||
|
|
@ -13456,6 +13662,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
}
|
||||
},
|
||||
|
||||
_setClippingProperties: function(ctx) {
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.strokeStyle = 'transparent';
|
||||
ctx.fillStyle = '#000000';
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets line dash
|
||||
|
|
@ -14067,8 +14279,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
if (typeof patterns[1] !== 'undefined') {
|
||||
object.stroke = patterns[1];
|
||||
}
|
||||
var instance = extraParam ? new klass(object[extraParam], object) : new klass(object);
|
||||
callback && callback(instance);
|
||||
fabric.util.enlivenObjects([object.clipPath], function(enlivedProps) {
|
||||
object.clipPath = enlivedProps[0];
|
||||
var instance = extraParam ? new klass(object[extraParam], object) : new klass(object);
|
||||
callback && callback(instance);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -15179,8 +15394,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* Returns id attribute for svg output
|
||||
* @return {String}
|
||||
*/
|
||||
getSvgId: function() {
|
||||
return this.id ? 'id="' + this.id + '" ' : '';
|
||||
getSvgCommons: function() {
|
||||
return [
|
||||
this.id ? 'id="' + this.id + '" ' : '',
|
||||
this.clipPath ? 'clip-path="url(#' + this.clipPath.clipPathId + ')" ' : '',
|
||||
].join('');
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -15256,7 +15474,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @private
|
||||
*/
|
||||
_createBaseSVGMarkup: function() {
|
||||
var markup = [];
|
||||
var markup = [], clipPath = this.clipPath;
|
||||
|
||||
if (this.fill && this.fill.toLive) {
|
||||
markup.push(this.fill.toSVG(this, false));
|
||||
|
|
@ -15267,6 +15485,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
if (this.shadow) {
|
||||
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'
|
||||
);
|
||||
}
|
||||
return markup;
|
||||
},
|
||||
|
||||
|
|
@ -16283,7 +16509,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
var markup = this._createBaseSVGMarkup(),
|
||||
p = this.calcLinePoints();
|
||||
markup.push(
|
||||
'<line ', this.getSvgId(),
|
||||
'<line ', this.getSvgCommons(),
|
||||
'x1="', p.x1,
|
||||
'" y1="', p.y1,
|
||||
'" x2="', p.x2,
|
||||
|
|
@ -16465,7 +16691,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
|
||||
if (angle === 0) {
|
||||
markup.push(
|
||||
'<circle ', this.getSvgId(),
|
||||
'<circle ', this.getSvgCommons(),
|
||||
'cx="' + x + '" cy="' + y + '" ',
|
||||
'r="', this.radius,
|
||||
'" style="', this.getSvgStyles(),
|
||||
|
|
@ -16683,7 +16909,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
.join(',');
|
||||
|
||||
markup.push(
|
||||
'<polygon ', this.getSvgId(),
|
||||
'<polygon ', this.getSvgCommons(),
|
||||
'points="', points,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(), '"',
|
||||
|
|
@ -16823,7 +17049,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
markup.push(
|
||||
'<ellipse ', this.getSvgId(),
|
||||
'<ellipse ', this.getSvgCommons(),
|
||||
'cx="0" cy="0" ',
|
||||
'rx="', this.rx,
|
||||
'" ry="', this.ry,
|
||||
|
|
@ -17052,7 +17278,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2;
|
||||
markup.push(
|
||||
'<rect ', this.getSvgId(),
|
||||
'<rect ', this.getSvgCommons(),
|
||||
'x="', x, '" y="', y,
|
||||
'" rx="', this.get('rx'), '" ry="', this.get('ry'),
|
||||
'" width="', this.width, '" height="', this.height,
|
||||
|
|
@ -17248,7 +17474,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
);
|
||||
}
|
||||
markup.push(
|
||||
'<', this.type, ' ', this.getSvgId(),
|
||||
'<', this.type, ' ', this.getSvgCommons(),
|
||||
'points="', points.join(''),
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(),
|
||||
|
|
@ -17939,7 +18165,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
var path = chunks.join(' ');
|
||||
addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
|
||||
markup.push(
|
||||
'<path ', this.getSvgId(),
|
||||
'<path ', this.getSvgCommons(),
|
||||
'd="', path,
|
||||
'" style="', this.getSvgStyles(),
|
||||
'" transform="', this.getSvgTransform(), addTransform,
|
||||
|
|
@ -18714,13 +18940,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
for (var i = 0, len = this._objects.length; i < len; i++) {
|
||||
this._objects[i].render(ctx);
|
||||
}
|
||||
this._drawClipPath(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if cache is dirty
|
||||
*/
|
||||
isCacheDirty: function() {
|
||||
if (this.callSuper('isCacheDirty')) {
|
||||
isCacheDirty: function(skipCanvas) {
|
||||
if (this.callSuper('isCacheDirty', skipCanvas)) {
|
||||
return true;
|
||||
}
|
||||
if (!this.statefullCache) {
|
||||
|
|
@ -18904,7 +19131,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
toSVG: function(reviver) {
|
||||
var markup = this._createBaseSVGMarkup();
|
||||
markup.push(
|
||||
'<g ', this.getSvgId(), 'transform="',
|
||||
'<g ', this.getSvgCommons(), 'transform="',
|
||||
/* avoiding styles intentionally */
|
||||
this.getSvgTransform(),
|
||||
this.getSvgTransformMatrix(),
|
||||
|
|
@ -19195,15 +19422,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
*/
|
||||
stateProperties: fabric.Object.prototype.stateProperties.concat('cropX', 'cropY'),
|
||||
|
||||
/**
|
||||
* When `true`, object is cached on an additional canvas.
|
||||
* default to false for images
|
||||
* since 1.7.0
|
||||
* @type Boolean
|
||||
* @default
|
||||
*/
|
||||
objectCaching: false,
|
||||
|
||||
/**
|
||||
* key used to retrieve the texture representing this image
|
||||
* since 2.0.0
|
||||
|
|
@ -19415,7 +19633,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
|
||||
}
|
||||
markup.push('<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n');
|
||||
var imageMarkup = ['\t<image ', this.getSvgId(), 'xlink:href="', this.getSvgSrc(true),
|
||||
var imageMarkup = ['\t<image ', this.getSvgCommons(), '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
|
||||
|
|
@ -19539,9 +19757,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
|
||||
filters = filters || this.filters || [];
|
||||
filters = filters.filter(function(filter) { return filter && !filter.isNeutralState(); });
|
||||
if (this.group) {
|
||||
this.set('dirty', true);
|
||||
}
|
||||
this.set('dirty', true);
|
||||
|
||||
// needs to clear out or WEBGL will not resize correctly
|
||||
this.removeTexture(this.cacheKey + '_filtered');
|
||||
|
|
@ -20420,6 +20636,7 @@ function copyGLTo2DPutImageData(gl, pipelineState) {
|
|||
* @tutorial {@link http://fabricjs.com/fabric-intro-part-2#image_filters}
|
||||
* @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
|
||||
*/
|
||||
fabric.Image = fabric.Image || { };
|
||||
fabric.Image.filters = fabric.Image.filters || { };
|
||||
|
||||
/**
|
||||
|
|
@ -28090,7 +28307,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
|
|||
style = filter === '' ? '' : ' style="' + filter + '"',
|
||||
textDecoration = this.getSvgTextDecoration(this);
|
||||
markup.push(
|
||||
'\t<g ', this.getSvgId(), 'transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
|
||||
'\t<g ', this.getSvgCommons(), 'transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
|
||||
style, '>\n',
|
||||
textAndBg.textBgRects.join(''),
|
||||
'\t\t<text xml:space="preserve" ',
|
||||
|
|
|
|||
2
dist/fabric.min.js
vendored
2
dist/fabric.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@
|
|||
"name": "fabric",
|
||||
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
||||
"homepage": "http://fabricjs.com/",
|
||||
"version": "2.3.6",
|
||||
"version": "2.4.0",
|
||||
"author": "Juriy Zaytsev <kangax@gmail.com>",
|
||||
"contributors": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue