mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-04-29 17:44:42 +00:00
parent
b46c5b4ca3
commit
49c7858596
5 changed files with 160 additions and 245 deletions
|
|
@ -1,217 +1,124 @@
|
|||
fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
|
||||
* @param {Object} [options] Options object
|
||||
* @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
|
||||
* @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
|
||||
* @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
|
||||
* @example <caption>Generate jpeg dataURL with lower quality</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'jpeg',
|
||||
* quality: 0.8
|
||||
* });
|
||||
* @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'png',
|
||||
* left: 100,
|
||||
* top: 100,
|
||||
* width: 200,
|
||||
* height: 200
|
||||
* });
|
||||
* @example <caption>Generate double scaled png dataURL</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'png',
|
||||
* multiplier: 2
|
||||
* });
|
||||
*/
|
||||
toDataURL: function (options) {
|
||||
options || (options = { });
|
||||
var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
|
||||
|
||||
var format = options.format || 'png',
|
||||
quality = options.quality || 1,
|
||||
multiplier = options.multiplier || 1,
|
||||
cropping = {
|
||||
left: options.left,
|
||||
top: options.top,
|
||||
width: options.width,
|
||||
height: options.height
|
||||
};
|
||||
fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
|
||||
|
||||
if (this._isRetinaScaling()) {
|
||||
multiplier *= fabric.devicePixelRatio;
|
||||
}
|
||||
/**
|
||||
* Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
|
||||
* @param {Object} [options] Options object
|
||||
* @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
|
||||
* @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
|
||||
* @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
|
||||
* @example <caption>Generate jpeg dataURL with lower quality</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'jpeg',
|
||||
* quality: 0.8
|
||||
* });
|
||||
* @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'png',
|
||||
* left: 100,
|
||||
* top: 100,
|
||||
* width: 200,
|
||||
* height: 200
|
||||
* });
|
||||
* @example <caption>Generate double scaled png dataURL</caption>
|
||||
* var dataURL = canvas.toDataURL({
|
||||
* format: 'png',
|
||||
* multiplier: 2
|
||||
* });
|
||||
*/
|
||||
toDataURL: function (options) {
|
||||
options || (options = { });
|
||||
|
||||
if (multiplier !== 1) {
|
||||
var format = options.format || 'png',
|
||||
quality = options.quality || 1,
|
||||
multiplier = options.multiplier || 1,
|
||||
cropping = {
|
||||
left: options.left || 0,
|
||||
top: options.top || 0,
|
||||
width: options.width || 0,
|
||||
height: options.height || 0,
|
||||
};
|
||||
return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
|
||||
}
|
||||
else {
|
||||
return this.__toDataURL(format, quality, cropping);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__toDataURL: function(format, quality, cropping) {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
|
||||
|
||||
this.renderAll();
|
||||
var origWidth = this.getWidth(),
|
||||
origHeight = this.getHeight(),
|
||||
scaledWidth = (cropping.width || this.getWidth()) * multiplier,
|
||||
scaledHeight = (cropping.width || this.getHeight()) * multiplier,
|
||||
zoom = this.getZoom(),
|
||||
newZoom = zoom * multiplier,
|
||||
vp = this.viewportTransform,
|
||||
translateX = (vp[4] - cropping.left) * multiplier,
|
||||
translateY = (vp[5] - cropping.top) * multiplier,
|
||||
newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
|
||||
originalInteractive = this.interactive;
|
||||
|
||||
var canvasEl = this.contextContainer.canvas,
|
||||
croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
|
||||
this.viewportTransform = newVp;
|
||||
// setting interactive to false avoid exporting controls
|
||||
this.interactive && (this.interactive = false);
|
||||
if (origWidth !== scaledWidth || origHeight !== scaledHeight) {
|
||||
// this.setDimensions is going to renderAll also;
|
||||
this.setDimensions({ width: scaledWidth, height: scaledHeight });
|
||||
}
|
||||
else {
|
||||
this.renderAll();
|
||||
}
|
||||
var data = this.__toDataURL(format, quality, cropping);
|
||||
originalInteractive && (this.interactive = originalInteractive);
|
||||
this.viewportTransform = vp;
|
||||
//setDimensions with no option object is taking care of:
|
||||
//this.width, this.height, this.renderAll()
|
||||
this.setDimensions({ width: origWidth, height: origHeight });
|
||||
return data;
|
||||
},
|
||||
|
||||
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
|
||||
if (format === 'jpg') {
|
||||
format = 'jpeg';
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__toDataURL: function(format, quality) {
|
||||
|
||||
var data = (fabric.StaticCanvas.supports('toDataURLWithQuality'))
|
||||
? (croppedCanvasEl || canvasEl).toDataURL('image/' + format, quality)
|
||||
: (croppedCanvasEl || canvasEl).toDataURL('image/' + format);
|
||||
var canvasEl = this.contextContainer.canvas;
|
||||
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
|
||||
if (format === 'jpg') {
|
||||
format = 'jpeg';
|
||||
}
|
||||
|
||||
if (croppedCanvasEl) {
|
||||
croppedCanvasEl = null;
|
||||
}
|
||||
var data = supportQuality
|
||||
? canvasEl.toDataURL('image/' + format, quality)
|
||||
: canvasEl.toDataURL('image/' + format);
|
||||
|
||||
return data;
|
||||
},
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__getCroppedCanvas: function(canvasEl, cropping) {
|
||||
/**
|
||||
* Exports canvas element to a dataurl image (allowing to change image size via multiplier).
|
||||
* @deprecated since 1.0.13
|
||||
* @param {String} format (png|jpeg)
|
||||
* @param {Number} multiplier
|
||||
* @param {Number} quality (0..1)
|
||||
* @return {String}
|
||||
*/
|
||||
toDataURLWithMultiplier: function (format, multiplier, quality) {
|
||||
return this.toDataURL({
|
||||
format: format,
|
||||
multiplier: multiplier,
|
||||
quality: quality
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var croppedCanvasEl,
|
||||
croppedCtx,
|
||||
shouldCrop = 'left' in cropping ||
|
||||
'top' in cropping ||
|
||||
'width' in cropping ||
|
||||
'height' in cropping;
|
||||
|
||||
if (shouldCrop) {
|
||||
|
||||
croppedCanvasEl = fabric.util.createCanvasElement();
|
||||
croppedCtx = croppedCanvasEl.getContext('2d');
|
||||
|
||||
croppedCanvasEl.width = cropping.width || this.width;
|
||||
croppedCanvasEl.height = cropping.height || this.height;
|
||||
|
||||
croppedCtx.drawImage(canvasEl, -cropping.left || 0, -cropping.top || 0);
|
||||
}
|
||||
|
||||
return croppedCanvasEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
__toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
|
||||
|
||||
var origWidth = this.getWidth(),
|
||||
origHeight = this.getHeight(),
|
||||
scaledWidth = origWidth * multiplier,
|
||||
scaledHeight = origHeight * multiplier,
|
||||
activeObject = this._activeObject,
|
||||
activeGroup = this._activeGroup,
|
||||
zoom = this.getZoom(),
|
||||
newZoom = zoom * multiplier / fabric.devicePixelRatio;
|
||||
|
||||
if (multiplier > 1) {
|
||||
this.setDimensions({ width: scaledWidth, height: scaledHeight });
|
||||
}
|
||||
|
||||
this.setZoom(newZoom);
|
||||
|
||||
if (cropping.left) {
|
||||
cropping.left *= multiplier;
|
||||
}
|
||||
if (cropping.top) {
|
||||
cropping.top *= multiplier;
|
||||
}
|
||||
if (cropping.width) {
|
||||
cropping.width *= multiplier;
|
||||
}
|
||||
else if (multiplier < 1) {
|
||||
cropping.width = scaledWidth;
|
||||
}
|
||||
if (cropping.height) {
|
||||
cropping.height *= multiplier;
|
||||
}
|
||||
else if (multiplier < 1) {
|
||||
cropping.height = scaledHeight;
|
||||
}
|
||||
|
||||
if (activeGroup) {
|
||||
// not removing group due to complications with restoring it with correct state afterwords
|
||||
this._tempRemoveBordersControlsFromGroup(activeGroup);
|
||||
}
|
||||
else if (activeObject && this.deactivateAll) {
|
||||
this.deactivateAll();
|
||||
}
|
||||
|
||||
var data = this.__toDataURL(format, quality, cropping);
|
||||
if (activeGroup) {
|
||||
this._restoreBordersControlsOnGroup(activeGroup);
|
||||
}
|
||||
else if (activeObject && this.setActiveObject) {
|
||||
this.setActiveObject(activeObject);
|
||||
}
|
||||
this.setZoom(zoom);
|
||||
//setDimensions with no option object is taking care of:
|
||||
//this.width, this.height, this.renderAll()
|
||||
this.setDimensions({ width: origWidth, height: origHeight });
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Exports canvas element to a dataurl image (allowing to change image size via multiplier).
|
||||
* @deprecated since 1.0.13
|
||||
* @param {String} format (png|jpeg)
|
||||
* @param {Number} multiplier
|
||||
* @param {Number} quality (0..1)
|
||||
* @return {String}
|
||||
*/
|
||||
toDataURLWithMultiplier: function (format, multiplier, quality) {
|
||||
return this.toDataURL({
|
||||
format: format,
|
||||
multiplier: multiplier,
|
||||
quality: quality
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_tempRemoveBordersControlsFromGroup: function(group) {
|
||||
group.origHasControls = group.hasControls;
|
||||
group.origBorderColor = group.borderColor;
|
||||
|
||||
group.hasControls = true;
|
||||
group.borderColor = 'rgba(0,0,0,0)';
|
||||
|
||||
group.forEachObject(function(o) {
|
||||
o.origBorderColor = o.borderColor;
|
||||
o.borderColor = 'rgba(0,0,0,0)';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_restoreBordersControlsOnGroup: function(group) {
|
||||
group.hideControls = group.origHideControls;
|
||||
group.borderColor = group.origBorderColor;
|
||||
|
||||
group.forEachObject(function(o) {
|
||||
o.borderColor = o.origBorderColor;
|
||||
delete o.origBorderColor;
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
47
src/node.js
47
src/node.js
|
|
@ -152,19 +152,30 @@
|
|||
|
||||
canvasEl.width = nodeCanvas.width;
|
||||
canvasEl.height = nodeCanvas.height;
|
||||
|
||||
options = options || { };
|
||||
options.nodeCanvas = nodeCanvas;
|
||||
options.nodeCacheCanvas = nodeCacheCanvas;
|
||||
var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
|
||||
fabricCanvas = new FabricCanvas(canvasEl, options);
|
||||
|
||||
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
|
||||
fabricCanvas.nodeCanvas = nodeCanvas;
|
||||
fabricCanvas.contextCache = nodeCacheCanvas.getContext('2d');
|
||||
fabricCanvas.nodeCacheCanvas = nodeCacheCanvas;
|
||||
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
|
||||
fabricCanvas.contextCache = nodeCacheCanvas.getContext('2d');
|
||||
fabricCanvas.Font = Canvas.Font;
|
||||
|
||||
return fabricCanvas;
|
||||
};
|
||||
|
||||
var originaInitStatic = fabric.StaticCanvas.prototype._initStatic;
|
||||
fabric.StaticCanvas.prototype._initStatic = function(el, options) {
|
||||
el = el || fabric.document.createElement('canvas');
|
||||
this.nodeCanvas = new Canvas(el.width, el.height);
|
||||
this.nodeCacheCanvas = new Canvas(el.width, el.height);
|
||||
originaInitStatic.call(this, el, options);
|
||||
this.contextContainer = this.nodeCanvas.getContext('2d');
|
||||
this.contextCache = this.nodeCacheCanvas.getContext('2d');
|
||||
this.Font = Canvas.Font;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
fabric.StaticCanvas.prototype.createPNGStream = function() {
|
||||
return this.nodeCanvas.createPNGStream();
|
||||
|
|
@ -174,24 +185,30 @@
|
|||
return this.nodeCanvas.createJPEGStream(opts);
|
||||
};
|
||||
|
||||
var origSetWidth = fabric.StaticCanvas.prototype.setWidth;
|
||||
fabric.StaticCanvas.prototype.setWidth = function(width, options) {
|
||||
origSetWidth.call(this, width, options);
|
||||
this.nodeCanvas.width = width;
|
||||
fabric.StaticCanvas.prototype._initRetinaScaling = function() {
|
||||
if (!this._isRetinaScaling()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
|
||||
this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
|
||||
this.nodeCanvas.width = this.width * fabric.devicePixelRatio;
|
||||
this.nodeCanvas.height = this.height * fabric.devicePixelRatio;
|
||||
this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
|
||||
return this;
|
||||
};
|
||||
if (fabric.Canvas) {
|
||||
fabric.Canvas.prototype.setWidth = fabric.StaticCanvas.prototype.setWidth;
|
||||
fabric.Canvas.prototype._initRetinaScaling = fabric.StaticCanvas.prototype._initRetinaScaling;
|
||||
}
|
||||
|
||||
var origSetHeight = fabric.StaticCanvas.prototype.setHeight;
|
||||
fabric.StaticCanvas.prototype.setHeight = function(height, options) {
|
||||
origSetHeight.call(this, height, options);
|
||||
this.nodeCanvas.height = height;
|
||||
var origSetBackstoreDimension = fabric.StaticCanvas.prototype._setBackstoreDimension;
|
||||
fabric.StaticCanvas.prototype._setBackstoreDimension = function(prop, value) {
|
||||
origSetBackstoreDimension.call(this, prop, value);
|
||||
this.nodeCanvas[prop] = value;
|
||||
return this;
|
||||
};
|
||||
if (fabric.Canvas) {
|
||||
fabric.Canvas.prototype.setHeight = fabric.StaticCanvas.prototype.setHeight;
|
||||
fabric.Canvas.prototype._setBackstoreDimension = fabric.StaticCanvas.prototype._setBackstoreDimension;
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1367,10 +1367,8 @@
|
|||
|
||||
el.width = boundingRect.width;
|
||||
el.height = boundingRect.height;
|
||||
|
||||
fabric.util.wrapElement(el, 'div');
|
||||
var canvas = new fabric.StaticCanvas(el, { enableRetinaScaling: options.enableRetinaScaling });
|
||||
|
||||
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
|
||||
if (options.format === 'jpg') {
|
||||
options.format = 'jpeg';
|
||||
|
|
|
|||
|
|
@ -179,7 +179,6 @@
|
|||
this._createLowerCanvas(el);
|
||||
this._initOptions(options);
|
||||
this._setImageSmoothing();
|
||||
|
||||
// only initialize retina scaling once
|
||||
if (!this.interactive) {
|
||||
this._initRetinaScaling();
|
||||
|
|
@ -222,7 +221,6 @@
|
|||
if (!this._isRetinaScaling()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
|
||||
this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
|
||||
|
||||
|
|
@ -459,28 +457,18 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
_createCanvasElement: function() {
|
||||
var element = fabric.document.createElement('canvas');
|
||||
_createCanvasElement: function(canvasEl) {
|
||||
var element = fabric.util.createCanvasElement(canvasEl)
|
||||
if (!element.style) {
|
||||
element.style = { };
|
||||
}
|
||||
if (!element) {
|
||||
throw CANVAS_INIT_ERROR;
|
||||
}
|
||||
this._initCanvasElement(element);
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
_initCanvasElement: function(element) {
|
||||
fabric.util.createCanvasElement(element);
|
||||
|
||||
if (typeof element.getContext === 'undefined') {
|
||||
throw CANVAS_INIT_ERROR;
|
||||
}
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -514,8 +502,7 @@
|
|||
* @param {HTMLElement} [canvasEl]
|
||||
*/
|
||||
_createLowerCanvas: function (canvasEl) {
|
||||
this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
|
||||
this._initCanvasElement(this.lowerCanvasEl);
|
||||
this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement(canvasEl);
|
||||
|
||||
fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
|
||||
|
||||
|
|
@ -834,14 +821,14 @@
|
|||
ctx.transform.apply(ctx, this.viewportTransform);
|
||||
this._renderObjects(ctx, objects);
|
||||
ctx.restore();
|
||||
if (!this.controlsAboveOverlay && this.drawControls) {
|
||||
if (!this.controlsAboveOverlay && this.interactive) {
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
if (this.clipTo) {
|
||||
ctx.restore();
|
||||
}
|
||||
this._renderOverlay(ctx);
|
||||
if (this.controlsAboveOverlay && this.drawControls) {
|
||||
if (this.controlsAboveOverlay && this.interactive) {
|
||||
this.drawControls(ctx);
|
||||
}
|
||||
this.fire('after:render');
|
||||
|
|
|
|||
|
|
@ -796,8 +796,10 @@
|
|||
});
|
||||
|
||||
test('loadFromJSON with no objects', function() {
|
||||
var c1 = new fabric.Canvas('c1', { backgroundColor: 'green', overlayColor: 'yellow' }),
|
||||
c2 = new fabric.Canvas('c2', { backgroundColor: 'red', overlayColor: 'orange' });
|
||||
var canvas1 = fabric.document.createElement('canvas'),
|
||||
canvas2 = fabric.document.createElement('canvas'),
|
||||
c1 = new fabric.Canvas(canvas1, { backgroundColor: 'green', overlayColor: 'yellow' }),
|
||||
c2 = new fabric.Canvas(canvas2, { backgroundColor: 'red', overlayColor: 'orange' });
|
||||
|
||||
var json = c1.toJSON();
|
||||
var fired = false;
|
||||
|
|
@ -812,8 +814,10 @@
|
|||
});
|
||||
|
||||
test('loadFromJSON without "objects" property', function() {
|
||||
var c1 = new fabric.Canvas('c1', { backgroundColor: 'green', overlayColor: 'yellow' }),
|
||||
c2 = new fabric.Canvas('c2', { backgroundColor: 'red', overlayColor: 'orange' });
|
||||
var canvas1 = fabric.document.createElement('canvas'),
|
||||
canvas2 = fabric.document.createElement('canvas'),
|
||||
c1 = new fabric.Canvas(canvas1, { backgroundColor: 'green', overlayColor: 'yellow' }),
|
||||
c2 = new fabric.Canvas(canvas2, { backgroundColor: 'red', overlayColor: 'orange' });
|
||||
|
||||
var json = c1.toJSON();
|
||||
var fired = false;
|
||||
|
|
@ -831,8 +835,10 @@
|
|||
});
|
||||
|
||||
test('loadFromJSON with empty fabric.Group', function() {
|
||||
var c1 = new fabric.Canvas('c1'),
|
||||
c2 = new fabric.Canvas('c2'),
|
||||
var canvas1 = fabric.document.createElement('canvas'),
|
||||
canvas2 = fabric.document.createElement('canvas'),
|
||||
c1 = new fabric.Canvas(canvas1),
|
||||
c2 = new fabric.Canvas(canvas2),
|
||||
group = new fabric.Group();
|
||||
|
||||
c1.add(group);
|
||||
|
|
|
|||
Loading…
Reference in a new issue