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))
Related
I am trying to resize a rectangle based on the user input, but i get an error saying: Cannot read property 'addEventListener' of null. Do i need to change something in the HTML? I tried moving the script below the body but it didn't work. Seems like everything is alright but it doesn't work.
https://jsfiddle.net/sbLk9zvd/2/
var rect = new fabric.Rect({
fill: "red",
width: 300,
height: 400,
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
canvas.add(rect);
var Length = document.getElementById("Length");
var Width = document.getElementById("Width");
Length.addEventListener("input", Modify_Length);
Width.addEventListener("input", Modify_Width);
function Modify_Length() {
var rect = new fabric.Rect({
fill: "red",
width: parseFloat(Width.value) || 300,
height: 400 || parseFloat(Length.value),
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
}
function Modify_Width() {
var rect = new fabric.Rect({
fill: "red",
width: parseFloat(Width.value) || 300,
height: parseFloat(Length.value) || 400,
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
}
canvas.renderAll();
There is no need for a separate listener for each element. Just share a single callback function. Also, don't forget to rerender.
const canvas = new fabric.Canvas("c");
const rect = new fabric.Rect({
fill: "red",
width: 200,
height: 80,
stroke: "gray",
strokeWidth: 1,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
canvas.on("object:scaling", function() {
var obj = canvas.getActiveObject(),
width = obj.width,
height = obj.height,
scaleX = obj.scaleX,
scaleY = obj.scaleY;
obj.set({
width: width * scaleX,
height: height * scaleY,
scaleX: 1,
scaleY: 1,
});
});
rect.setControlsVisibility({
mt: false,
mb: false,
ml: false,
mr: false,
bl: false,
br: false,
tl: false,
tr: false,
mtr: false,
});
canvas.add(rect);
canvas.centerObject(rect);
canvas.setActiveObject(rect);
canvas.item(0).lockMovementX = true;
canvas.item(0).lockMovementY = true;
const width = document.getElementById('width');
const height = document.getElementById('height');
const adjust = (e) => {
Object.assign(rect, {
width: parseFloat(width.value) || 300,
height: parseFloat(height.value) || 400,
});
canvas.renderAll();
};
width.addEventListener('input', adjust);
height.addEventListener('input', adjust);
canvas.renderAll();
label, input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/451/fabric.min.js"></script>
<canvas id="c" width="250" height="120"></canvas>
<div id="user-input">
<label for="width">Width:</label>
<input type="number" id="width" name="width" value="200" />
<label for="height">Height:</label>
<input type="number" id="height" name="height" value="80" />
</div>
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>
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>
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,
});
}
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>