Download Canvas as PNG in fabric.js giving network Error
The problem you are facing is not directly related to fabricjs, (nor canvas and not even javascript btw), but comes from limitations some browsers (including Chrome) does have on the maximum length for the src
attribute of an Anchor Element (<a>
) with the donwload attribute.
When this limit is reached, then the only thing you've got is this uncatchable "Network Error" in the console ; the download as failed, but you as the developer can't know about it.
As proposed in this (you-refused-to-mark-as-) duplicate, the solution is either to directly get a Blob when available (for canvas, you may call its toBlob()
method, or to first convert your dataURI to a Blob, and then create an object URL from this Blob.
Fabricjs doesn't seem to have a toBlob
function implemented yet, so in your exact case, you'll have to do the later.
You can find many scripts to convert dataURI to Blob, one is available in MDN's polyfill to Canvas.toBlob()
method.
Then it would look like this :
// edited from https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill
function dataURIToBlob(dataURI, callback) {
var binStr = atob(dataURI.split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr]));
}
var callback = function(blob) {
var a = document.createElement('a');
a.download = fileName;
a.innerHTML = 'download';
// the string representation of the object URL will be small enough to workaround the browser's limitations
a.href = URL.createObjectURL(blob);
// you must revoke the object URL,
// but since we can't know when the download occured, we have to attach it on the click handler..
a.onclick = function() {
// ..and to wait a frame
requestAnimationFrame(function() {
URL.revokeObjectURL(a.href);
});
a.removeAttribute('href')
};
};
dataURIToBlob(yourDataURL, callback);
Saving a fabricjs canvas as PNG is not working
Without needing to inspect the code, if you get this error:
Uncaught DOMException: Failed to execute 'toDataURL' on
'HTMLCanvasElement': Tainted canvases may not be exported.
at n.__toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111706)
at n.__toDataURLWithMultiplier (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111496)
at n.toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111060)
at downloadFabric (file://portrait.js:207:21)
at HTMLButtonElement.onclick (file://index.html:62:114)
your canvas is tainted.
That means you loaded some images without specifying the crossOrigin attribute.
Fabricjs does support crossOrigin option fromUrl as a third argument after the callback ( please check docs here http://fabricjs.com/docs/fabric.Image.html#.fromURL )
in the function replaceImage you have to take care of it by yourself before setting the .src property to the image.
// oImgObj bread and butter, kudos @grunt
function replaceImage(oImgObj, imgUrl) {
if (!isImageLoaded) return; //return if initial image not loaded
var imgElem = oImgObj._element; //reference to actual image element
imgElem.src = imgUrl; //set image source
imgElem.onload = () => canvas.renderAll(); //render on image load
}
// initialize default frame (light brown wood oval)
fabric.Image.fromURL('https://i.imgur.com/DrzSWSa.png', function(img) {
isImageLoaded = true;
oImg = img.set({
selectable: false,
evented: false,
}).scale(0.5);
canvas.add(oImg).renderAll();
canvas.sendToBack(oImg);
});
How save image with canvas in fabric.js
Hello,
you have to create an image object (tshirt) with a text object that holds the message.
- to do that , load the image with fabric.Image.fromURL() function and inside the function , also create a text object that is going to show the tshirt message.
so, your image and text belong to a group object. - every time you want to load new text , you call the loadText function and you change the text object.
- i also added 4 buttons in order to manupulate up/down/left/right the text .
you can export the canvas + image+ text into the function saveImg(),
but on the jsfiddle you will get a security message for Tained canvases.
that happens because on the example i load the image from another domain and the code runs on another domain, you can run that code on your web application with no problem at all.that is the code :
var canvas = new fabric.Canvas('c');
var scaleFactor=0.4
canvas.backgroundColor = 'yellow';
canvas.renderAll();
var myImg = 'http://izy.urweb.eu/files/tshirt.jpg';
fabric.Image.fromURL(myImg, function(myImg) {
var img1 = myImg.scale(scaleFactor).set({ left: 0, top: 0 });
var text = new fabric.Text('the_text_sample\nand more', {
fontFamily: 'Arial',
fontSize:20,
});
text.set("top",myImg.height*scaleFactor-myImg.height*scaleFactor+150);
text.set("left",myImg.width*scaleFactor/2-text.width/2);
var group = new fabric.Group([ img1,text ], { left: 10, top: 10 });
canvas.add(group);
});
$('#loadText').on('click',loadText);
$('#saveImg').on('click',saveImg);
function loadText(){
console.log($('#logo').val());
canvas._objects[0]._objects[1].text = $('#logo').val();
canvas.renderAll();
}
function saveImg(){
console.log('export image');
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
}
else {
window.open(canvas.toDataURL('png'));
}
}
$('#left').on('click',function(){
canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left-1);
canvas.renderAll();
})
$('#right').on('click',function(){
canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left+1);
canvas.renderAll();
})
$('#top').on('click',function(){
canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top-1);
canvas.renderAll();
})
$('#bottom').on('click',function(){
canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top+1);
canvas.renderAll();
})that is the jsfiddle example: http://jsfiddle.net/tornado1979/zrazuhcq/1/
hope helps, good luck.
Failed - Network Error When trying to provide download in HTML5 using 'download' attribute
Apparently this is a Chrome issue with the data-URL getting too long. I'm still working through it myself, but there apparently are some solutions involving Blob-objects.
See here: Download Canvas as PNG in fabric.js giving network Error
And here: How to export JavaScript array info to csv (on client side)?
Downloading Canvas element to an image
The one way to save is exporting as an image... You already found this solution, and it's the best one i think ;)
var canvas = document.getElementById("mycanvas");
var img = canvas.toDataURL("image/png");
document.write('<img src="'+img+'"/>');
You can use different image types. Change the mimetype in this function:
canvas.toDataURL("image/jpeg");
An other way to save canvas data (into a PDF) is using the wkhtmltopdf Library
Cheers. Frank
frankneff.ch / @frankneff
Draw Image on Canvas and export print quality image
You should not style canvas directly with dimensions.
Set the canvas attribute width and height, without exceeding the screen dimensions:
<canvas width="500" height="400" ></canvas>
When exporting to image use
canvas.toDataURL({multiplier: 4});
This will give you a canvas 4 times bigger the one on the screen.
Styling the canvas with the style to reduce its diemension and having a bigger image with dataURL will make the overall experience worse.
The style squeeze the canvas to your eyes with something that fabricjs is not ready to understand. So you will not get normal sized controls.
//remove those
width:400px !important;
height:600px !important;
http://jsfiddle.net/Q3TMA/1043/
FabricJS - Tainted canvases may not be exported JS Error when run toDataURL
You need to add crossOrigin: 'anonymous'
to the image element. Have added in fabric.Image.fromURL
, or you need to use images from same server, or where crossOrigin is defined.
DEMO
fabric.ImageContainer = fabric.util.createClass(fabric.Rect, { type: 'image-container', initialize: function(options) { options || (options = { }); options.content || (options.content = { }); options.content.angle = options.content.angle || 0; this.callSuper('initialize', options); this.set({ objectCaching: false, ddpPreviousCenter: this.getCenterPoint() }); this.on('scaling', function(el){ this.set({ width: this.width * this.scaleX, height: this.height * this.scaleY, scaleX: 1, scaleY: 1 }); this.setCoords(); this._drawImage(); }.bind(this)); this.on('modified', function(el){ this._updateContentCoords(); }.bind(this)); this._drawImage(); }, _updateContentCoords: function(){ const ddpPreviousCenter = {...this.ddpPreviousCenter}; const content = {...this.content}; const shiftX = this.getCenterPoint().x - ddpPreviousCenter.x; const shiftY = this.getCenterPoint().y - ddpPreviousCenter.y;
content.left += shiftX; content.top += shiftY; this.set({ ddpPreviousCenter: this.getCenterPoint(), content }); }, _drawImage: function() { const scaleFactor = 1; const imgSrc = [ 'https://picsum.photos/', this.content.width * scaleFactor, '/', this.content.height * scaleFactor ].join('');
fabric.Image.fromURL(imgSrc, function(img) { img.set({ left: this.content.left - this.left + this.content.width / 2, top: this.content.top - this.top + this.content.height / 2, scaleX: 1, scalY: 1, angle: this.content.angle, originX: 'center', originY: 'center', }); // img.scaleToWidth(this.content.width); const patternSourceCanvas = new fabric.StaticCanvas(); patternSourceCanvas.setDimensions({ width: this.width, height: this.height });
patternSourceCanvas.setBackgroundColor(this.backgroundColor); patternSourceCanvas.add(img); patternSourceCanvas.renderAll();
const pattern = new fabric.Pattern({ source: function() { return patternSourceCanvas.getElement(); }, repeat: 'no-repeat' }); this.set({ fill: pattern });
this.canvas.renderAll(); this.canvas.fire('image:pattern:loaded'); }.bind(this),{ crossOrigin: 'anonymous' }); }, toObject: function(options) { return fabric.util.object.extend(this.callSuper('toObject'), { ddpPreviousCenter: this.get('ddpPreviousCenter'), content: this.get('content'), });
// return fabric.util.object.extend(this.callSuper('toObject'), {}); }, fromObject: function(object, callback) { return fabric.Object._fromObject('ImageContainer', object, callback); }, _render: function(ctx) { this.callSuper('_render', ctx); }});
fabric.ImageContainer.__fromObject = function(object, callback, forceAsync) { if (!forceAsync) { fabric.util.enlivenPatterns([object.fill, object.stroke], function(patterns) { console.log(patterns); object.fill = patterns[0]; object.stroke = patterns[1]; var rect = new fabric.ImageContainer(object); callback && callback(rect); }); } else { var rect = new fabric.ImageContainer(object); callback && callback(rect); return rect; } };
// =========================================================================
let store;const canvas = new fabric.Canvas('paper');
const container = new fabric.ImageContainer({ left: 10, top: 10, width: 150, height: 150, backgroundColor: 'green', content: { left: 20, top: 20, width: 130, height: 130 }});
canvas.on('image:pattern:loaded', function(){ $('#img').attr('src', this.toDataURL());});
canvas.add(container);canvas.renderAll();
#paper { border: solid 1px red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.2.3/fabric.js"></script><img id="image" /><canvas id="paper" width="400" height="200" style="border:1px solid #ccc"></canvas><img id="img" />
How to save an HTML5 Canvas as an image on a server?
Here is an example of how to achieve what you need:
- Draw something (taken from canvas tutorial)
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// begin custom shape
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.bezierCurveTo(430, 40, 370, 30, 340, 50);
context.bezierCurveTo(320, 5, 250, 20, 250, 50);
context.bezierCurveTo(200, 5, 150, 20, 170, 80);
// complete custom shape
context.closePath();
context.lineWidth = 5;
context.fillStyle = '#8ED6FF';
context.fill();
context.strokeStyle = 'blue';
context.stroke();
</script>
Related Topics
Make Function Wait Until Element Exists
Integrating Dropzone.Js into Existing HTML Form with Other Fields
Cropping Images in the Browser Before the Upload
Print Specific Part of Webpage
Drop-Down Box Dependent on the Option Selected in Another Drop-Down Box
Get Value of Input Field Inside an Iframe
Template Does Not Update When Using Ui-Router and Ion-Tabs
Use Functions Defined in Es6 Module Directly in HTML
Getelementsbyname() Not Working
JavaScript Function Name Cannot Set as Click
Add a "Hook" to All Ajax Requests on a Page
Html5 Filereader How to Return Result
How to Check If a Variable Is an Integer in JavaScript
Alternation Operator Inside Square Brackets Does Not Work
Event.Returnvalue Is Deprecated. Please Use the Standard Event.Preventdefault() Instead