Contextmenu on the objects in fabricjs - javascript

Can you please help me in creating a context menu on the fabric object. I have googled a lot but couldn't find exact solution. I have created two objects as below on the fabric. How can i bind a context menu to the fabric object?
Fiddle link: http://jsfiddle.net/fabricjs/S9sLu/
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 50,
fill: '#9f9',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
Thanks in advance.

I'm using contextMenu.js, this should get you started:
function contextMenu () {
var ctxTarget = null;
var menu = [{
name: 'Select Object',
img: '',
title: 'Select Object',
fun: function (o, jqEvent) {
canvas.setActiveObject(ctxTarget);
console.log(ctxTarget);
}
}];
$('.upper-canvas').on('contextmenu', function (e) {
e.preventDefault();
ctxTarget = canvas.findTarget(e.originalEvent);
});
$('.upper-canvas').contextMenu(menu, {
triggerOn: 'contextmenu',
closeOnClick: true,
});
}

Related

Is it possible to have custom-shaped canvas in Fabric.js?

I would like to have canvas in Fabric.js custom shape. For example circle or triangle. So the user is restricted only to this circle shape and overflow of it is naturally hidden just like using standard rectangular canvas.
I couldn't figure it out from documentation and tutorials of Fabric.js. Can anyone help me find a way ?
For explaining why I need this - I want to let my users design their own fabric labels for clothing and we have different label shapes, not only rectangles.
my demo:
// Initialize set canvas size
var canvas = new fabric.Canvas("canvas", {
width: 800,
height: 600,
});
// Ellipse
fabric.Image.fromURL('../public/img/1.jfif',img => {
img.set({
left: 20,
top: 20,
clipPath: new fabric.Ellipse({
rx: 70,
ry: 50,
originX: 'center',
originY: 'center'
})
})
canvas.add(img)
canvas.renderAll()
})
// Circle
fabric.Image.fromURL('../public/img/2.jfif',img => {
img.set({
left: 300,
top: 20,
clipPath: new fabric.Circle({
radius: 60,
originX: 'center',
originY: 'center',
})
})
canvas.add(img)
canvas.renderAll()
})
// Rounded rectangle
fabric.Image.fromURL('../public/img/3.jfif',img => {
img.set({
left: 550,
top: 20,
clipPath: new fabric.Rect({
width:120,
height:100,
rx:20,
ry:20,
originX: 'center',
originY: 'center',
})
})
canvas.add(img)
canvas.renderAll()
})
// Triangle
fabric.Image.fromURL('../public/img/4.jfif',img => {
img.set({
left: 20,
top: 200,
clipPath: new fabric.Triangle({
width: 100, // 底边宽度
height: 100, // 底边到定点的距离
originX: 'center',
originY: 'center',
})
})
canvas.add(img)
canvas.renderAll()
})
// The native capabilities of canvas are suitable for tailoring. Version 4.2.0 does not work, but 2.6.0 can achieve the effect.
fabric.Image.fromURL('../public/img/4.jfif',img => {
img.set({
left: 300,
top: 200,
clipTo: function (ctx) {
ctx.arc(0, 0, 60, 0, Math.PI * 2, true);//裁剪圆形
}
})
canvas.add(img)
canvas.renderAll()
})
// Polygons don't work.
fabric.Image.fromURL('../public/img/4.jfif',img => {
img.set({
left: 300,
top: 200,
clipPath:new fabric.Polygon([
{x: 300, y: 200},
{x: 500, y: 320},
{x: 500, y: 400},
{x: 320, y: 380}
],{
backgroundColor:'#cccccc',
opacity:0.6,
stroke:'#6639a6',
strokeWidth:2,
originX: 'center',
originY: 'center'
})
})
console.log(img)
canvas.add(img)
canvas.renderAll()
})
// The line segment does not work
fabric.Image.fromURL('../public/img/4.jfif',img => {
img.set({
left: 550,
top: 200,
clipPath:new fabric.Polyline([
{x:450,y:450},
{x:530,y:450},
{x:450,y:530},
{x:530,y:530},
],{
stroke:'#6639a6',
strokeWidth:2
})
})
canvas.add(img)
canvas.renderAll()
})
// Set the background of the canvas
canvas.setBackgroundColor('pink', canvas.renderAll.bind(canvas))

Can't move FabricJS svg or group objects to the canvas top left corner

I'm working on a FabricJS app where I want the user to move objects to the canvas origin by a simple click, even after rotating or scaling objects or a group of objects.
To do so, when clicking I perform the code below when the user click:
obj.left= obj.getBoundingRect().width/2;
obj.top=obj.getBoundingRect().height/2;
It works perfectly with rectangles or texts, but it doesn't work at all when using shape, svg or even group of objects. I have no idea why!
Below it's the complete code.
IMPORTANT: Please enable cross-origin access in your browser for the svg to be loaded.
this.__canvas = new fabric.Canvas('meCanvas', {
preserveObjectStacking: true,
height: 400,
width: 400,
backgroundColor: '#1F1F1F',
canvasKey:'azpoazpoaz'
});
let rect = new fabric.Rect({
fill: 'red',
width: 200,
height: 100,
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let text = new fabric.IText('Text', {
fontFamily: 'Times',
fontSize: 18,
fill: 'white',
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let url = 'https://svgshare.com/i/9DX.svg';
fabric.loadSVGFromURL(url,(objects,options)=> {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.left=50;
loadedObjects.top=150;
this.__canvas.add(loadedObjects);
});
let url2 = 'https://svgshare.com/i/9Cp.svg';
fabric.loadSVGFromURL(url2,(objects,options)=> {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.left=100;
loadedObjects.top=200;
loadedObjects.angle=20;
this.__canvas.add(loadedObjects);
});
this.__canvas.add(rect);
this.__canvas.add(text);
this.__canvas.renderAll();
$('#pushorigin').click((e)=>{
let obj = this.__canvas.getActiveObject();
obj.left= obj.getBoundingRect().width/2;
obj.top=obj.getBoundingRect().height/2;
this.__canvas.requestRenderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<div style='display: inline-block'>
<div>
<canvas id='meCanvas' ref='meFabric'/>
</div>
<div>
<button id='pushorigin'>Push to origin</button>
</div>
</div>
If you select the text or the rectangle and click the button "Push to origin", they will go to the origin (top left corner). But the same action doesn't work with the svg (triangle and the polygone) and it goes even worst if we stretch those svg or even rotating them, they won't go to the origin. Same thing when using a group of object!
I don't know what to do, I'm lost. I've googled for days and found nothing.
Please help!
Set originX: 'center',originY: 'center' for all object then your solution will work.
this.__canvas = new fabric.Canvas('meCanvas', {
preserveObjectStacking: true,
height: 400,
width: 400,
backgroundColor: '#1F1F1F',
canvasKey: 'azpoazpoaz'
});
let rect = new fabric.Rect({
fill: 'red',
width: 200,
height: 100,
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let text = new fabric.IText('Text', {
fontFamily: 'Times',
fontSize: 18,
fill: 'white',
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let url = 'https://svgshare.com/i/9DX.svg';
fabric.loadSVGFromURL(url, (objects, options) => {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.set({
left: 50,
top: 150,
originX: 'center',
originY: 'center'
})
this.__canvas.add(loadedObjects);
});
let url2 = 'https://svgshare.com/i/9Cp.svg';
fabric.loadSVGFromURL(url2, (objects, options) => {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.set({
left: 100,
top: 200,
angle: 20,
originX: 'center',
originY: 'center'
})
this.__canvas.add(loadedObjects);
});
this.__canvas.add(rect);
this.__canvas.add(text);
this.__canvas.renderAll();
$('#pushorigin').click((e) => {
let obj = this.__canvas.getActiveObject();
obj.set({
left: obj.getBoundingRect().width / 2,
top: obj.getBoundingRect().height / 2
}).setCoords()
this.__canvas.requestRenderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<div style='display: inline-block'>
<div>
<canvas id='meCanvas' ref='meFabric'/>
</div>
<div>
<button id='pushorigin'>Push to origin</button>
</div>
</div>

Replacing functions without clearing the whole canvas?

I'm trying to replace text with functions, without clearing all drawn text. Right now I can replace a function but only by clearing the whole canvas. I'd like it to be a little bit more dynamic so that a third function (for example) would remain.
Here's what I've got so far; note how the original text is cleared:
var $ = function(id) {
return document.getElementById(id)
};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(500);
function textOne() {
canvas.clear();
canvas.add(new fabric.IText('One', {
left: 50,
top: 100,
fontFamily: 'arial',
fill: '#333',
fontSize: 50
}));
}
// Text that should stay
canvas.add(new fabric.IText('This Should Stay The Same\nEdited Or Not', {
left: 300,
top: 45,
fontFamily: 'Monsieur La Doulaise',
fontSize: 27,
hasBorders: false,
hasControls: false,
selectable: true,
lockRotation: true,
lockMovementX: true,
lockMovementY: true,
align: 'mid',
originX: 'center',
originY: 'center',
centeredScaling: true,
}));
function textTwo() {
canvas.clear();
canvas.add(new fabric.IText('Two', {
left: 200,
top: 100,
fontFamily: 'arial black',
fill: '#333',
fontSize: 50
}));
}
canvas {
border: 1px solid #dddddd;
border-radius: 3px;
margin-top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<button onclick="textOne()">One</button>
<button onclick="textTwo()">Two</button>
<canvas id="c"></canvas>
Thanks in advance!
You just have to add an empty text inside your canvas and update it inside the corresponding functions. then execute canvas.renderAll after the updates. FYI, I have ZERO experience with fabric.js.
var $ = function(id) {
return document.getElementById(id)
};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(500);
var dynamicText = new fabric.IText('', {
left: 50,
top: 100,
fontFamily: 'arial',
fill: '#333',
fontSize: 50
})
canvas.add(dynamicText);
function textOne() {
dynamicText.setText('ONE');
canvas.renderAll();
}
// Text that should stay
canvas.add(new fabric.IText('This Should Stay The Same\nEdited Or Not', {
left: 300,
top: 45,
fontFamily: 'Monsieur La Doulaise',
fontSize: 27,
hasBorders: false,
hasControls: false,
selectable: true,
lockRotation: true,
lockMovementX: true,
lockMovementY: true,
align: 'mid',
originX: 'center',
originY: 'center',
centeredScaling: true,
}));
function textTwo() {
dynamicText.setText('TWO');
canvas.renderAll();
}
canvas {
border: 1px solid #dddddd;
border-radius: 3px;
margin-top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<button onclick="textOne()">One</button>
<button onclick="textTwo()">Two</button>
<canvas id="c"></canvas>

uniScaleTransform property is not working?

I do not want uniform scaling at corner points so i have added uniScaleTransform property to true at the time of initiation but it is not working , still it's scaling uniformly.I am using fabric js version 1.6.3, below is the code,
<!-- fabric js code-->
var canvas = new fabric.Canvas('canvas', {
uniScaleTransform : true
});
I've added a working example below that seem to work fine. If you are seeing different results please do post a snippet of working code demonstrating the problem.
var canvas = new fabric.Canvas('c', { uniScaleTransform : true, preserveObjectStacking:true });
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 50,
fill: '#9f9',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></canvas>

Modifying Grid Height/Width

I am working on a project that is a world planner/builder for a game that I enjoy. I want to make the grid 100 * 54. I have been messing around with some code on JSFiddle and I can't seem to get what I want. I would like the size of the grid squares to be 16*16
var canvas = new fabric.Canvas('c', {
selection: false
});
var grid = 50;
// create grid
for (var i = 0; i < (600 / grid); i++) {
canvas.add(new fabric.Line([i * grid, 0, i * grid, 600], {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Line([0, i * grid, 600, i * grid], {
stroke: '#ccc',
selectable: false
}))
}
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas {
border: 1px solid #ccc;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
The way this works:
Grid is how big your grid will be in pixels.
You need two for loops to indicate how many lines for each.
The for loops generate the grid based on how many loops it goes through. (so 54 means 54 lines.)
The 2000 indicates how long the line will be in pixels.
Working code:
var canvas = new fabric.Canvas('c', {
selection: false
});
var grid = 16;
// create grid
for (var i = 0; i < 100; i++) {
canvas.add(new fabric.Line([i * grid, 0, i * grid, 2000], {
stroke: '#ccc',
selectable: false
})); // horizontal
}
for (var i = 0; i < 54; i++) {
canvas.add(new fabric.Line([0, i * grid, 2000, i * grid], {
stroke: '#ccc',
selectable: false
})); // vertical
}
// add objects
canvas.add(new fabric.Rect({
left: 16,
top: 16,
width: 16,
height: 16,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// add objects
canvas.add(new fabric.Rect({
left: 16,
top: 16,
width: 16,
height: 16,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas {
border: 1px solid #ccc;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="1600px" height="865px" ></canvas>

Categories