mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-03-16 22:10:32 +00:00
Add toCanvasElement for object, fixes a bug, speedUp cloneAsImage (#5481)
* a couple of changes * removed unnecessary branching
This commit is contained in:
parent
9c35ed263b
commit
68a6af40a1
7 changed files with 126 additions and 152 deletions
|
|
@ -1,7 +1,4 @@
|
|||
(function () {
|
||||
|
||||
var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
|
||||
|
||||
fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +40,7 @@
|
|||
quality = options.quality || 1,
|
||||
multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? this.getRetinaScaling() : 1),
|
||||
canvasEl = this.toCanvasElement(multiplier, options);
|
||||
return this.__toDataURL(canvasEl, format, quality);
|
||||
return fabric.util.toDataURL(canvasEl, format, quality);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -93,17 +90,6 @@
|
|||
this.interactive = originalInteractive;
|
||||
return canvasEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* since 2.5.0 does not need to be on canvas instance anymore.
|
||||
* leave it here for context;
|
||||
* @private
|
||||
*/
|
||||
__toDataURL: function(canvasEl, format, quality) {
|
||||
return supportQuality
|
||||
? canvasEl.toDataURL('image/' + format, quality)
|
||||
: canvasEl.toDataURL('image/' + format);
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1544,6 +1544,7 @@
|
|||
|
||||
/**
|
||||
* Creates an instance of fabric.Image out of an object
|
||||
* could make use of both toDataUrl or toCanvasElement.
|
||||
* @param {Function} callback callback, invoked with an instance as a first argument
|
||||
* @param {Object} [options] for clone as image, passed to toDataURL
|
||||
* @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
|
||||
|
|
@ -1559,20 +1560,16 @@
|
|||
* @return {fabric.Object} thisArg
|
||||
*/
|
||||
cloneAsImage: function(callback, options) {
|
||||
var dataUrl = this.toDataURL(options);
|
||||
fabric.util.loadImage(dataUrl, function(img) {
|
||||
if (callback) {
|
||||
callback(new fabric.Image(img));
|
||||
}
|
||||
});
|
||||
var canvasEl = this.toCanvasElement(options);
|
||||
if (callback) {
|
||||
callback(new fabric.Image(canvasEl));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts an object into a data-url-like string
|
||||
* Converts an object into a HTMLCanvas element
|
||||
* @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
|
||||
|
|
@ -1583,11 +1580,12 @@
|
|||
* @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) {
|
||||
toCanvasElement: function(options) {
|
||||
options || (options = { });
|
||||
|
||||
var utils = fabric.util, origParams = utils.saveObjectTransform(this),
|
||||
originalShadow = this.shadow, abs = Math.abs;
|
||||
originalShadow = this.shadow, abs = Math.abs,
|
||||
multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? fabric.devicePixelRatio : 1);
|
||||
|
||||
if (options.withoutTransform) {
|
||||
utils.resetObjectTransform(this);
|
||||
|
|
@ -1613,7 +1611,7 @@
|
|||
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,
|
||||
enableRetinaScaling: false,
|
||||
renderOnAddRemove: false,
|
||||
skipOffscreen: false,
|
||||
});
|
||||
|
|
@ -1624,10 +1622,10 @@
|
|||
|
||||
var originalCanvas = this.canvas;
|
||||
canvas.add(this);
|
||||
var data = canvas.toDataURL(options);
|
||||
var canvasEl = canvas.toCanvasElement(multiplier || 1, options);
|
||||
this.shadow = originalShadow;
|
||||
this.set(origParams).setCoords();
|
||||
this.canvas = originalCanvas;
|
||||
this.set(origParams).setCoords();
|
||||
// canvas.dispose will call image.dispose that will nullify the elements
|
||||
// since this canvas is a simple element for the process, we remove references
|
||||
// to objects in this way in order to avoid object trashing.
|
||||
|
|
@ -1635,7 +1633,27 @@
|
|||
canvas.dispose();
|
||||
canvas = null;
|
||||
|
||||
return data;
|
||||
return canvasEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts an object into a data-url-like string
|
||||
* @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
|
||||
* @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 = { });
|
||||
return fabric.util.toDataURL(this.toCanvasElement(options), options.format || 'png', options.quality || 1);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1795,7 +1795,7 @@
|
|||
* (either those of HTMLCanvasElement itself, or rendering context)
|
||||
*
|
||||
* @param {String} methodName Method to check support for;
|
||||
* Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
|
||||
* Could be one of "setLineDash"
|
||||
* @return {Boolean | null} `true` if method is supported (or at least exists),
|
||||
* `null` if canvas element or context can not be initialized
|
||||
*/
|
||||
|
|
@ -1813,23 +1813,9 @@
|
|||
|
||||
switch (methodName) {
|
||||
|
||||
case 'getImageData':
|
||||
return typeof ctx.getImageData !== 'undefined';
|
||||
|
||||
case 'setLineDash':
|
||||
return typeof ctx.setLineDash !== 'undefined';
|
||||
|
||||
case 'toDataURL':
|
||||
return typeof el.toDataURL !== 'undefined';
|
||||
|
||||
case 'toDataURLWithQuality':
|
||||
try {
|
||||
el.toDataURL('image/jpeg', 0);
|
||||
return true;
|
||||
}
|
||||
catch (e) { }
|
||||
return false;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,6 +583,7 @@
|
|||
|
||||
/**
|
||||
* Creates a canvas element that is a copy of another and is also painted
|
||||
* @param {CanvasElement} canvas to copy size and content of
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @return {CanvasElement} initialized canvas element
|
||||
|
|
@ -595,6 +596,19 @@
|
|||
return newCanvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* since 2.6.0 moved from canvas instance to utility.
|
||||
* @param {CanvasElement} canvasEl to copy size and content of
|
||||
* @param {String} format 'jpeg' or 'png', in some browsers 'webp' is ok too
|
||||
* @param {Number} quality <= 1 and > 0
|
||||
* @static
|
||||
* @memberOf fabric.util
|
||||
* @return {String} data url
|
||||
*/
|
||||
toDataURL: function(canvasEl, format, quality) {
|
||||
return canvasEl.toDataURL('image/' + format, quality);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates image element (works on client and node)
|
||||
* @static
|
||||
|
|
|
|||
|
|
@ -1170,16 +1170,11 @@
|
|||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
}
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
});
|
||||
|
||||
// QUnit.test('getPointer', function(assert) {
|
||||
|
|
|
|||
|
|
@ -482,20 +482,15 @@
|
|||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var rect = new fabric.Rect({width: 100, height: 100, fill: 'red', top: 0, left: 0});
|
||||
canvas.add(rect);
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
//we can just compare that the dataUrl generated differs from the dataURl of an empty canvas.
|
||||
assert.equal(dataURL.substring(200, 210) !== 'AAAAAAAAAA', true);
|
||||
}
|
||||
var rect = new fabric.Rect({width: 100, height: 100, fill: 'red', top: 0, left: 0});
|
||||
canvas.add(rect);
|
||||
var dataURL = canvas.toDataURL();
|
||||
// don't compare actual data url, as it is often browser-dependent
|
||||
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
//we can just compare that the dataUrl generated differs from the dataURl of an empty canvas.
|
||||
assert.equal(dataURL.substring(200, 210) !== 'AAAAAAAAAA', true);
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL with enableRetinaScaling: true and no multiplier', function(assert) {
|
||||
|
|
@ -604,39 +599,28 @@
|
|||
});
|
||||
|
||||
QUnit.test('toDataURL jpeg', function(assert) {
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
try {
|
||||
var dataURL = canvas.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
else {
|
||||
try {
|
||||
var dataURL = canvas.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
// node-canvas does not support jpeg data urls
|
||||
catch (err) {
|
||||
assert.ok(true);
|
||||
}
|
||||
// node-canvas does not support jpeg data urls
|
||||
catch (err) {
|
||||
assert.ok(true);
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL cropping', function(assert) {
|
||||
var done = assert.async();
|
||||
assert.ok(typeof canvas.toDataURL === 'function');
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
done();
|
||||
}
|
||||
else {
|
||||
var croppingWidth = 75,
|
||||
croppingHeight = 50,
|
||||
dataURL = canvas.toDataURL({width: croppingWidth, height: croppingHeight});
|
||||
var croppingWidth = 75,
|
||||
croppingHeight = 50,
|
||||
dataURL = canvas.toDataURL({width: croppingWidth, height: croppingHeight});
|
||||
|
||||
fabric.Image.fromURL(dataURL, function (img) {
|
||||
assert.equal(img.width, croppingWidth, 'Width of exported image should correspond to cropping width');
|
||||
assert.equal(img.height, croppingHeight, 'Height of exported image should correspond to cropping height');
|
||||
done();
|
||||
});
|
||||
}
|
||||
fabric.Image.fromURL(dataURL, function (img) {
|
||||
assert.equal(img.width, croppingWidth, 'Width of exported image should correspond to cropping width');
|
||||
assert.equal(img.height, croppingHeight, 'Height of exported image should correspond to cropping height');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('centerObjectH', function(assert) {
|
||||
|
|
|
|||
|
|
@ -410,74 +410,71 @@
|
|||
QUnit.test('cloneAsImage', function(assert) {
|
||||
var done = assert.async();
|
||||
var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red', strokeWidth: 0 });
|
||||
|
||||
assert.ok(typeof cObj.cloneAsImage === 'function');
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
fabric.log('`toDataURL` is not supported by this environment; skipping `cloneAsImage` test (as it relies on `toDataURL`)');
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 100, 'the image has same dimension of object');
|
||||
done();
|
||||
}
|
||||
else {
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 100, 'the image has same dimension of object');
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('cloneAsImage with retina scaling enabled', function(assert) {
|
||||
var done = assert.async();
|
||||
var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red', strokeWidth: 0 });
|
||||
fabric.devicePixelRatio = 2;
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
fabric.log('`toDataURL` is not supported by this environment; skipping `cloneAsImage` test (as it relies on `toDataURL`)');
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 200, 'the image has been scaled by retina');
|
||||
fabric.devicePixelRatio = 1;
|
||||
done();
|
||||
}
|
||||
else {
|
||||
cObj.cloneAsImage(function(image) {
|
||||
assert.ok(image);
|
||||
assert.ok(image instanceof fabric.Image);
|
||||
assert.equal(image.width, 200, 'the image has been scaled by retina');
|
||||
fabric.devicePixelRatio = 1;
|
||||
done();
|
||||
}, { enableRetinaScaling: true });
|
||||
}
|
||||
}, { enableRetinaScaling: true });
|
||||
});
|
||||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
// var data =
|
||||
// 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQA'+
|
||||
// 'AABkCAYAAABw4pVUAAAA+UlEQVR4nO3RoRHAQBDEsOu/6YR+B2s'+
|
||||
// 'gIO4Z3919pMwDMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AI'+
|
||||
// 'akzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR'+
|
||||
// '6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHoAhafMADEm'+
|
||||
// 'bB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0'+
|
||||
// 'uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPw'+
|
||||
// 'JC0eQCGpM0DMCRtHsDjB5K06yueJFXJAAAAAElFTkSuQmCC';
|
||||
QUnit.test('toCanvasElement', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
|
||||
assert.ok(typeof cObj.toCanvasElement === 'function');
|
||||
|
||||
var canvasEl = cObj.toCanvasElement();
|
||||
|
||||
assert.ok(typeof canvasEl.getContext === 'function', 'the element returned is a canvas');
|
||||
});
|
||||
|
||||
QUnit.test('toCanvasElement does not modify oCoords on zoomed canvas', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
canvas.setZoom(2);
|
||||
canvas.add(cObj);
|
||||
var originaloCoords = cObj.oCoords;
|
||||
var originalaCoords = cObj.aCoords;
|
||||
cObj.toCanvasElement();
|
||||
assert.deepEqual(cObj.oCoords, originaloCoords, 'cObj did not get object coords changed');
|
||||
assert.deepEqual(cObj.aCoords, originalaCoords, 'cObj did not get absolute coords changed');
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('toDataURL', function(assert) {
|
||||
var cObj = new fabric.Rect({
|
||||
width: 100, height: 100, fill: 'red', strokeWidth: 0
|
||||
});
|
||||
|
||||
assert.ok(typeof cObj.toDataURL === 'function');
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var dataURL = cObj.toDataURL();
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
var dataURL = cObj.toDataURL();
|
||||
assert.equal(typeof dataURL, 'string');
|
||||
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
|
||||
|
||||
try {
|
||||
dataURL = cObj.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log('jpeg toDataURL not supported');
|
||||
}
|
||||
try {
|
||||
dataURL = cObj.toDataURL({ format: 'jpeg' });
|
||||
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
|
||||
}
|
||||
catch (err) {
|
||||
fabric.log('jpeg toDataURL not supported');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -496,16 +493,10 @@
|
|||
width: 100, height: 100, fill: 'red'
|
||||
});
|
||||
canvas.add(cObj);
|
||||
var objCanvas = cObj.canvas;
|
||||
cObj.toDataURL();
|
||||
|
||||
if (!fabric.Canvas.supports('toDataURL')) {
|
||||
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
|
||||
}
|
||||
else {
|
||||
var objCanvas = cObj.canvas;
|
||||
cObj.toDataURL();
|
||||
|
||||
assert.equal(objCanvas, cObj.canvas);
|
||||
}
|
||||
assert.equal(objCanvas, cObj.canvas);
|
||||
});
|
||||
|
||||
QUnit.test('isType', function(assert) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue