I am trying to use FabricJS to animate any object when it is selected.
So when selected an object 'shrinks' from center on mouse:down (10px off the width and 10px off the height)
And on mouse:up it returns to the original size and state.
I don't know if I need to use FabricJS built in animation for this. I wanted it to grow and shrink linear though.
Right now my Fiddle does pretty much nothing, I can't get far enough without errors.
Does anyone have any examples of this or similar? I couldn't find anything about changing the appearance of FabricJS objects on mouse:up/:down when selected.
(function() {
var canvas = this.__canvas = new fabric.Canvas('canvas');
var touchedObject;
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: 'blue',
hasBorders: false,
});
canvas.add(rect);
var rect2 = new fabric.Rect({
left: 190,
top: 50,
width: 50,
height: 50,
fill: 'red',
hasBorders: false,
});
canvas.add(rect2);
canvas.on('object:selected', function(evn) {
/*animate({
'width': ,
'height':
}, {
duration: 200,
});*/
});
canvas.renderAll();
})();
You could accomplish that using the fabric.util.animate() method.
(function() {
var canvas = this.__canvas = new fabric.Canvas('canvas');
var touchedObject;
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: '#07C',
hasBorders: false,
});
canvas.add(rect);
// disable controls and set hover-cursor
canvas.forEachObject(function(o) {
o.hasBorders = o.hasControls = false;
});
canvas.hoverCursor = 'pointer';
// mouse events
canvas.on('mouse:down', function(e) {
animate(e, 1);
});
canvas.on('mouse:up', function(e) {
animate(e, 0);
});
function animate(e, p) {
if (e.target) {
fabric.util.animate({
startValue: e.target.get('height'),
endValue: e.target.get('height') + (p ? -10 : 50 - e.target.height),
duration: 200,
onChange: function(v) {
e.target.setHeight(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('width'),
endValue: e.target.get('width') + (p ? -10 : 50 - e.target.width),
duration: 200,
onChange: function(v) {
e.target.setWidth(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('top'),
endValue: e.target.get('top') + (p && 5),
duration: 200,
onChange: function(v) {
e.target.setTop(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('left'),
endValue: e.target.get('left') + (p && 5),
duration: 200,
onChange: function(v) {
e.target.setLeft(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
}
}
canvas.renderAll();
})();
#canvas {
border: 1px solid lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
<canvas id="canvas" width="150"></canvas>
Related
I'm trying to check for a collision between 2 text objects and using the intersectsWithObject. It's working but it's taking the bouding rect into account. Is it possible to check on pixel level?
Current behaviour:
Wanted behaviour:
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const text = new fabric.Textbox('some text', {
width: 300,
fontSize: 70,
top: 120,
left: 100
});
const text2 = new fabric.Textbox('some more text', {
width: 350,
fontSize: 50,
top: 200,
left: 20,
})
if (text.intersectsWithObject(text2, true, true)) {
text.set('fill', 'red');
}
else {
text.set('fill', 'black');
}
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect();
canvas.contextContainer.strokeRect(
bound.left + 0.5,
bound.top + 0.5,
bound.width,
bound.height
);
})
});
canvas.add(text);
canvas.add(text2);
https://jsbin.com/menadejato/edit?js,console,output
I have a function that hides and shows an image whenever it's clicked, but I want to do the same thing, by clicking another object.
Here is the code for the image
function drawCheckbox(left, top, width, height){
var imgClass = new fabric.Image.fromURL('https://image.flaticon.com/icons/svg/33/33281.svg',function(img){
img.width = width;
img.height = height;
img.left = left;
img.top = top;
img.set({
hoverCursor: 'default',
selectable: true,
opacity: 0
})
img.on('mousedown', function(e) {
if(e.target.opacity <= 0.5){
e.target.opacity = 1;
}else{
e.target.opacity = 0;
}
canvas.renderAll();
});
canvas.add(img);
canvas.renderAll();
})
}
And this is the code for the rectangle object:
function addRect(left, top, width, height, id) {
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: 'center',
originY: 'center',
centeredRotation: true,
snapAngle: 45,
selectable: true
})
const t = new fabric.IText(number.toString(), {
fontFamily: 'Calibri',
fontSize: 14,
fill: '#fff',
textAlign: 'center',
originX: 'center',
originY: 'center'
})
const g = new fabric.Group([o, t], {
left: left,
top: top,
centeredRotation: true,
snapAngle: 45,
selectable: true,
type: 'table',
id: id,
number: number
})
canvas.add(g)
number++
g.on('selected', function () {
// here I want to make de image dissapear, when the object is clicked
})
canvas.hoverCursor = 'pointer';
canvas.discardActiveObject();
canvas.renderAll();
return g;
}
I tried creating the image inside the rectangle, but it doesn't do anything when the rectangle it's clicked. Does anyone had a similar problem?
Here is the codepen: codepen.io/Zerro1/pen/PoZvmOE .
Here i have created a codepen for you, this is one way to do it. I tried to create a square and clicking on that square i am toggling the last checkbox visibility.
portion of code:
var square = new fabric.Rect({
width: 100,
height: 100,
fill: '#000',
left:120
});
square.on('mousedown', function(e) {
if(img.opacity <= 0.5){
img.opacity = 1;
}else{
img.opacity = 0;
}
canvas.renderAll();
});
canvas.add(img);
canvas.add(square);
canvas.renderAll();
How can you create a checkbox like component with Fabric JS? More like to click on the image, and to set the opacity to 0. If you click again, to set the opacity to 1.
Here is my code:
For the checkbox image:
fabric.Image.fromURL('https://image.flaticon.com/icons/svg/33/33281.svg', (image) => {
image.scale(0.35);
image.set({
left: 152,
top: 120,
hoverCursor: 'default',
selectable: true,
opacity: 0.5,
hasControls: false,
lockMovementX: true,
lockMovementY: true
})
if(image.onselect === true ) {
image.set().opacity = 0;
}
canvas.add(image);
});
The box where the image is put:
function addBox(left, top, width, height) {
const o = new fabric.Rect({
left: left,
top: top,
width: 30,
height: 30,
fill: boxFill,
strokeWidth: 2,
originX: 'left',
originY: 'top',
centeredRotation: true,
snapAngle: 45,
selectable: true,
type: 'box',
id: generateId()
})
if(image.onselect === true ) {
image.set().opacity = 0;
}
canvas.add(o)
canvas.getObjects().map(o => {
o.hasControls = false
o.lockMovementX = true
o.lockMovementY = true
o.borderColor = '#38A62E'
o.borderScaleFactor = 2.5
})
canvas.selection = false
canvas.hoverCursor = 'pointer'
canvas.discardActiveObject()
canvas.renderAll()
return o
}
How can I resolve this issue? Is there a way to make the image appear and dissapear when you click on it ( using opacity or something similarly) ?
Hope this helps you and many who want to add Event on objects.
var canvas = new fabric.Canvas("canvas");
function drawCheckbox(left,right, width, height){
var imgClass = new fabric.Image.fromURL('https://image.flaticon.com/icons/svg/33/33281.svg',function(img){
img.width = width;
img.height = height;
img.left = left;
img.top = right;
img.hasControls = false
img.on('mousedown', function(e) {
if(e.target.opacity <= 0.5){
e.target.opacity = 1;
}else{
e.target.opacity = 0.4;
}
canvas.renderAll();
});
canvas.add(img);
canvas.renderAll();
})
}
drawCheckbox(0,0, 100,100)
drawCheckbox(100,100, 100,100)
drawCheckbox(200,200, 100,100)
body {
background-color:silver;
}
canvas {
border:1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"> </script>
<canvas id="canvas" width=300 height=300></canvas><br>
I'm creating a shape with certain width and height (this shape is a clipping rect) inside canvas. Then inside this shape I'm loading an object which can be moved and rotated.
I wrote a custom function for get center point inside this shape, and it works good if object are not rotated.
If I rotate this object over 90deg and click "center" button - the object is moved up outside the clipping rect.
I cannot use a native FabricJs "centerV" function, because I would like that object will be centered inside clipping rect - not the canvas container, so that is why I made a variable "objectVerticalCenter".
My code is presented here:
var canvas = new fabric.Canvas('canvas', {
'selection': false
});
var clippingRect = new fabric.Rect({
left: 170,
top: 90,
width: 185,
height: 400,
fill: 'transparent',
stroke: 1,
opacity: 1,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
selectable: false,
preserveObjectStacking: true,
objectCaching: false
});
var retinaScalling = canvas.getRetinaScaling();
var pol = new fabric.Polygon([{
x: 200,
y: 0
}, {
x: 250,
y: 50
}, {
x: 250,
y: 100
}, {
x: 150,
y: 100
}, {
x: 150,
y: 50
}], {
left: 250,
top: 150,
angle: 0,
fill: 'green'
});
pol.scaleX = 0.5;
pol.scaleY = 0.5;
pol.set('id', 'shape');
pol.scaleToWidth(clippingRect.getWidth());
pol.setCoords();
pol.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
canvas.centerObjectH(pol);
canvas.add(pol);
canvas.renderAll();
canvas.setActiveObject(pol);
canvas.add(pol);
canvas.renderAll();
document.getElementById('rotate').onclick = function() {
var activeObject = canvas.getActiveObject();
activeObject.setAngle(200);
activeObject.setCoords();
canvas.renderAll();
};
document.getElementById('center').onclick = function() {
var activeObject = canvas.getActiveObject();
var rectHeight = activeObject.getBoundingRectHeight();
var objectVerticalCenter = (clippingRect.getHeight() / 2) - (rectHeight / 2) + clippingRect.top;
activeObject.set('top', objectVerticalCenter);
activeObject.setCoords();
canvas.renderAll();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<button id="rotate">Rotate</button>
<button id="center">Center</button>
<canvas id="canvas" width="530" height="600"></canvas>
JSFiddle
To reproduce my problem you have to, click "rotate", then "center" button and you will see that shape is moved up outside the clipping rect.
If object is not rotated centering works fine.
Is there any way to fix my centering function or way to use "centerV" function inside the clipping rect?
Regards
If you want to center an object inside another, the safest thing to do is:
get the center point of the container
set the center point of the object in that point
in code this could equal to:
var clipCenter = clippingRect.getCenterPoint();
activeObject.setPositionByOrigin(clipCenter,'center','center');
var canvas = new fabric.Canvas('canvas', {
'selection': false
});
var clippingRect = new fabric.Rect({
left: 170,
top: 90,
width: 185,
height: 400,
fill: 'transparent',
stroke: 1,
opacity: 1,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
selectable: false,
preserveObjectStacking: true,
objectCaching: false
});
var retinaScalling = canvas.getRetinaScaling();
var pol = new fabric.Polygon([{
x: 200,
y: 0
}, {
x: 250,
y: 50
}, {
x: 250,
y: 100
}, {
x: 150,
y: 100
}, {
x: 150,
y: 50
}], {
left: 250,
top: 150,
angle: 0,
fill: 'green'
});
pol.scaleX = 0.5;
pol.scaleY = 0.5;
pol.set('id', 'shape');
pol.scaleToWidth(clippingRect.getWidth());
pol.setCoords();
pol.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
canvas.centerObjectH(pol);
canvas.setActiveObject(pol);
canvas.add(pol);
canvas.renderAll();
document.getElementById('rotate').onclick = function() {
var activeObject = canvas.getActiveObject();
activeObject.setAngle(200);
activeObject.setCoords();
canvas.renderAll();
};
document.getElementById('center').onclick = function() {
var activeObject = canvas.getActiveObject();
var clipCenter = clippingRect.getCenterPoint();
activeObject.setPositionByOrigin(clipCenter,'center','center');
canvas.renderAll();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<button id="rotate">Rotate</button>
<button id="center">Center</button>
<canvas id="canvas" width="530" height="600"></canvas>
Have such code :
var $canvas = $("#canvas");
var canvas = new fabric.Canvas('canvas', {
hoverCursor: 'pointer',
selection: false
});
var rect1 = new fabric.Rect({
width: 200,
height: 100,
left: 0,
top: 50,
angle: 30,
fill: 'rgba(255,0,0,0.5)'
});
canvas.add(rect1);
$canvas.droppable({
drop: dragDrop,
over: enterIn,
});
function enterIn(e, ui) {
event.preventDefault();
rect1.width = 400;
}
function dragDrop(e, ui) {
var element = ui.draggable;
var imgInstance = new fabric.Image(element.data("image"), {
left: x,
top: y,
});
canvas.add(imgInstance);
}
Problem:
rect1 width change is applied only after drop on canvas.
But I tested that enterIn function is working fine.
Could you advise something?