No able to save fabric canvas as image using FileSaver.js - javascript

I have included Fabric.js and Filesaver.js in my code but still
I am getting "Uncaught SecurityError: Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported." error whenever I am trying to save the fabric canvas.
I referred: https://www.youtube.com/watch?v=ng8OJ6a-wQY
Is there a way I could be able to save the canvas to a shared directory location?
//-----------------------------Getting hold of Canvas---------------------------------------
var canvas = new fabric.Canvas('canvas');
canvas.setHeight(window.innerHeight * 0.75);
canvas.setWidth(window.innerWidth * 0.75);
drawBackground();
//--------------------------Image Rendering-------------------------------------------------
function drawBackground() {
fabric.Image.fromURL('https://upload.wikimedia.org/wikipedia/commons/f/f9/Phoenicopterus_ruber_in_S%C3%A3o_Paulo_Zoo.jpg', function(img) {
img.crossOrigin = "Anonymous";
img.scaleToWidth(window.innerWidth * 0.75);
img.scaleToHeight(window.innerHeight * 0.75);
canvas.setBackgroundImage(img);
canvas.renderAll();
});
}
//------------------------Rectangle---------------------------------------------------------
window.addRect = function() {
var box = new fabric.Rect({
left: 0,
top: 0,
stroke: 'red',
fill: 'rgba(255,0,0,.4)',
width: 50,
height: 50,
});
box.hasRotatingPoint = false;
canvas.add(box);
}
//---------------------Circle-------------------------------------------------------------
window.addCircle = function() {
var circle = new fabric.Circle({
left: 0,
top: 0,
radius: 50,
stroke: 'green',
fill: 'transparent',
});
circle.hasRotatingPoint = false;
canvas.add(circle);
}
//-----------------------Line Arrow-----------------------------------------------------
window.addArrow = function() {
var arrowbox = new fabric.Rect({
left: 0,
top: 0,
stroke: 'red',
fill: 'red',
width: 1,
height: 50,
});
var arrowtriangle = new fabric.Triangle({
width: 10, height: 10, fill: 'red', left: -4, top: -10
});
var arrowgroup = new fabric.Group([ arrowbox, arrowtriangle ], {
left: 150,
top: 100,
angle: 90
});
canvas.add(arrowgroup);
}
//-----------------------Save Canvas---------------------------------------------------------
window.saveCanvas = function(){
canvas.getElement().toBlob(function(blob){
saveAs(blob, annotation.png);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.min.js"></script>
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.min.js"></script>
<canvas id="canvas" width="800" height="600" style="border:1px solid red;"></canvas>
<button onClick="addCircle()">Circle</button>
<button onClick="addRect()">Box</button>
<button onClick="addArrow()">Arrow</button>
<button onClick="saveCanvas()">Save</button>

I am also facing same problem using fabric js. My current solution is encode the canvas element to base64 string using toDataUrl() and send it to server side code to decode it again, then write it as image file. I am using PHP on the server side

Related

How to use the fabric.js library with the online library url?

I'm trying to insert the Fabric.js library to test some things with:
<script src='https://unpkg.com/fabric#latest/dist/fabric.js'></script>
But apparently dosen't work...
I'm just trying to do a basic free canvas like:
var canvas = this.__canvas = new fabric.Canvas('c', {
isDrawingMode: true
});
var rect = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 70,
fill: 'red',
});
canvas.add(rect);
canvas.renderAll();
But still nothing appears on my project, i can't do or paint anything..
Anyone with some answers?
I think you need canvas element the html in order for fabricjs to work.
There are two ways to fix achieve this
Option 1. - Add canvas element in html.
<canvas id="c"/>
var canvas = this.__canvas = new fabric.Canvas('c', {
isDrawingMode: true
});
var rect = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 70,
fill: 'red',
});
canvas.add(rect);
canvas.renderAll();
canvas {
border-style: groove;
}
<canvas id="c" />
<script src='https://unpkg.com/fabric#latest/dist/fabric.js'></script>
Option 2. - Add canvas element in html using javascript.
var mycanvas = document.createElement("canvas");
mycanvas.id = "mycanvas";
document.body.appendChild(mycanvas);
var mycanvas = document.createElement("canvas");
mycanvas.id = "c";
document.body.appendChild(mycanvas);
var canvas = this.__canvas = new fabric.Canvas('c', {
isDrawingMode: true
});
var rect = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 70,
fill: 'red',
});
canvas.add(rect);
canvas.renderAll();
canvas {
border-style: groove;
}
<script src='https://unpkg.com/fabric#latest/dist/fabric.js'></script>

Offset issues images when inside group (scaleToWidth)

I'm experiencing an issue when scaling an image inside a group the offset of the image changes. The group consists of a background (Rect) and an image. When scaling the group the offset if the image becomes different. Expected is that the offset remains the same as the background.
I'm using Fabric 4.0.0-beta.6
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
https://jsbin.com/migogekoxu/1/edit?html,css,js,output
Setting the strokeWidth property of the background element to 0 fixed the offset for me.
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
strokeWidth: 0, //<---
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<canvas id="canvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
Obviously that's a bug with fabricJS. If you don't want to fix it on your own in it's source code you can workaround it however.
You can manually correct the position by adding an offset of 0.5 to the image's top and left properties.
For example:
const canvas = new fabric.Canvas('canvas');
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red'
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width);
img.left += 0.5;
img.top += 0.5;
const post = new fabric.Group([bg, img], {
left: 100,
top: 100
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
<canvas id="canvas" width="600" height="400"></canvas>

fabric.js how to use fromObject api

I want to implement importing data from one template into another without affecting data from the other. Using deserialization API loadFromJson will empty the canvas data first, so I want to use toObect method to export the data, and then use toFromObject to import, but the object cannot display correctly after the import, may I ask if there is any solution?Or is there another implementation?thank you
demo: https://jsfiddle.net/laibin/gpvef0k5/2/
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.min.js"></script>
<canvas id="canvas" style="border: 1px solid black" height=480 width=460></canvas>
<button id="btn">导出</button>
<canvas id="canvas_template" style="border: 1px solid black; margin-top: 20px" height=480 width=660></canvas>
Javascript:
var canvas = new fabric.Canvas('canvas');
var canvas_template = new fabric.Canvas('canvas_template');
var circle = new fabric.Circle({
radius: 50, left: 275, top: 75, fill: '#aac'
})
var triangle = new fabric.Triangle({
width: 100, height: 100, left: 50, top: 300, fill: '#cca'
});
var rect1 = new fabric.Rect({
width: 200, height: 100, left: 0, top: 50, angle: 30,
fill: 'rgba(255,0,0,0.5)'
});
var group = new fabric.Group([circle, triangle, rect1])
canvas.add(group).setActiveObject(group);
canvas.renderAll();
document.querySelector('#btn').addEventListener('click',function() {
var data = group.toObject();
let newGroup = new fabric.Group([]);
fabric.Group.fromObject(data, function(obj) {
console.log('newGroup', obj)
canvas_template.setActiveObject(obj).renderAll();
})
})
You never added the resulting object to canvas, that's why you didn't see it, only the selection made by setActiveObject(). You should add the object in the fromObject() callback:
fabric.Group.fromObject(data, function(obj) {
canvas_template.add(obj) // <- like this
console.log('newGroup', obj)
canvas_template.setActiveObject(obj).renderAll();
})

Fabric.js - Attach object creation to mouse click

When you create a new fabric object, you can specify the location for it to appear on the canvas. Is there a way to attach the generated object to the mouse and then place the object on click (or touch)?
E.g. way to generate a circle which appears on the canvas.
var circle = new fabric.Circle({
radius: 20, fill: 'green', left: 100, top: 100
});
It's fairly easy to update the position of an object to match that of the mouse's position and on mouse:up clone that object and place it on the canvas.
var canvas = new fabric.Canvas('c');
var mousecursor = new fabric.Circle({
left: 0,
top: 0,
radius: 5,
fill: '#9f9',
originX: 'right',
originY: 'bottom',
})
canvas.add(mousecursor);
canvas.on('mouse:move', function(obj) {
mousecursor.top = obj.e.y - mousecursor.radius;
mousecursor.left = obj.e.x - mousecursor.radius;
canvas.renderAll()
})
canvas.on('mouse:out', function(obj) {
// put circle off screen
mousecursor.top = -100;
mousecursor.left = -100;
canvas.renderAll()
})
canvas.on('mouse:up', function(obj) {
canvas.add(mousecursor.clone())
canvas.renderAll()
})
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
var canvas = new fabric.Canvas('c');
var mousecursor = new fabric.Circle({
left: 0,
top: 0,
radius: 50,
fill: '#9f9',
originX: 'right',
originY: 'bottom',
})
canvas.add(mousecursor);
canvas.on('mouse:move', function(obj) {
mousecursor.top = obj.e.y;
mousecursor.left = obj.e.x;
canvas.renderAll()
})
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>

clone one canvas to another having trouble

I have checked all the answers to clone existing canvas to another one. But I could not get it done.
Please check my current progress.
http://jsfiddle.net/37n8rtdf/5/
First canvas will be clipped to that square you see initially and content of that canvas will be added in another canvas. But I don't know it always throw TYPE_MISMATCH_ERR: DOM Exception 171 in chrome. I am using fabricjs to clipping content.
Bit of help is appreciated.
Thanks
Here is code of my script:
HTML
<textarea id="line_1"></textarea>
<input type="button" id="render" value="Apply" />
<input type="button" id="preview" value="preview" />
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<canvas id="c_new" width="500" height="500" style="border:1px solid red; margin: 30px;"></canvas>
`
Javascript
var canvas = this.__canvas = new fabric.Canvas('c');
var canvas_new = this.__canvas = new fabric.Canvas('c_new');
var product_image = 'http://www.jail.se/hardware/digital_camera/canon/ixus_800is-powershot_sd700/images/sample_photos/sample3.jpg';
//var ctx = canvas.getContext("2d");
var polygon;
$(document).ready(function(){
fabric.Object.prototype.transparentCorners = false;
//canvas.setDimensions({width:w,height:h});
var center = canvas.getCenter();
canvas.setBackgroundImage(product_image,
canvas.renderAll.bind(canvas), {
scaleX:1,
scaleY:1,
top: center.top,
left: center.left,
originX: 'center',
originY: 'center',
backgroundImageOpacity: 0,
backgroundImageStretch: false
});
canvas_new.setBackgroundImage(product_image,
canvas_new.renderAll.bind(canvas_new), {
scaleX:1,
scaleY:1,
top: center.top,
left: center.left,
originX: 'center',
originY: 'center'
});
polygon = new fabric.Polygon([
{x: 0, y: 0},
{x: 220, y: 0},
{x: 220, y: 180},
{x: 0, y: 180} ], {
left: 140,
top: 150,
angle: 0,
fill: 'transparent',
stroke: '#000', strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasControls: false,
hasBorders: false,
hoverCursor: 'default',
overflow: 'hidden'
});
canvas.add(polygon);
$('#render').click(function(){ return render(); });
$('#preview').click(function(){ return rasterize(); });
});
function render()
{
var text_val = $('#line_1').val();
var comicSansText = new fabric.Text(text_val, {
fontWeight: 'normal'
});
canvas.add(comicSansText.set({ left: 200, top: 150, angle: 0 }));
}
function rasterize()
{
var shape = canvas.item(0);
polygon.strokeWidth=0;
canvas.renderAll();
//canvas.remove(shape);
canvas.clipTo = function(ctx) {
shape.render(ctx);
};
var ctx2 = canvas_new.getContext('2d');
ctx2.drawImage(canvas, 0, 0);
}
`
#WinterMute is right, you are trying to draw the Fabric object instead of the canvas element.
You fabric object seems to have two canvases embedded (lowerCanvasEl and upperCanvasEl).
So you can modify just a little bit your code to make it look like :
function rasterize() {
var shape = canvas.item(0);
polygon.strokeWidth = 0;
canvas.renderAll();
canvas.clipTo = function(ctx) {
shape.render(ctx);
};
var ctx2 = canvas_new.getContext('2d');
ctx2.drawImage(canvas.lowerCanvasEl, 0, 0);
ctx2.drawImage(canvas.upperCanvasEl, 0, 0);
}
Updated fiddle

Categories

Resources