changed to dataurl export (#3314)

* changed to dataurl
This commit is contained in:
Andrea Bogazzi 2016-10-02 19:37:20 +02:00 committed by GitHub
parent b46c5b4ca3
commit 49c7858596
5 changed files with 160 additions and 245 deletions

View file

@ -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;
});
}
});
})();

View file

@ -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;
}
})();

View file

@ -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';

View file

@ -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');

View file

@ -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);