From a8bdd40612da55c5db05317b84f5e353fb2c1d74 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sun, 30 Dec 2018 15:49:50 +0100 Subject: [PATCH] Create a different dataUrl (#5452) * changed toDataUrl * ok not broken * ok not broken * fix * missed the offscreen --- src/mixins/canvas_dataurl_exporter.mixin.js | 82 ++++++++++----------- test/unit/canvas_static.js | 62 +++++++++++++--- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/src/mixins/canvas_dataurl_exporter.mixin.js b/src/mixins/canvas_dataurl_exporter.mixin.js index 294b011e323..477cf028a6a 100644 --- a/src/mixins/canvas_dataurl_exporter.mixin.js +++ b/src/mixins/canvas_dataurl_exporter.mixin.js @@ -41,70 +41,64 @@ var format = options.format || 'png', quality = options.quality || 1, - multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? 1 : 1 / this.getRetinaScaling()), - cropping = { - left: options.left || 0, - top: options.top || 0, - width: options.width || 0, - height: options.height || 0, - }; - return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier); + multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? this.getRetinaScaling() : 1), + canvasEl = this.toCanvasElement(multiplier, options); + return this.__toDataURL(canvasEl, format, quality); }, /** - * @private + * Create a new HTMLCanvas element painted with the current canvas content. + * No need to resize the actual one or repaint it. + * Will transfer object ownership to a new canvas, paint it, and set everything back. + * This is an intermediary step used to get to a dataUrl but also it is usefull to + * create quick image copies of a canvas without passing for the dataUrl string + * @param {Number} [multiplier] a zoom factor. + * @param {Object} [cropping] Cropping informations + * @param {Number} [cropping.left] Cropping left offset. + * @param {Number} [cropping.top] Cropping top offset. + * @param {Number} [cropping.width] Cropping width. + * @param {Number} [cropping.height] Cropping height. */ - __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) { - - var origWidth = this.width, - origHeight = this.height, - scaledWidth = (cropping.width || this.width) * multiplier, + toCanvasElement: function(multiplier, cropping) { + multiplier = multiplier || 1; + cropping = cropping || { }; + var scaledWidth = (cropping.width || this.width) * multiplier, scaledHeight = (cropping.height || this.height) * 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], + translateX = (vp[4] - (cropping.left || 0)) * multiplier, + translateY = (vp[5] - (cropping.top || 0)) * multiplier, originalInteractive = this.interactive, - originalSkipOffScreen = this.skipOffscreen, - needsResize = origWidth !== scaledWidth || origHeight !== scaledHeight; - - this.viewportTransform = newVp; + originalOffscreen = this.skipOffscreen, + originalContext = this.contextContainer, + newVp = [newZoom, 0, 0, newZoom, translateX, translateY], + canvasEl = fabric.util.createCanvasElement(); + canvasEl.width = scaledWidth; + canvasEl.height = scaledHeight; this.skipOffscreen = false; - // setting interactive to false avoid exporting controls this.interactive = false; - if (needsResize) { - this.setDimensions({ width: scaledWidth, height: scaledHeight }, { backstoreOnly: true }); - } - // call a renderAll to force sync update. This will cancel the scheduled requestRenderAll - // from setDimensions + this.viewportTransform = newVp; + this.contextContainer = canvasEl.getContext('2d'); + // will be renderAllExport(); this.renderAll(); - var data = this.__toDataURL(format, quality, cropping); - this.interactive = originalInteractive; - this.skipOffscreen = originalSkipOffScreen; this.viewportTransform = vp; - //setDimensions with no option object is taking care of: - //this.width, this.height, this.requestRenderAll() - if (needsResize) { - this.setDimensions({ width: origWidth, height: origHeight }, { backstoreOnly: true }); - } - this.renderAll(); - return data; + this.skipOffscreen = originalOffscreen; + this.contextContainer = originalContext; + 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(format, quality) { - - var canvasEl = this.contextContainer.canvas; - var data = supportQuality + __toDataURL: function(canvasEl, format, quality) { + return supportQuality ? canvasEl.toDataURL('image/' + format, quality) : canvasEl.toDataURL('image/' + format); - - return data; - }, + } }); })(); diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index e0f271ea02a..0e589204354 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -3,10 +3,10 @@ // var emptyImageCanvasData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAYAAADL1t+KAAAH7ElEQVR4nO3VMQ0AMAzAsPInvYHoMS2yEeTLHADge/M6AADYM3QACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIuMjH4b7osLFBAAAAAElFTkSuQmCC"; var CANVAS_SVG = '\n\n' + - '\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; + '\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; var CANVAS_SVG_VIEWBOX = '\n\n' + - '\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; + '\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; var PATH_JSON = '{"version":"' + fabric.version + '","objects": [{"type": "path", "version":"' + fabric.version + '", "originX": "left", "originY": "top", "left": 268, "top": 266, "width": 51, "height": 49,' + ' "fill": "rgb(0,0,0)", "stroke": null, "strokeWidth": 1, "scaleX": 1, "scaleY": 1, ' + @@ -143,8 +143,8 @@ // force creation of static canvas // TODO: fix this - var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600}); - var canvas2 = this.canvas2 = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600}); + var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 200, height: 200}); + var canvas2 = this.canvas2 = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 200, height: 200}); var lowerCanvasEl = canvas.lowerCanvasEl; @@ -158,6 +158,8 @@ beforeEach: function() { fabric.Object.__uid = 0; canvas.clear(); + canvas.setDimensions({ width: 200, heigth: 200 }); + canvas2.setDimensions({ width: 200, heigth: 200 }); canvas.backgroundColor = fabric.StaticCanvas.prototype.backgroundColor; canvas.backgroundImage = fabric.StaticCanvas.prototype.backgroundImage; canvas.overlayColor = fabric.StaticCanvas.prototype.overlayColor; @@ -454,6 +456,30 @@ assert.equal(canvas, canvas.renderAll()); }); + // QUnit.test('setDimensions', function(assert) { + // assert.ok(typeof canvas.setDimensions === 'function');; + // canvas.setDimensions({ width: 4, height: 5 }); + // assert.equal(canvas.getWidth(), 4); + // assert.equal(canvas.getHeight(), 5); + // assert.equal(canvas.lowerCanvasEl.style.width, '5px'); + // assert.equal(canvas.lowerCanvasEl.style.height, '4px'); + // }); + + QUnit.test('toCanvasElement', function(assert) { + assert.ok(typeof canvas.toCanvasElement === 'function');; + var canvasEl = canvas.toCanvasElement(); + assert.equal(canvasEl.width, canvas.getWidth(), 'get a canvas of same width'); + assert.equal(canvasEl.height, canvas.getHeight(), 'get a canvas of same height'); + }); + + QUnit.test('toCanvasElement with multiplier', function(assert) { + assert.ok(typeof canvas.toCanvasElement === 'function'); + var multiplier = 2; + var canvasEl = canvas.toCanvasElement(multiplier); + assert.equal(canvasEl.width, canvas.getWidth() * multiplier, 'get a canvas of multiplied width'); + assert.equal(canvasEl.height, canvas.getHeight() * multiplier, 'get a canvas of multiplied height'); + }); + QUnit.test('toDataURL', function(assert) { assert.ok(typeof canvas.toDataURL === 'function'); if (!fabric.Canvas.supports('toDataURL')) { @@ -885,12 +911,12 @@ canvas.renderOnAddRemove = false; canvas.backgroundImage = imageBG; canvas.overlayImage = imageOL; - var expectedSVG = '\n\n\nCreated with Fabric.js ' + fabric.version + '\n\n\n\n\t\n\n\n\t\n\n'; + var expectedSVG = '\n\n\nCreated with Fabric.js ' + fabric.version + '\n\n\n\n\t\n\n\n\t\n\n'; var svg1 = canvas.toSVG(); assert.equal(svg1, expectedSVG, 'svg with bg and overlay do not match'); imageBG.excludeFromExport = true; imageOL.excludeFromExport = true; - var expectedSVG2 = '\n\n\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; + var expectedSVG2 = '\n\n\nCreated with Fabric.js ' + fabric.version + '\n\n\n'; var svg2 = canvas.toSVG(); assert.equal(svg2, expectedSVG2, 'svg without bg and overlay do not match'); canvas.backgroundImage = null; @@ -1470,7 +1496,7 @@ QUnit.test('getSetWidth', function(assert) { assert.ok(typeof canvas.getWidth === 'function'); - assert.equal(canvas.getWidth(), 600); + assert.equal(canvas.getWidth(), 200); assert.equal(canvas.setWidth(444), canvas, 'should be chainable'); assert.equal(canvas.getWidth(), 444); assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px'); @@ -1478,7 +1504,7 @@ QUnit.test('getSetHeight', function(assert) { assert.ok(typeof canvas.getHeight === 'function'); - assert.equal(canvas.getHeight(), 600); + assert.equal(canvas.getHeight(), 200); assert.equal(canvas.setHeight(765), canvas, 'should be chainable'); assert.equal(canvas.getHeight(), 765); assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px'); @@ -1497,7 +1523,16 @@ canvas.setHeight('100%', { cssOnly: true }); assert.equal(canvas.lowerCanvasEl.style.height, '100%', 'Should be as the css only value'); - assert.equal(canvas.getWidth(), 123, 'Should be as the none css only value'); + assert.equal(canvas.getHeight(), 123, 'Should be as the none css only value'); + }); + + QUnit.test('setDimensions css only', function(assert) { + canvas.setDimensions({ width: 200, height: 200 }); + canvas.setDimensions({ width: '250px', height: '350px' }, { cssOnly: true }); + assert.equal(canvas.lowerCanvasEl.style.width, '250px', 'Should be as none backstore only value + "px"'); + assert.equal(canvas.lowerCanvasEl.style.height, '350px', 'Should be as none backstore only value + "px"'); + assert.equal(canvas.getWidth(), 200, 'Should be as the backstore only value'); + assert.equal(canvas.getHeight(), 200, 'Should be as the backstore only value'); }); QUnit.test('setWidth backstore only', function(assert) { @@ -1516,6 +1551,15 @@ assert.equal(canvas.getHeight(), 500, 'Should be as the backstore only value'); }); + QUnit.test('setDimensions backstore only', function(assert) { + canvas.setDimensions({ width: 200, height: 200 }); + canvas.setDimensions({ width: 250, height: 350 }, { backstoreOnly: true }); + assert.equal(canvas.lowerCanvasEl.style.width, 200 + 'px', 'Should be as none backstore only value + "px"'); + assert.equal(canvas.lowerCanvasEl.style.height, 200 + 'px', 'Should be as none backstore only value + "px"'); + assert.equal(canvas.getWidth(), 250, 'Should be as the backstore only value'); + assert.equal(canvas.getHeight(), 350, 'Should be as the backstore only value'); + }); + QUnit.test('fxRemove', function(assert) { var done = assert.async(); assert.ok(typeof canvas.fxRemove === 'function');