I have a this SVG, which i am trying to load in canvas but it just render the black image, so how to solve it?
version: 2.0.0-beta.7
I have tried both method loadSVGFromString as well as loadSVGFromURL but no success:
//Using loadSVGFromString
var a = 'svg string';
fabric.loadSVGFromString(a,function(objects, options) {
var obj = fabric.util.groupSVGElements(objects, options);
canvas.add(obj).renderAll();
});
//using loadSVGFromURL
fabric.loadSVGFromURL('URL Here', function(objects,options) {
var group = new fabric.Group(objects, {
left: 50,
top: 50,
});
canvas.add(group);
canvas.renderAll();
});
checkout this codepen
"All black" svgs often is a result of some invalid or unsupported svg markup.
In your case I think you should remove the fill-opacity="null" references from your svg.
Related
I am trying to draw a textbox on canvas that contains an image and some drawing over it. now I am trying to draw a textbox over it.
When I am trying to initialize canvas object using fabricJS it's making canvas empty first. like..
var canvas = new fabric.Canvas('mycanvas')
after this code, it makes canvas empty. but if I initialize this code in the very beginning just after canvas initialized and then I try to add a textbox to that canvas object like...
var text = new fabric.Textbox('A Computer Science Portal',
{
width: 450,
height: 10,
top: 100,
left: 100,
});
this.canvas.add(text);
Then the textbox is not visible. I think it went in the background of the image.
I have already written lots of code for drawing over the canvas not want to draw textbox on the canvas after with all existing drawing and image that is developed in javascript only.
On Fabric you have bringToFront use that to keep the text on top.
Here is the official documentation:
http://fabricjs.com/docs/fabric.Canvas.html#bringToFront
Sample code:
var image;
var canvas = new fabric.Canvas('canvas');
var url = "http://swagger-net-test.azurewebsites.net/api/SvgImage?"
var text = new fabric.Textbox('TEST', { width: 90, height: 10, top: 30, left: 50 });
this.canvas.add(text);
function loadSVG(url) {
fabric.loadSVGFromURL(url, function(objects, options) {
if (image) canvas.remove(image);
image = fabric.util.groupSVGElements(objects, options);
image.name = 'sticker';
canvas.add(image);
image.scaleToHeight(100);
image.center();
image.setCoords();
canvas.renderAll();
canvas.bringToFront(text)
});
}
loadSVG(url + "color=red")
setTimeout(function() {
loadSVG(url + "color=blue")
}, 3000);
<canvas id="canvas" width="200" height="200"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.4/fabric.min.js"></script>
I'd like to load a SVG image into canvas using Fabric.js and have access to all attributes of it.
Let's assume I have an image like this:
SVG Image
I tried to it using Fabric.js on 3 ways:
1. (Saved as String) - this method is not loading my image, I took other SVG and attributes can be accessed with svgImg.fill.color property.
fabric.loadSVGFromString(svg, function (objects, options) {
var svgImg = fabric.util.groupSVGElements(objects, options);
canvas.add(svgImg);
});
2. Directly from URL:
fabric.loadSVGFromURL(URLsvg, function (objects, options) {
var svgImg = fabric.util.groupSVGElements(objects, options);
canvas.add(svgImg);
});
3.Using fabric.Image:
<img id="Test_Img" src="~/SVG/homer-simpson.svg" style="display: none;" />
var Img = document.getElementById('Test_Img');
var imgInstance = new fabric.Image(Img, {
left: 90,
top: 80
});
How can I access inner attributes of my SVG and change for example color of Homers T-Shirt?
Loaded SVG objects become groups in fabric.js (if they are not simple geometric forms like a rectangle, circle, etc.).
You can access the members of the group with getObjects(), which returns an array.
Here is a simple example. It reads an SVG consisting of 3 arcs and changes the color of the first arc to blue:
var canvas = new fabric.Canvas('canvas', { preserveObjectStacking: true });
canvas.backgroundColor="#fff";
canvas.renderAll();
fabric.loadSVGFromURL("https://media-public.canva.com/MABtmZU4zXQ/1/screen.svg",function(objects,options) {
var loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.set({
width: 300,
height: 150
});
canvas.add(loadedObjects);
// Change the first arc to blue
loadedObjects.getObjects()[0].set({fill: "blue"});
canvas.renderAll();
});
It runs with fabric.js Version 3.5.
Working example: https://jsfiddle.net/awehring/k7azLb84/9/
Started using fabric.js and trying to add a canvas inside another canvas, so that the top canvas stays constant and I'll add objects to inner canvas.
Here is the snippet of adding a canvas to another canvas.
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
canvas.add(innerCanvas);
and my html looks like this
<canvas id="artcanvas" width="500" height="500"></canvas>
<canvas id="innerCanvas" width="200" height="200" ></canvas>
Once adding these successfully, what I am going to do is , add the coordinates to the inner canvas, so that it looks like one on another to the end user.
However, ran into the below error for the tried code
Uncaught TypeError: obj.setCoords is not a function
at klass._onObjectAdded (fabric.js:6894)
at klass.add (fabric.js:231)
at main.js:60
at fabric.js:19435
at HTMLImageElement.fabric.util.loadImage.img.onload (fabric.js:754)
_onObjectAdded # fabric.js:6894
add # fabric.js:231
(anonymous) # main.js:60
(anonymous) # fabric.js:19435
fabric.util.loadImage.img.onload # fabric.js:754
Looking at the error message, just went to the line of error and here is what I found in chrome console
Can someone point the mistake in my codes ?
After going through no.of discussions and internet solutions, for time being I am using Fabric Rectangle as a clipper and setting it's boundaries so user can be able to drop/play with in that particular clipper.
Dotted red(image below) is my clipper and now I can bound the dropping and below is the code to add an image with a clipper.
function addImageToCanvas(imgSrc) {
fabric.Object.prototype.transparentCorners = false;
fabric.Image.fromURL(imgSrc, function(myImg) {
var img1 = myImg.set({
left: 20,
top: 20,
width: 460,
height: 460
});
img1.selectable = false;
canvas.add(img1);
var clipRectangle = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 150,
top: 150,
width: 200,
height: 200,
fill: 'transparent',
/* use transparent for no fill */
strokeDashArray: [10, 10],
stroke: 'red',
selectable: false
});
clipRectangle.set({
clipFor: 'layer'
});
canvas.add(clipRectangle);
});
}
Now while appending any image/layer to the canvas, I bind that image/layer/text to the clipper I created.
function addLayerToCanvas(laImg) {
var height = $(laImg).height();
var width = $(laImg).width();
var clickedImage = new Image();
clickedImage.onload = function(img) {
var pug = new fabric.Image(clickedImage, {
width: width,
height: height,
left: 150,
top: 150,
clipName: 'layer',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
clickedImage.src = $(laImg).attr("src");
}
And the looks like, after restriction of bounds,
Here is the fiddle I have created with some static image url.
https://jsfiddle.net/sureshatta/yxuoav39/
So I am staying with this solution for now and I really feel like this is hacky and dirty. Looking for some other clean solutions.
As far as I know you can't add a canvas to another canvas - you're getting that error as it tries to call setCoords() on the object you've added, but in this case it's another canvas and fabric.Canvas doesn't contain that method (see docs). I think a better approach would be to have two canvases and position them relatively using CSS - see this simple fiddle
HTML
<div class="parent">
<div class="artcanvas">
<canvas id="artcanvas" width="500" height="500"></canvas>
</div>
<div class="innerCanvas">
<canvas id="innerCanvas" width="200" height="200" ></canvas>
</div>
</div>
CSS
.parent {
position: relative;
background: black;
}
.artcanvas {
position: absolute;
left: 0;
top: 0;
}
.innerCanvas {
position: absolute;
left: 150px;
top: 150px;
}
JS
$(document).ready(function() {
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
var rect = new fabric.Rect({
fill: 'grey',
width: 500,
height: 500
});
canvas.add(rect);
var rect2 = new fabric.Rect({
fill: 'green',
width: 200,
height: 200
});
innerCanvas.add(rect2);
})
To handle the object serialization, you can do something like this:
var innerObjs = innerCanvas.toObject();
console.dir(innerObjs);
var outerObjs = canvas.toObject();
innerObjs.objects.forEach(function (obj) {
obj.left += leftOffset; // offset of inner canvas
obj.top += topOffset;
outerObjs.objects.push(obj);
});
var json = JSON.stringify(outerObjs);
This will then give you the JSON for all objects on both canvases
I have no understanding why you want to do this thing, but to put a canvas inside another canvas, you have one simple way:
canvas = new fabric.Canvas('artcanvas');
innerCanvas = new fabric.Canvas("innerCanvas");
imageContainer = new fabric.Image(innerCanvas.lowerCanvasEl);
canvas.add(imageContainer);
Then depending what you want to do, you may need additional tweaks, but this should work out of the box.
Don't create a canvas
Most objects in fabric (from my limited experience) are at some point converted to a canvas. Creating an additional fabric canvas to manage a group of objects is kind of pointless as you are just adding overhead and mimicking fabrics built in groups object.
Fabric objects are basically DOM canvases wrappers.
The following example shows how fabric uses a canvas to store the content of a group. The demo creates a group and adds it to the fabric canvas, then gets the groups canvas and adds it to the DOM. Clicking on the group's canvas will add a circle. Note how it grows to accommodate the new circles.
const radius = 50;
const fill = "#0F0";
var pos = 60;
const canvas = new fabric.Canvas('fCanvas');
// create a fabric group and add two circles
const group = new fabric.Group([
new fabric.Circle({radius, top : 5, fill, left : 20 }),
new fabric.Circle({radius, top : 5, fill, left : 120 })
], { left: 0, top: 0 });
// add group to the fabric canvas;
canvas.add(group);
// get the groups canvas and add it to the DOM
document.body.appendChild(group._cacheContext.canvas);
// add event listener to add circles to the group
group._cacheContext.canvas.addEventListener("click",(e)=>{
group.addWithUpdate(
new fabric.Circle({radius, top : pos, fill : "blue", left : 60 })
);
canvas.renderAll();
pos += 60;
});
canvas {
border : 2px solid black;
}
div {
margin : 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<div>Fabric's canvas "canvas = new fabric.Canvas('fCanvas');"</div>
<canvas id="fCanvas" width="256" height="140"></canvas>
<div>Fabric group canvas below. Click on it to add a circle.</div>
Use a group rather than a new instance of a fabric canvas.
As you can see a canvas is generated for you. Adding another fabric canvas (Note that a fabric canvas is not the same as a DOM canvas) will only add more work for fabric to do, which already has a lot of work to do.
You are best of to use a group and have that hold the content of the other fabric object you wish to shadow. That would also contain its content in a group.
Just an image
And just a side not, a DOM canvas is an image and can be used by fabric just as any other image. It is sometimes better to do the rendering directly to the canvas rather than via fabric so you can avoid rendering overheads that fabric needs to operate.
To add a DOM canvas to fabric just add it as an image. The border and text are not fabric object, and apart from the code to render them take up no memory, and no additional CPU overhead that would be incurred if you used a fabric canvas and objects.
const canvas = new fabric.Canvas('fCanvas');
// create a standard DOM canvas
const myImage = document.createElement("canvas");
// size it add borders and text
myImage.width = myImage.height = 256;
const ctx = myImage.getContext("2d");
ctx.fillRect(0,0,256,256);
ctx.fillStyle = "white";
ctx.fillRect(4,4,248,248);
ctx.fillStyle = "black";
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("The DOM canvas!",128,128);
// use the canvas to create a fabric image and add it to fabrics canvas.
canvas.add( new fabric.Image(myImage, {
left: (400 - 256) / 2,
top: (400 - 256) / 2,
}));
canvas {
border : 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<canvas id="fCanvas" width="400" height="400"></canvas>
innerCanvas.setCoords(); ìs a function, but you need it only after you set the coordinates. Or more precise, set these four elements:
innerCanvas.scaleX = 1;
innerCanvas.scaleY = 1;
innerCanvas.left = 150;
innerCanvas.top = 150;
innerCanvas.setCoords();
canvas.renderAll();
I am complete newbie in fabric.js. I need to load several images from URL in canvas and add them as image object so I can set their z index.
I tried using Image.fromURL but it did not return object so I can't set their z index. New fabric.Image take images from page so I am not getting results from it.
This is what I have done so far
var canvas = new fabric.Canvas('myCanvas');
var front_wall = fabric.Image.fromURL('img/bedroom1-front/front_wall.png',
function(img) {
img.scale(0.65);
canvas.add(img);
}
);
I add five more images same way.
Can fabric.Image.fromURL return the image object (by using any attribute) ?
So I can change their z-index after loading.
Sorry I don't have jsfiddle to show.
Welcome to Stack Overflow,
Please check this fiddle
I added some comments in that fiddle. Code is below:
var srcImg = "http://fabricjs.com/lib/pug.jpg";
var canvas = new fabric.Canvas('c');
var icon = null;
var image = fabric.Image.fromURL(srcImg, function(oImg) {
oImg.set({width: oImg.width / 3,
height: oImg.height / 3,
left: 100,
top: 100,
originX: 'center',
originY: 'center',
selectable: false,
transparentCorners: false});
//magic strats here to make active Image and you can control image object here
canvas.setActiveObject(oImg);
icon = canvas.getActiveObject();
icon.hasBorders = false;
icon.hasControls = false;
canvas.add(oImg);
});
//if you want to control outside somewhere you can do this:
setTimeout(function(){
icon.set({'top': 200, 'left': 200});
canvas.renderAll();
}, 1500);
There are two canvas instance, the smaller 'd' for free drawing, the bigger 'c' is to be added on. How can I only add the boundary of drawing on 'd' to 'c', not the entire 'd' canvas area with lots of empty area to 'c'?
Fiddle
I hope the code can explains more clear. thanks.
HTML
<canvas width="300" height="300" id="c"></canvas>
<canvas width="150" height="150" id="d"></canvas>
<button id="btn">add To c</button>
JavaScript
var c = new fabric.Canvas('c')
var d = new fabric.Canvas('d', {
isDrawingMode: true
})
document.getElementById('btn').onclick = addDrawToBig
function addDrawToBig(){
var svg = d.toSVG()
fabric.loadSVGFromString(svg,function(objects, options) {
var obj = fabric.util.groupSVGElements(objects, options);
c.add(obj).centerObject(obj).setActiveObject(obj);
});
}
var circle = new fabric.Circle({
stroke: 'red',
radius: 15,
left: 15,
top: 15
})
d.add(circle)
d.renderAll()
addDrawToBig()
finally i find the answer to my question, just use fabric.Group and .clone() method:
function addDrawToBig(){
var group = [ ]
d.getObjects().forEach(function(path){
path.clone(function(copy){
group.push(copy);
});
});
group = new fabric.Group( group )
c.add(group).centerObject(group).setActiveObject(group);
}
fiddle here
Special thanks to #AndreaBogazzi, wish a good day!
i updated your fiddle with something that works better.
Fiddle
Canvas.toSVG() is not meant to be used as a export import function.
I suggest you to cycle throught the canvas objects and clone all of them in the new canvas.
This demo is not really cloning them, just copying them on the other canvas.
Without cloning you have the side effect that if you modify something in one canvas the modification get mirrored in the other.
You should add
c.add(ao[i].clone());
But for some reason free drawing path .clone() is failing. ( will look in this next days to see if it is a bug or a misusage )
var c = new fabric.Canvas('c')
var d = new fabric.Canvas('d', {
isDrawingMode: true
})
document.getElementById('btn').onclick = addDrawToBig
function addDrawToBig(){
var ao = d.getObjects();
console.log(ao);
for(var i in ao) {
c.add(ao[i]);
}
}
var circle = new fabric.Circle({
stroke: 'red',
radius: 15,
left: 15,
top: 15
})
d.add(circle)
d.renderAll()
addDrawToBig()
Hope it helps.