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
Related
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>
WARNING: turn the volume down before you run the snippet!
I want to be able to click on the stage to add a 'module' shape. But I have found that a click on the 'module' shape itself creates another, meaning that the stage.click listener is being fired when it should not be.
How can I have a stage.click listener that does not fire incorrectly when I click on a shape ?
var width = window.innerWidth;
var height = window.innerHeight;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var group = new Konva.Group({
draggable: true
});
stage.on('contentClick', function() {
createModule();
});
function createModule() {
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: true
});
group.add(rect);
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black'
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function() {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
var width = window.innerWidth;
var height = window.innerHeight;
//var drag = false;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var group = new Konva.Group({
draggable: true
});
stage.on('contentClick', function() {
createModule();
});
function createModule() {
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: true
});
group.add(rect);
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black'
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function() {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
<script src="https://tonejs.github.io/build/Tone.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<div id="container"></div>
The stage.contentClick() listener is a special case to be used when you want the stage to listen to events on the stage content. However, the cancelBubble() function does not stop events bubbling from say a click on a shape to the stage.contentClick() listener.
To get the effect that you want, which is to give the impression that a click on the stage has happened, you need to add a rect that fills the stage and listen for events on that rect instead of the stage.
Below is a working example. The red background I added deliberately so you know there is something else above the stage. To remove this take out the fill color on the clickRect.
I also fixed up your buttons so that the contents are correctly grouped and drag together. You were almost correct but you needed the group to be created within in the createModule() function. You can see that I also made the group elements dragabble = false to complete the process.
I added a couple of console writes to show when the events fire.
[Also I got quite a shock when I switched on the tone for tone].
var width = window.innerWidth;
var height = window.innerHeight;
//var drag = false;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
stage.add(layer);
var clickRect = new Konva.Rect({
x:0,
y:0,
width: width,
height: height,
fill: 'red',
stroke: '#807C7B',
strokeWidth: 2,
listening: 'true'
})
layer.add(clickRect);
clickRect.on('click', function() {
console.log('Stage click');
createModule();
});
function createModule() {
var group = new Konva.Group({ // move group create into createModule
draggable: true // we will make the elements not draggable - we drag the group
});
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: false // make the element not draggable - we drag the group
});
group.add(rect);
rect.on('click', function(evt){
console.log('Clicked on button');
})
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
listening: true,
draggable: false // make the element not draggable - we drag the group
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black',
draggable: false // make the element not draggable - we drag the group
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function(evt) {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
stage.draw(); // draw so we can see click rect.
<script src="https://tonejs.github.io/build/Tone.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<div id="container" style="background-color: gold;"></div>
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>
I try to add several times the same element in my paper.
Then I created it thanks to embed and then I cloned it with deep:true.
My problem is that deep:true change some properties (I don't know which) but I'm now enable to apply a translation to it.
here is my code, that work without deep:true but doesn't with. (but clone a rect and not my combined element) :
var width=400, height=1000;
var graph = new joint.dia.Graph; //define a graph
var paper = new joint.dia.Paper({ //define a paper
el: $('#myholder'),
width: width,
height: height,
gridSize: 1,
model: graph, //include paper in my graph
restrictTranslate: function(element) { //restriction of drag n drop to the vertical line (x never changes)
return {
x: element.model.attributes.position.x, //this represent the original x position of my element
y: 0,
width: 0,
height: height
};
}
});
var rect1 = new joint.shapes.basic.Rect({ //create first rect (with input and output)
position: { x: 100, y: 20 },
size: { width: 90, height: 90 },
attrs: {
rect: { fill: '#2ECC71' }
}
});
var add_block= new joint.shapes.basic.Circle({ //create add button
position: { x: 170, y: 20 },
size: { width: 20, height: 20 },
attrs: {
circle: { fill: '#2ECC71'},
text:{text:'+', "font-size":20}
}
});
rect1.embed(add_block);
graph.addCell([rect1,add_block]);
var clones=rect1.clone({deep:true});
graph.addCells(clones);
var line = V('line', { x1: 250, y1: 10, x2: 250, y2: 500, stroke: '#ddd' });
V(paper.viewport).append(line);
//Create a new block and link between two blocks
//chek if click on + button
//if click on this button, create a new block and link between them
paper.on('cell:pointerclick', function(cellView, evt, x, y) {
var elementBelow = graph.get('cells').find(function(cell) {
if (!cell.get('parent')) return false;//not interested in parent
if (cell instanceof joint.dia.Link) return false; // Not interested in links.
if (cell.getBBox().containsPoint(g.point(x, y))) {
return true;
}
return false;
});
if(elementBelow) //if we start from a block, let's go!
{
rect=rect1.clone({deep:true});
graph.addCell(rect);
rect.translate(200, 0);
}
});