Object's custom properties getting cleared in canvas to json - javascript

I have a canvas with multiple image objects. Every object has some custom attributes. I need to save this canvas as a json object into a database. I used following code to convert canvas into json.
canvasJson = JSON.stringify(canvas);
After converting, canvasJson can't have any custom attribute as well as it's values. It has only default attributes and it's values like width, height, opacity etc.
How can I fix that? Please suggest some right ways to solve this...
Edit
Following is my image object creation code to create image objects in canvas.
var imgObj = new Image();
var imgSrc = $IMAGE_URL;
imgObj.src = imgSrc;
var image = new fabric.Image(imgObj);
image.set({
left: 0,
top: 0,
angle: 0,
padding: 0,
cornersize: 0,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
elementId: $elementID,
elementname: $elementName,
elementstatus: $elementStatus,
width: componentImageWidth,
height: componentImageHeight
});
canvas.add(image);
image.setCoords();
canvas.renderAll();
canvas.selection = false;
canvas.renderAll();
setObjectAction(image, false);
Converting the canvas into json using JSON.stringify(canvas); the elementId, elementname and elementstatus are missing. but default attributes are gets correctly.
How to fix this ?

You can use fabric's canvas.toJson() or canvas.toDatalessJSON() functions, and you can include your custom properties as parameters like this:
var json = JSON.stringify(canvas.toDatalessJSON(['elementId','elementname', 'elementstatus']));

Related

Pixel manipulation with FabricJS

I'm using FabricJS v1.5.0 and have a problem when manipulated pixel by pixel on the canvas.
this.canvas = new this.fabric.Canvas('canvas');
let imageData = this.canvas.contextContainer.getImageData(0, 0, this.canvas.getWidth(), this.canvas.getHeight());
//here a changed imageData
this.canvas.contextContainer.putImageData(imageData, 0, 0);
//data seted in canvas.
let imageAdd = new this.fabric.Image(image, {
left: 100,
top: 100,
width: 100,
height: 100
});
this.canvas.add(imageAdd).setActiveObject(imageAdd);
//in this moment, the canvas don't have more the imageData setted
Why add image clear imageData? I need to transform imageData in FabricJS Object to add in canvas?
Yes, I think you do need to turn your image data into a fabricjs image. When you need to save the state of the canvas you can do this:
var dataUrl;
....
// Save canvas state
dataUrl = canvas.toDataURL()
// Make changes to the canvas
Then when you need to restore the canvas from it's saved state:
var img = fabric.Image.fromURL(dataUrl, function (i) {
canvas.clear();
canvas.add(i);
canvas.renderAll();
});
You should then be able to add other images as before.
What you need to find out is if your image is actually an image object, because fabric.Image() needs your image object.
If it's a URL or Blob format of your image ( which I guess it is ), you need to use fabric.Image.fromURL() and provide the src of the image not the whole image object.
Here is an example on how to create an instance of fabric.Image from a URL string
fabric.Image.fromURL(link, function(img) {
img.set({
id : 'someId',
width : canvas.width / 2,
height : canvas.height / 2
});
canvas.add(img).renderAll().setActiveObject(img);
});

Is it possible to make a dynamic mask/crop system via fabric.js that behaves like canva.com?

edit 2016, Oct 24
I've an idea about this feature by using 'pattern' which seems to be a good idea.
I tried to prove my thoughts with this pen, its still buggy but at least we can set the image(pattern) position and keep the original image when double click.
I'am not very good at javascript, So if you are interest in this please help to make this more useable, any discussions/thoughts or code correction is welcome.
http://codepen.io/wushan/pen/LRrQEL?editors=1010
// Create Canvas
var canvas = this.__canvas = new fabric.CanvasEx('c', {
preserveObjectStacking: true
});
fabric.Object.prototype.transparentCorners = false;
// Global Settings
var url = "http://fabricjs.com/assets/pug.jpg";
//Make the Pattern by url
function createMaskedImage(url) {
//Load Image
fabric.Image.fromURL(url, function(img) {
img.scaleToWidth(300);
//Make a Pattern
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(img);
var pattern = new fabric.Pattern({
source: function() {
patternSourceCanvas.setDimensions({
width: img.getWidth(),
height: img.getHeight()
});
return patternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
console.log(pattern.offsetX)
console.log(pattern.offsetY)
console.log(img.getWidth()) // 縮小後 (*scale)
console.log(img.width) // 原尺寸
//Mask (can be any shape ex: Polygon, Circles....)
var rect = new fabric.Rect({
width: 200,
height: 200,
left: 150,
top: 100,
fill: pattern
})
//Bind Double Click Event from fabric.ext
//https://github.com/mazong1123/fabric.ext
rect.on('object:dblclick', function (options) {
//Pass pattern out
enterEditMode(rect, img);
});
canvas.add(rect);
canvas.setActiveObject(rect);
});
}
function enterEditMode(mask, image) {
image.left = mask.left;
image.top = mask.top;
// New Image
// Fake Crop Area (fixed)
var rect = new fabric.Rect({
width: mask.width,
height: mask.height,
left: mask.left,
top: mask.top,
fill: '#000000',
opacity: 0.8,
selectable: false
})
canvas.remove(mask);
canvas.add(image);
image.on('object:dblclick', function (options) {
//Flatten
flatten(rect, image);
});
canvas.add(rect);
// console.log(JSON.stringify(canvas));
}
function flatten(mask, image) {
//Make a Pattern
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(image);
var pattern = new fabric.Pattern({
source: function() {
patternSourceCanvas.setDimensions({
width: image.getWidth(),
height: image.getHeight()
});
return patternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
//Offsets
pattern.offsetX = image.left - mask.left - image.left;
pattern.offsetY = image.top - mask.top - image.top;
var rect = new fabric.Rect({
width: mask.width,
height: mask.height,
left: mask.left,
top: mask.top,
fill: pattern
})
//Bind Double Click Event from fabric.ext
//https://github.com/mazong1123/fabric.ext
rect.on('object:dblclick', function (options) {
//Pass pattern out
enterEditMode(rect, image);
});
canvas.remove(mask);
canvas.remove(image);
canvas.add(rect);
canvas.setActiveObject(rect);
canvas.renderAll();
}
//Button Events
//Create
document.getElementById('createMaskedImage').addEventListener('click', function () {
createMaskedImage(url);
});
Test this :
click 'create'
double click on the image object
move/scale the image
double click the image to flatten the obejct.
Know issue:
when cutting the image without scale, the image position looks correct but there is a wired transparent space.
It generates a lot duplicate objects on the scene...
Original Post
I've been working with fabric.js for a few month, haven't seen any example or discussions about a mask/crop system which is behaves like canva.com ( which is way easy to understand for users. )
the object looks like this:
when double clicked on a group/mask, it shows the original image with an unselectable mask, you can move/scale the image whatever you need and click 'OK' to made the change without modifying the original image.
I'd like to know if there is any possible solutions about making this in fabricjs, or maybe some thoughts about this issue is welcome.
Thank you a lot !

FabricJS adding Image to Group causes odd control behavior

I'm attempting to add a loaded image into a fabric Group object. Everything looks ok, but the selection controls aren't selectable and I can't drag the object around. The top left control works though and after clicking it everything is fine.
Here is a jsfiddle that demonstrates the behavior.
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
canvas.setActiveObject(group);
canvas.renderAll();
});
Is this a bug or am I doing something wrong?
For some performance reason, fabricjs does not call setCoords automatically after adding objects to a group ( in case of many object added you can call setCoords just once ).
So after doing addWithUpdate, just call group.setCoords();
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
group.setCoords();
canvas.setActiveObject(group);
canvas.renderAll();
});
I came across this post because I was having trouble getting the positioning of images to work properly. I ended up finding that it's easier to just create create an image element using document.createElement and set the src, then feed that into fabric.Image with all the options you need (instead of using fabric.Image.fromURL which was too much of a headache to use), before adding it to the group.
var oImg = document.createElement("img");
oImg.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Cog_font_awesome.svg/512px-Cog_font_awesome.svg.png');
var i = new fabric.Image(oImg, {
originX: 'center',
originY: 'center',
left: left+35,
top: top-30,
scaleX:.05,
scaleY:.05,
});
g = new fabric.Group([r, t, i]); // where r and t are other fabric objects

Adding filter to pattern image of shape in fabric.js

I am trying to apply a filter to the pattern image of shape but receiving numerous errors. I googled a lot but still cannot find a solution that works. Below is my code. Q 1. Is it even possible? Q 2. How to achieve it!?
var canvas = new fabric.Canvas('c1');
var circle = new fabric.Circle({
radius: 100, fill: 'green', left: 100, top: 100
});
canvas.add(circle);
loadPattern('http://i0.wp.com/www.illustratoring.com/wp-content/uploads/2012/12/chevron-pattern-illustrator.png?resize=40%2C40', circle);
function loadPattern(url, obj){
fabric.util.loadImage(url, function(img) {
obj.setPatternFill({
source: img,
repeat: 'repeat'
});
canvas.renderAll();
});
}
here is the JSfilddle https://jsfiddle.net/eepmzy9n/2/ I want to apply filters to the pattern image.
Yes, this should be possible using the fabric js filters. Combining patterns and filters shouldn't be an issue.
Info on image filters:
http://fabricjs.com/fabric-intro-part-2/#image_filters
http://fabricjs.com/image-filters/
Other code examples using filters:
Fabric js image filter
How to apply filter to canvas backgroundImage in Fabric.js

Using a background image with javascript pie chart

I am building an app in Rails, and I am using a gem called easy_as_pie which enables me to use this Jquery plugin called 'Easy Pie Chart' (http://rendro.github.io/easy-pie-chart/)
I have the pie chart working no problem, using the following code:
$(document).ready(function() {
$('.chart').easyPieChart({
size: 300,
animate: 1400,
lineWidth: 150,
lineCap: "butt",
scaleColor: false,
trackColor: "black",
barColor: "white"
});
});
The question I have, is whether not it would be possible to have the chart load a background image instead of a solid colour. In the documentation it allows you to use a gradient with a function, using the following code:
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
var gradient = ctx.createLinearGradient(0,0,canvas.width,0);
gradient.addColorStop(0, "#ffe57e");
gradient.addColorStop(1, "#de5900");
return gradient;
}
});
I was hoping to get the gradient working, then work on trying to manipulate the function to load in an image. But I have been unsuccessful in even getting the gradient to work.
Provided your gradient example works like it looks, you should be able to draw just about anything you want into the canvas object they've provided. With that in mind, you might be able to draw images like so:
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
var yourImage = new Image();
yourImage.src = "path-to-your-img.jpg";
return ctx.drawImage(yourImage);
}
});
However, Image objects, like <img> tags, require a further GET request to the server to actually load them, so the above probably won't actually work (sorry for the tease). Instead, you'll have to wait for that image to load before calling it, for example:
// Instantiate the image, it's blank here.
var yourImage = new Image();
// Add a callback that uses the fully-loaded img
yourImage.onload = function() {
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
return ctx.drawImage(yourImage);
}
});
};
// Set the src, that queues it for the actual GET request.
yourImage.src = "path-to-your-img.jpg";

Categories

Resources