I need to create object group on undo function with fabricjs - javascript

I have two canvas objects (Rectungle and Triangle) in group.
Then i need to delete them, then return one of objects to is past position.
Here is two objects and group
var rect1 = new fabric.Rect(
{
id: 1, left: 10, top: 10, width: 100, height: 50, angle: 45, fill: 'red'
});
var tria1 = new fabric.Triangle({
id: 2, left: 200, top: 200, width: 100, height: 50, angle: 20, fill: 'yellow'
});
var objsGroup = new fabric.Group([rect1, tria1], {left: 100, top: 100});
And the whole fiddle here. I separate all steps by alerts https://jsfiddle.net/5js60oec/
UPDATE
Have the next problem. Lets assume that existing object group making some move and rotating before deleting. Then i have to restore it to point before rotating and moving. Thats, i think the main problem.
Actions
objsGroup.top = 200;
objsGroup.left = 200;
canvas.renderAll();
alert('4');
history.push(canvas.getActiveGroup());
objsGroup.setAngle(45);
canvas.renderAll();
Here the full fiddler: https://jsfiddle.net/nppaLetn/1/

For example I am deleting the last added path in my canvas like below
var lastItemIndex = (fabricCanvas.getObjects().length - 1);
var item = fabricCanvas.item(lastItemIndex);
if(item.get('type') === 'path') {
fabricCanvas.remove(item);
fabricCanvas.renderAll();
}
This may help you to accomplish your probs!!! I think so .

Related

Fabricjs: cloning a subclassed object makes the clone invisible

I want to make a copy of a fabric object that is a sub-class of a regular fabric class (i.e. a new object with the same properties as the original). Cloning seemed to be the way to go, and indeed cloning a regular object, such as a Rect, works fine. But if I use the same code to clone an instance of a sub-class, the resulting object is not visible on the canvas (although it is listed in the results of canvas.getObjects()).
What am I doing wrong?
Here is a minimal example. This is also at https://codepen.io/micrology/pen/dyKPbRO Running this displays 3 rectangles, two red but only one yellow. There should be 2 yellow rectangles.
var canvas = new fabric.Canvas('canvas')
var rect = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 70,
fill: 'red',
})
canvas.add(rect)
var clonedRect
rect.clone((obj) => {
clonedRect = obj
clonedRect.set({left: obj.left + 10, top: obj.top + 10})
})
canvas.add(clonedRect)
var CustomRect = fabric.util.createClass(fabric.Rect, {
type: 'rect',
initialize: function (options) {
this.callSuper('initialize', options)
},
})
var customRect = new CustomRect({
top: 200,
left: 200,
width: 50,
height: 50,
fill: '#ffff00',
})
canvas.add(customRect)
var clonedCustomRect
customRect.clone((obj) => {
clonedCustomRect = obj
clonedCustomRect.set({left: obj.left + 10, top: obj.top + 10})
})
canvas.add(clonedCustomRect)
For people still having this problem, there is currently an open discussion.

fabricjs objects order in group

I have no idea how to order objects in group other then repeat add and remove to sort the objects.
I tried unshift on group objects but it does not save state.
$(document).on("click", "#addToGroup", function() {
var objects = canvas.getActiveObject();
if(objects && objects.type === "activeSelection") {
objects.toGroup();
canvas.requestRenderAll();
}
});
count = 1;
$(document).on("click", "#addObject", function() {
canvas.add(new fabric.Rect({
width: 100, height: 100, left: 100 + (count * 20), top: 20 + (count * 10), angle: -10,
fill: 'rgba(0,200,0,1)'
}));
});
Template JSFiddle
You can use fabric.Object.prototype.moveTo(). There is an undocumented behavior: if the object you call it upon is a part of a group, the object is moved within the group's _objects array, effectively changing its z-index when the group is redrawn.
Also, make sure to somehow force fabric to redraw the group after that, e.g. set group.dirty = true.
const canvas = new fabric.Canvas("c")
const a = new fabric.Rect({
width: 100,
height: 100,
left: 100,
top: 20,
angle: -10,
fill: 'rgba(0,200,0,1)'
})
const b = new fabric.Rect({
width: 50,
height: 100,
left: 150,
top: 50,
angle: 45,
stroke: '#eee',
strokeWidth: 10,
fill: 'rgba(0,0,200,1)'
})
const c = new fabric.Circle({
radius: 50,
left: 150,
top: 75,
fill: '#aac'
})
const group = new fabric.Group([a, b, c])
canvas.add(group).requestRenderAll()
document.querySelector('#b1').addEventListener('click', () => {
const bottomChild = group.getObjects()[0]
bottomChild.moveTo(group.size() - 1)
// force fabric to redraw the group
group.dirty = true
canvas.requestRenderAll()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.js"></script>
<canvas id='c' width="400" height="200"></canvas>
<button id='b1'>move bottom to top</button>

Fabric.js: Assigning background color to group

I am trying to set a background color for a group without affecting its contained objects.
So far my code looks like this:
var canvas = new fabric.Canvas('canvas');
var helloText = new fabric.Text('hello', {
fontSize: 30,
top: 10, left: 10,
originX: 0, originY: 0
});
var worldText = new fabric.Text('world!', {
fontSize: 40,
top: 50, left: 100,
originX: 0, originY: 0
});
var group = new fabric.Group([helloText, worldText], {
selectionBackgroundColor: 'red',
backgroundColor: 'blue'
});
canvas.add(group);
A jsFiddle version can be found here.
As you can see from my code, I already tried the attribute backgroundColor yet it only affects contained objects. I would like to achieve an effect similar to selectionBackgroundColor.
Slight tweak, but this should do it for you (relevant code):
var text = new fabric.Group([helloText, worldText], {});
var textBoundingRect = text.getBoundingRect();
var background = new fabric.Rect({
top: textBoundingRect.top,
left: textBoundingRect.left,
width: textBoundingRect.width,
height: textBoundingRect.height,
fill: 'blue'
});
var group = new fabric.Group([background, text], {});
Your JSFiddle updated, https://jsfiddle.net/rekrah/92ss3d86/.

How to put canvas within canvas?

I just want to know what is the best way to use multiple canvas in a single page. These canvas can be overlapped on each other.
I tried to search this issue on different form, but wasn't able to find any helpful material. This is what we actually want to do(in the following image). There are 5 canvases, and we want all of them to be fully functional. We can add images, text and draw different things on selected canvas.
We are currently using fabricjs.
If that`s not possible, what is the best solution for achieving something like that ?
Thanks in advance!
Simply use CSS for that.
<div class="wrapper">
<canvas id="background_layer" class="canvas-layer" width="100" height="100"></canvas>
<canvas id="other_layer" class="canvas-layer" width="100" height="100"></canvas>
</div>
<style>
.wrapper { position: relative }
.canvas-layer {
position: absolute; left: 0; top: 0;
}
</style>
I am not sure what you are trying to achieve but you can refer to this Fiddle http://jsfiddle.net/PromInc/ZxYCP/
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
// Note the use of the `originX` and `originY` properties, which we set
// to 'left' and 'top', respectively. This makes the math in the `clipTo`
// functions a little bit more straight-forward.
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 180,
top: 10,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
var clipRect2 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 10,
top: 10,
width: 150,
height: 150,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect2.set({
clipFor: 'logo'
});
canvas.add(clipRect2);
function findByClipName(name) {
return _(canvas.getObjects()).where({
clipFor: name
}).first()
}
// Since the `angle` property of the Image object is stored
// in degrees, we'll use this to convert it to radians.
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
var clipByName = function (ctx) {
this.setCoords();
var clipRect = findByClipName(this.clipName);
var scaleXTo1 = (1 / this.scaleX);
var scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth;
var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth;
var ctxWidth = clipRect.width - clipRect.strokeWidth;
var ctxHeight = clipRect.height - clipRect.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
}
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 45,
width: 500,
height: 500,
left: 230,
top: 50,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
var logoImg = new Image();
logoImg.onload = function (img) {
var logo = new fabric.Image(logoImg, {
angle: 0,
width: 550,
height: 190,
left: 50,
top: 50,
scaleX: 0.25,
scaleY: 0.25,
clipName: 'logo',
clipTo: function(ctx) {
return _.bind(clipByName, logo)(ctx)
}
});
canvas.add(logo);
};
logoImg.src = img01URL;
I hope this might help.

Fabric.js: can't get the active groups anymore

I'm using fabric 1.4.10 and i noticed that i can't get the active groups anymore! Everytime i call canvas.getActiveGroup() even when a group is selected, i get null.
My code to create groups:
var circle1 = new fabric.Circle({
radius: 50,
fill: 'red',
left: 0
});
var circle2 = new fabric.Circle({
radius: 50,
fill: 'green',
left: 100
});
var circle3 = new fabric.Circle({
radius: 50,
fill: 'blue',
left: 200
});
var group = new fabric.Group([ circle1, circle2, circle3 ], {
left: 200,
top: 100
});
canvas.add(group);
Can anyone help please?
This is because you have to check for "activeObject".
fabric.canvas.getActiveObject() will return the current selected object.
getActiveGroup will return a group if there is a temporary group selection made with interactivity layer. ( mouse )
You can add these two lines code to try:
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();

Categories