How to restrict freedrawing a circle in Fabricjs? - javascript

I am working on Fabricjs where I have to allow the user to freedraw some area on the image.
Now the fiddle contains code that is able to draw throughout the canvas.
I want it to be only restricted to draw within the image.
Here's the code :
`
var canvas = new fabric.Canvas("canvas2");
var circle, isDown, origX, origY;
fabric.Image.fromURL('http://www.beatnyama.com/wp-content/uploads/2015/05/assets.jpg', function(img){
img.evented=false;
img.selectable=false;
canvas.add(img);
minX = img.oCoords.tl.x;
maxX = img.oCoords.br.x;
minY = img.oCoords.tl.y;
maxY = img.oCoords.br.y;
canvas.sendToBack(img);
canvas.on('mouse:down', function(o){
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
radius: pointer.x-origX,
angle: 0,
fill: '',
stroke:'red',
strokeWidth:3,
});
canvas.add(circle);
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
var radius = Math.max(Math.abs(origY - pointer.y),Math.abs(origX - pointer.x))/2;
if (radius > circle.strokeWidth) {
radius -= circle.strokeWidth/2;
}
circle.set({ radius: radius});
if(origX>pointer.x){
circle.set({originX: 'right' });
} else {
circle.set({originX: 'left' });
}
if(origY>pointer.y){
circle.set({originY: 'bottom' });
} else {
circle.set({originY: 'top' });
}
canvas.renderAll();
});
canvas.on('mouse:up', function(o){
isDown = false;
});
});
`

Adapating the logic of your previous questions, one to each other, you should try this:
var canvas = new fabric.Canvas('canvas');
var minX, minY, maxX, maxY;
var circle, isDown, origX, origY, lastGoodRadius;
fabric.Image.fromURL('http://www.beatnyama.com/wp-content/uploads/2015/05/assets.jpg', function(img){
img.evented=false;
img.selectable=false;
canvas.add(img);
minX = img.oCoords.tl.x;
maxX = img.oCoords.br.x;
minY = img.oCoords.tl.y;
maxY = img.oCoords.br.y;
canvas.sendToBack(img);
});
canvas.on('mouse:down', function(o){
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
radius: pointer.x-origX,
angle: 0,
fill: '',
stroke:'red',
strokeWidth:3,
});
lastGoodRadius = 0;
canvas.add(circle);
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var restore = false;
var pointer = canvas.getPointer(o.e);
var radius = Math.max(Math.abs(origY - pointer.y),Math.abs(origX - pointer.x))/2;
if (radius > circle.strokeWidth) {
radius -= circle.strokeWidth/2;
}
var diam = radius * 2;
if(origX>pointer.x){
circle.set({originX: 'right' });
if (origX - diam < minX) {
restore = (origX-minX)/2;
}
} else {
circle.set({originX: 'left' });
if (origX + diam > maxX) {
restore = (maxX-origX)/2;
}
}
if(origY>pointer.y){
circle.set({originY: 'bottom' });
if (origY - diam < minY) {
restore = (origY-minY)/2;
}
} else {
circle.set({originY: 'top' });
if (origY + diam > maxY) {
restore = (maxY-origY)/2;
}
}
if(!restore) {
circle.set({ radius: radius});
} else {
circle.set({ radius: restore});
}
canvas.renderAll();
});
<script src="http://fabricjs.com/lib/fabric.js"></script>
<canvas id='canvas' width="550" height="550" style="border:#000 1px solid;"></canvas>

Related

FabricJS How can I update the custom control point of a polygon after Zoom or Pan the canvas?

Can someone please suggest me the right way to update the transform points and custom control points of my custom polygon (draw by mouse move + click) after zoom or pan the canvas?
Step to reproduce:
click on Draw Polygon
draw a polygon in canvas
zoom (scroll up or down) or pan (alt + mouse drag)
Here is my code: https://jsfiddle.net/ckitisak/qv6y283p/
/**
* based on:
* 1. https://codepen.io/durga598/pen/gXQjdw?editors=0010
* 2. http://fabricjs.com/custom-controls-polygon
*/
let activeLine;
let activeShape;
let canvas;
let lineArray = [];
let pointArray = [];
let drawMode = false;
function initCanvas() {
canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'rgba(0,0,0,0.3)';
canvas.setHeight(500);
canvas.setWidth(500);
fabric.Object.prototype.originX = 'center';
fabric.Object.prototype.originY = 'center';
canvas.on('mouse:down', onMouseDown);
canvas.on('mouse:up', onMouseUp);
canvas.on('mouse:move', onMouseMove);
canvas.on('object:moving', onObjectMove);
canvas.on('mouse:wheel', onMouseWheel);
}
function onMouseDown(options) {
if (drawMode) {
if (options.target && options.target.id === pointArray[0].id) {
// when click on the first point
generatePolygon(pointArray);
} else {
addPoint(options);
}
}
var evt = options.e;
if (evt.altKey === true) {
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
}
function onMouseUp(options) {
this.isDragging = false;
this.selection = true;
}
function onMouseMove(options) {
if (this.isDragging) {
var e = options.e;
this.viewportTransform[4] += e.clientX - this.lastPosX;
this.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
if (drawMode) {
if (activeLine && activeLine.class === 'line') {
const pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
const points = activeShape.get('points');
points[pointArray.length] = {
x: pointer.x,
y: pointer.y,
};
activeShape.set({
points
});
}
canvas.renderAll();
}
}
function onMouseWheel(options) {
var delta = options.e.deltaY;
var pointer = canvas.getPointer(options.e);
var zoom = canvas.getZoom();
if (delta > 0) {
zoom += 0.1;
} else {
zoom -= 0.1;
}
if (zoom > 20) zoom = 20;
if (zoom < 0.1) zoom = 0.1;
canvas.zoomToPoint({ x: options.e.offsetX, y: options.e.offsetY }, zoom);
options.e.preventDefault();
options.e.stopPropagation();
}
function onObjectMove(option) {
const object = option.target;
object._calcDimensions();
object.setCoords();
canvas.renderAll();
}
function toggleDrawPolygon(event) {
if (drawMode) {
// stop draw mode
activeLine = null;
activeShape = null;
lineArray = [];
pointArray = [];
canvas.selection = true;
drawMode = false;
} else {
// start draw mode
canvas.selection = false;
drawMode = true;
}
}
function addPoint(options) {
const pointOption = {
id: new Date().getTime(),
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: options.e.layerX / canvas.getZoom(),
top: options.e.layerY / canvas.getZoom(),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
objectCaching: false,
};
const point = new fabric.Circle(pointOption);
if (pointArray.length === 0) {
// fill first point with red color
point.set({
fill: 'red'
});
}
const linePoints = [
options.e.layerX / canvas.getZoom(),
options.e.layerY / canvas.getZoom(),
options.e.layerX / canvas.getZoom(),
options.e.layerY / canvas.getZoom(),
];
const lineOption = {
strokeWidth: 2,
fill: '#999999',
stroke: '#999999',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
};
const line = new fabric.Line(linePoints, lineOption);
line.class = 'line';
if (activeShape) {
const pos = canvas.getPointer(options.e);
const points = activeShape.get('points');
points.push({
x: pos.x,
y: pos.y
});
const polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
} else {
const polyPoint = [{
x: options.e.layerX / canvas.getZoom(),
y: options.e.layerY / canvas.getZoom(),
}, ];
const polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(point);
lineArray.push(line);
canvas.add(line);
canvas.add(point);
}
function generatePolygon(pointArray) {
const points = [];
// collect points and remove them from canvas
for (const point of pointArray) {
points.push({
x: point.left,
y: point.top,
});
canvas.remove(point);
}
// remove lines from canvas
for (const line of lineArray) {
canvas.remove(line);
}
// remove selected Shape and Line
canvas.remove(activeShape).remove(activeLine);
// create polygon from collected points
const polygon = new fabric.Polygon(points, {
id: new Date().getTime(),
stroke: '#eee',
fill: '#f00',
objectCaching: false,
moveable: false,
//selectable: false
});
canvas.add(polygon);
toggleDrawPolygon();
editPolygon();
}
/**
* define a function that can locate the controls.
* this function will be used both for drawing and for interaction.
*/
function polygonPositionHandler(dim, finalMatrix, fabricObject) {
const transformPoint = {
x: fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
y: fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y,
};
return fabric.util.transformPoint(transformPoint, fabricObject.calcTransformMatrix());
}
/**
* define a function that will define what the control does
* this function will be called on every mouse move after a control has been
* clicked and is being dragged.
* The function receive as argument the mouse event, the current trasnform object
* and the current position in canvas coordinate
* transform.target is a reference to the current object being transformed,
*/
function actionHandler(eventData, transform, x, y) {
const polygon = transform.target;
const currentControl = polygon.controls[polygon.__corner];
const mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center');
const size = polygon._getTransformedDimensions(0, 0);
const finalPointPosition = {
x: (mouseLocalPosition.x * polygon.width) / size.x + polygon.pathOffset.x,
y: (mouseLocalPosition.y * polygon.height) / size.y + polygon.pathOffset.y,
};
polygon.points[currentControl.pointIndex] = finalPointPosition;
return true;
}
/**
* define a function that can keep the polygon in the same position when we change its
* width/height/top/left.
*/
function anchorWrapper(anchorIndex, fn) {
return function(eventData, transform, x, y) {
const fabricObject = transform.target;
const point = {
x: fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
y: fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y,
};
// update the transform border
fabricObject._setPositionDimensions({});
// Now newX and newY represent the point position with a range from
// -0.5 to 0.5 for X and Y.
const newX = point.x / fabricObject.width;
const newY = point.y / fabricObject.height;
// Fabric supports numeric origins for objects with a range from 0 to 1.
// This let us use the relative position as an origin to translate the old absolutePoint.
const absolutePoint = fabric.util.transformPoint(point, fabricObject.calcTransformMatrix());
fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
// action performed
return fn(eventData, transform, x, y);
};
}
function editPolygon() {
let activeObject = canvas.getActiveObject();
if (!activeObject) {
activeObject = canvas.getObjects()[0];
canvas.setActiveObject(activeObject);
}
activeObject.edit = true;
activeObject.objectCaching = false;
const lastControl = activeObject.points.length - 1;
activeObject.cornerStyle = 'circle';
activeObject.controls = activeObject.points.reduce((acc, point, index) => {
acc['p' + index] = new fabric.Control({
positionHandler: polygonPositionHandler,
actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
actionName: 'modifyPolygon',
pointIndex: index,
});
return acc;
}, {});
activeObject.hasBorders = false;
canvas.requestRenderAll();
}
function resizePolygon() {
let activeObject = canvas.getActiveObject();
if (!activeObject) {
activeObject = canvas.getObjects()[0];
canvas.setActiveObject(activeObject);
}
activeObject.edit = false;
activeObject.objectCaching = false;
activeObject.controls = fabric.Object.prototype.controls;
activeObject.cornerStyle = 'rect';
activeObject.hasBorders = true;
canvas.requestRenderAll();
}
initCanvas();
<script src="https://unpkg.com/fabric#4.0.0-beta.12/dist/fabric.js"></script>
<button type="button" onclick="toggleDrawPolygon()">Draw Polygon</button>
<button type="button" onclick="editPolygon()">Edit Polygon</button>
<button type="button" onclick="resizePolygon()">Resize/Move Polygon</button>
<canvas id="c"></canvas>
i modified the code to take in account of the canvas zoom and panning.
The viewport transform needs to be take in account when calculating the position of the controls.
/**
* based on:
* 1. https://codepen.io/durga598/pen/gXQjdw?editors=0010
* 2. http://fabricjs.com/custom-controls-polygon
*/
let activeLine;
let activeShape;
let canvas;
let lineArray = [];
let pointArray = [];
let drawMode = false;
function initCanvas() {
canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'rgba(0,0,0,0.3)';
canvas.setHeight(500);
canvas.setWidth(500);
fabric.Object.prototype.originX = 'center';
fabric.Object.prototype.originY = 'center';
canvas.on('mouse:down', onMouseDown);
canvas.on('mouse:up', onMouseUp);
canvas.on('mouse:move', onMouseMove);
canvas.on('object:moving', onObjectMove);
canvas.on('mouse:wheel', onMouseWheel);
}
function onMouseDown(options) {
if (drawMode) {
if (options.target && options.target.id === pointArray[0].id) {
// when click on the first point
generatePolygon(pointArray);
} else {
addPoint(options);
}
}
var evt = options.e;
if (evt.altKey === true) {
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
}
function onMouseUp(options) {
this.isDragging = false;
this.selection = true;
}
function onMouseMove(options) {
if (this.isDragging) {
var e = options.e;
this.viewportTransform[4] += e.clientX - this.lastPosX;
this.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
if (drawMode) {
if (activeLine && activeLine.class === 'line') {
const pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
const points = activeShape.get('points');
points[pointArray.length] = {
x: pointer.x,
y: pointer.y,
};
activeShape.set({
points
});
}
canvas.renderAll();
}
}
function onMouseWheel(options) {
var delta = options.e.deltaY;
var pointer = canvas.getPointer(options.e);
var zoom = canvas.getZoom();
if (delta > 0) {
zoom += 0.1;
} else {
zoom -= 0.1;
}
if (zoom > 20) zoom = 20;
if (zoom < 0.1) zoom = 0.1;
canvas.zoomToPoint({ x: options.e.offsetX, y: options.e.offsetY }, zoom);
options.e.preventDefault();
options.e.stopPropagation();
}
function onObjectMove(option) {
const object = option.target;
object._calcDimensions();
object.setCoords();
canvas.renderAll();
}
function toggleDrawPolygon(event) {
if (drawMode) {
// stop draw mode
activeLine = null;
activeShape = null;
lineArray = [];
pointArray = [];
canvas.selection = true;
drawMode = false;
} else {
// start draw mode
canvas.selection = false;
drawMode = true;
}
}
function addPoint(options) {
const pointOption = {
id: new Date().getTime(),
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: options.e.layerX / canvas.getZoom(),
top: options.e.layerY / canvas.getZoom(),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
objectCaching: false,
};
const point = new fabric.Circle(pointOption);
if (pointArray.length === 0) {
// fill first point with red color
point.set({
fill: 'red'
});
}
const linePoints = [
options.e.layerX / canvas.getZoom(),
options.e.layerY / canvas.getZoom(),
options.e.layerX / canvas.getZoom(),
options.e.layerY / canvas.getZoom(),
];
const lineOption = {
strokeWidth: 2,
fill: '#999999',
stroke: '#999999',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
};
const line = new fabric.Line(linePoints, lineOption);
line.class = 'line';
if (activeShape) {
const pos = canvas.getPointer(options.e);
const points = activeShape.get('points');
points.push({
x: pos.x,
y: pos.y
});
const polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
} else {
const polyPoint = [{
x: options.e.layerX / canvas.getZoom(),
y: options.e.layerY / canvas.getZoom(),
}, ];
const polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false,
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(point);
lineArray.push(line);
canvas.add(line);
canvas.add(point);
}
function generatePolygon(pointArray) {
const points = [];
// collect points and remove them from canvas
for (const point of pointArray) {
points.push({
x: point.left,
y: point.top,
});
canvas.remove(point);
}
// remove lines from canvas
for (const line of lineArray) {
canvas.remove(line);
}
// remove selected Shape and Line
canvas.remove(activeShape).remove(activeLine);
// create polygon from collected points
const polygon = new fabric.Polygon(points, {
id: new Date().getTime(),
stroke: '#eee',
fill: '#f00',
objectCaching: false,
moveable: false,
//selectable: false
});
canvas.add(polygon);
toggleDrawPolygon();
editPolygon();
}
/**
* define a function that can locate the controls.
* this function will be used both for drawing and for interaction.
*/
function polygonPositionHandler(dim, finalMatrix, fabricObject) {
var x = (fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x),
y = (fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y);
return fabric.util.transformPoint(
{ x: x, y: y },
fabric.util.multiplyTransformMatrices(
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix()
)
);
}
/**
* define a function that will define what the control does
* this function will be called on every mouse move after a control has been
* clicked and is being dragged.
* The function receive as argument the mouse event, the current trasnform object
* and the current position in canvas coordinate
* transform.target is a reference to the current object being transformed,
*/
function actionHandler(eventData, transform, x, y) {
var polygon = transform.target,
currentControl = polygon.controls[polygon.__corner],
mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
polygonBaseSize = polygon._getNonTransformedDimensions(),
size = polygon._getTransformedDimensions(0, 0),
finalPointPosition = {
x: mouseLocalPosition.x * polygonBaseSize.x / size.x + polygon.pathOffset.x,
y: mouseLocalPosition.y * polygonBaseSize.y / size.y + polygon.pathOffset.y
};
polygon.points[currentControl.pointIndex] = finalPointPosition;
return true;
}
/**
* define a function that can keep the polygon in the same position when we change its
* width/height/top/left.
*/
function anchorWrapper(anchorIndex, fn) {
return function(eventData, transform, x, y) {
var fabricObject = transform.target,
absolutePoint = fabric.util.transformPoint({
x: (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x),
y: (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y),
}, fabricObject.calcTransformMatrix()),
actionPerformed = fn(eventData, transform, x, y),
newDim = fabricObject._setPositionDimensions({}),
polygonBaseSize = fabricObject._getNonTransformedDimensions(),
newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
return actionPerformed;
}
}
function editPolygon() {
let activeObject = canvas.getActiveObject();
if (!activeObject) {
activeObject = canvas.getObjects()[0];
canvas.setActiveObject(activeObject);
}
activeObject.edit = true;
activeObject.objectCaching = false;
const lastControl = activeObject.points.length - 1;
activeObject.cornerStyle = 'circle';
activeObject.controls = activeObject.points.reduce((acc, point, index) => {
acc['p' + index] = new fabric.Control({
positionHandler: polygonPositionHandler,
actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
actionName: 'modifyPolygon',
pointIndex: index,
});
return acc;
}, {});
activeObject.hasBorders = false;
canvas.requestRenderAll();
}
function resizePolygon() {
let activeObject = canvas.getActiveObject();
if (!activeObject) {
activeObject = canvas.getObjects()[0];
canvas.setActiveObject(activeObject);
}
activeObject.edit = false;
activeObject.objectCaching = false;
activeObject.controls = fabric.Object.prototype.controls;
activeObject.cornerStyle = 'rect';
activeObject.hasBorders = true;
canvas.requestRenderAll();
}
initCanvas();
<script src="https://unpkg.com/fabric#4.0.0-beta.12/dist/fabric.js"></script>
<button type="button" onclick="toggleDrawPolygon()">Draw Polygon</button>
<button type="button" onclick="editPolygon()">Edit Polygon</button>
<button type="button" onclick="resizePolygon()">Resize/Move Polygon</button>
<canvas id="c"></canvas>

Fabric js with gesture how to prevent zooming on touch devices

I'm using fabric js with gestures, but i want to prevent zooming, if zoom is getting more than 4x in and less than 1x out
I've tried to call preventDefault() and stopPropogation() functions on the event, but it doesn't stop the zooming
event.e.stopPropagation();
Get the deltaY & getZoom then you can limit the zoom as below.
var canvas = new fabric.Canvas('c');
var dia1 = new fabric.Circle({
radius: 12,
originX: 'center',
originY: 'center',
fill: 'transparent',
strokeWidth: 5,
stroke: "red",
});
var dia2 = new fabric.Circle({
radius: 5,
originX: 'center',
originY: 'center',
fill: 'red',
});
var targetEl = new fabric.Group([dia1, dia2], {
originX: 'center',
originY: 'center',
});
canvas.centerObject(targetEl);
canvas.add(targetEl);
canvas.renderAll();
//mouse zoom
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom - delta / 200;
// limit zoom to 4x in
if (zoom > 4) zoom = 4;
// limit zoom to 1x out
if (zoom < 1) {
zoom = 1;
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
}
canvas.zoomToPoint({
x: opt.e.offsetX,
y: opt.e.offsetY
}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
//touch zoom
canvas.on({
'touch:gesture': function(e) {
if (e.e.touches && e.e.touches.length == 2) {
pausePanning = true;
var point = new fabric.Point(e.self.x, e.self.y);
if (e.self.state == "start") {
zoomStartScale = canvas.getZoom();
}
var delta = zoomStartScale * e.self.scale;
canvas.zoomToPoint(point, delta);
pausePanning = false;
// limit zoom to 4x in
if (delta > 4) delta = 4;
// limit zoom to 1x out
if (delta < 1) {
delta = 1;
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
}
}
}
});
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>
<canvas id="c" width="600" height="300"></canvas>

Fabric js : fabric js drawing mode and selection mode options

I'm using fabric js to develop a application to draw a line, Rectangle and circle in one canvas.And there is a other function to drag and drop a image on to canvas.
But when i added drag and drop function to the application other three function are not working.
I have found this article. But i was unable to integrate it with my one.
function initCanvas() {
$('.canvas-container').each(function (index) {
var canvasContainer = $(this)[0];
var canvasObject = $("canvas", this)[0];
var url = $(this).data('floorplan');
var canvas = window._canvas = new fabric.Canvas(canvasObject);
canvas.setHeight(400);
canvas.setWidth(500);
canvas.setBackgroundImage(url, canvas.renderAll.bind(canvas));
var imageOffsetX, imageOffsetY;
function handleDragStart(e) {
[].forEach.call(images, function (img) {
img.classList.remove('img_dragging');
});
this.classList.add('img_dragging');
var imageOffset = $(this).offset();
imageOffsetX = e.clientX - imageOffset.left;
imageOffsetY = e.clientY - imageOffset.top;
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
function handleDrop(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
var img = document.querySelector('.furniture img.img_dragging');
console.log('event: ', e);
var offset = $(canvasObject).offset();
var y = e.clientY - (offset.top + imageOffsetY);
var x = e.clientX - (offset.left + imageOffsetX);
var newImage = new fabric.Image(img, {
width: img.width,
height: img.height,
left: x,
top: y
});
canvas.add(newImage);
return false;
}
function handleDragEnd(e) {
[].forEach.call(images, function (img) {
img.classList.remove('img_dragging');
});
}
var images = document.querySelectorAll('.furniture img');
[].forEach.call(images, function (img) {
img.addEventListener('dragstart', handleDragStart, false);
img.addEventListener('dragend', handleDragEnd, false);
});
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
});
}
initCanvas();
var canvas = new fabric.Canvas('canvas1', { selection: false });
var line, isDown;
function myFun() {
removeEvents();
canvas.on('mouse:down', function (o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
line = new fabric.Line(points, {
strokeWidth: 5,
fill: '#07ff11a3',
stroke: '#07ff11a3',
originX: 'center',
originY: 'center'
});
canvas.add(line);
});
canvas.on('mouse:move', function (o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
line.set({ x2: pointer.x, y2: pointer.y });
canvas.renderAll();
});
canvas.on('mouse:up', function (o) {
isDown = false;
});
canvas.selection = false;
}
function drawrec() {
var line, isDown, origX, origY;
removeEvents();
canvas.on('mouse:down', function (o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var pointer = canvas.getPointer(o.e);
line = new fabric.Rect({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
width: pointer.x - origX,
height: pointer.y - origY,
angle: 0,
fill: '#07ff11a3',
stroke: 'black',
transparentCorners: false
});
canvas.add(line);
});
canvas.on('mouse:move', function (o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
if (origX > pointer.x) {
line.set({ left: Math.abs(pointer.x) });
}
if (origY > pointer.y) {
line.set({ top: Math.abs(pointer.y) });
}
line.set({ width: Math.abs(origX - pointer.x) });
line.set({ height: Math.abs(origY - pointer.y) });
canvas.renderAll();
});
canvas.on('mouse:up', function (o) {
isDown = false;
});
}
function drawcle() {
var circle, isDown, origX, origY;
removeEvents();
canvas.on('mouse:down', function (o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: pointer.x,
top: pointer.y,
radius: 1,
strokeWidth: 1,
fill: '#07ff11a3',
stroke: 'black',
selectable: false,
originX: 'center', originY: 'center'
});
canvas.add(circle);
});
canvas.on('mouse:move', function (o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
circle.set({ radius: Math.abs(origX - pointer.x) });
canvas.renderAll();
});
canvas.on('mouse:up', function (o) {
isDown = false;
});
}
function removeEvents() {
canvas.off('mouse:down');
canvas.off('mouse:up');
canvas.off('mouse:move');
}
<div class="fullpage">
<div class="section">
<a class="thmb" href="#" onclick="myFun()" style="padding: 0px 10px;margin:5px;border: 2px solid;">line</a>
<a class="thmb" href="#" onclick="drawrec()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Rectangle</a>
<a class="thmb" href="#" onclick="drawcle()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Circle</a>
<div class="canvas-container">
<canvas id="canvas1" style="border: 1px solid;width: 500px;height: 500px"></canvas>
</div>
<div class="furniture" style="padding: 20px;border: 1px solid;width: 460px">
<h3>Drag the image to canvas</h3>
<img draggable="true" src="https://www.mve.com/media/Move_logo_-01.png" width="60">
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.6/fabric.min.js'></script>
var canvas = window._canvas = new fabric.Canvas(canvasObject);
inside initCanvas() you are again creating fabric canvas, so remove it. And to enable free drawing you need to do canvas.isDrawingMode = true
function initCanvas() {
$('.canvas-container').each(function(index) {
var canvasContainer = $(this)[0];
var canvasObject = $("canvas", this)[0];
var imageOffsetX, imageOffsetY;
function handleDragStart(e) {
[].forEach.call(images, function(img) {
img.classList.remove('img_dragging');
});
this.classList.add('img_dragging');
var imageOffset = $(this).offset();
imageOffsetX = e.clientX - imageOffset.left;
imageOffsetY = e.clientY - imageOffset.top;
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
function handleDrop(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
var img = document.querySelector('.furniture img.img_dragging');
console.log('event: ', e);
var offset = $(canvasObject).offset();
var y = e.clientY - (offset.top + imageOffsetY);
var x = e.clientX - (offset.left + imageOffsetX);
var newImage = new fabric.Image(img, {
width: img.width,
height: img.height,
left: x,
top: y
});
canvas.add(newImage);
return false;
}
function handleDragEnd(e) {
[].forEach.call(images, function(img) {
img.classList.remove('img_dragging');
});
}
var images = document.querySelectorAll('.furniture img');
[].forEach.call(images, function(img) {
img.addEventListener('dragstart', handleDragStart, false);
img.addEventListener('dragend', handleDragEnd, false);
});
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
});
}
initCanvas();
var canvas = new fabric.Canvas('canvas1', {
selection: false
});
var line, isDown;
function drawLine() {
removeEvents();
changeObjectSelection(false);
canvas.on('mouse:down', function(o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
line = new fabric.Line(points, {
strokeWidth: 5,
fill: '#07ff11a3',
stroke: '#07ff11a3',
originX: 'center',
originY: 'center',
selectable: false
});
canvas.add(line);
});
canvas.on('mouse:move', function(o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
line.set({
x2: pointer.x,
y2: pointer.y
});
canvas.renderAll();
});
canvas.on('mouse:up', function(o) {
isDown = false;
line.setCoords();
});
}
function drawrec() {
var rect, isDown, origX, origY;
removeEvents();
changeObjectSelection(false);
canvas.on('mouse:down', function(o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var pointer = canvas.getPointer(o.e);
rect = new fabric.Rect({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
width: pointer.x - origX,
height: pointer.y - origY,
angle: 0,
selectable:false,
fill: '#07ff11a3',
stroke: 'black',
transparentCorners: false
});
canvas.add(rect);
});
canvas.on('mouse:move', function(o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
if (origX > pointer.x) {
rect.set({
left: Math.abs(pointer.x)
});
}
if (origY > pointer.y) {
rect.set({
top: Math.abs(pointer.y)
});
}
rect.set({
width: Math.abs(origX - pointer.x)
});
rect.set({
height: Math.abs(origY - pointer.y)
});
canvas.renderAll();
});
canvas.on('mouse:up', function(o) {
isDown = false;
rect.setCoords();
});
}
function drawcle() {
var circle, isDown, origX, origY;
removeEvents();
changeObjectSelection(false);
canvas.on('mouse:down', function(o) {
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: pointer.x,
top: pointer.y,
radius: 1,
strokeWidth: 1,
fill: '#07ff11a3',
stroke: 'black',
selectable: false,
originX: 'center',
originY: 'center'
});
canvas.add(circle);
});
canvas.on('mouse:move', function(o) {
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
circle.set({
radius: Math.abs(origX - pointer.x)
});
canvas.renderAll();
});
canvas.on('mouse:up', function(o) {
isDown = false;
circle.setCoords();
});
}
function enableFreeDrawing(){
removeEvents();
canvas.isDrawingMode = true;
}
function enableSelection() {
removeEvents();
changeObjectSelection(true);
canvas.selection = true;
}
function changeObjectSelection(value) {
canvas.forEachObject(function (obj) {
obj.selectable = value;
});
canvas.renderAll();
}
function removeEvents() {
canvas.isDrawingMode = false;
canvas.selection = false;
canvas.off('mouse:down');
canvas.off('mouse:up');
canvas.off('mouse:move');
}
<div class="fullpage">
<div class="section">
<a class="thmb" href="#" onclick="drawLine()" style="padding: 0px 10px;margin:5px;border: 2px solid;">line</a>
<a class="thmb" href="#" onclick="drawrec()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Rectangle</a>
<a class="thmb" href="#" onclick="drawcle()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Circle</a>
<a class="thmb" href="#" onclick="enableFreeDrawing()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Drawing</a>
<a class="thmb" href="#" onclick="enableSelection()" style="padding: 0px 10px;margin:5px;border: 2px solid;">Selection</a>
<div class="canvas-container">
<canvas id="canvas1" style="border: 1px solid;width: 500px;height: 500px"></canvas>
</div>
<div class="furniture" style="padding: 20px;border: 1px solid;width: 460px">
<h3>Drag the image to canvas</h3>
<img draggable="true" src="https://www.mve.com/media/Move_logo_-01.png" width="60">
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.6/fabric.min.js'></script>
Perfect solution for this is here :
var canvas = this.__canvas = new fabric.Canvas('c', {
isDrawingMode: true
});
For more just visit this fabricjs page...
Fabric js : fabric js drawing mode and selection mode options

draw line with real-time measurement fabric.js

I'm having this problem in Fabric.Js when drawing line with measurement.
When I drag the line it must show the changing measurement at the end or in the middle of the line.
I can now measure the length of line but showing the measurement in error way. Thanks.
Heres the fiddle link: https://jsfiddle.net/ydtt94zm/
$(function(){
var line, isDown;
var arr = new Array();
var startx = new Array();
var endx = new Array();
var starty = new Array();
var endy = new Array();
var temp = 0;
var graphtype; trigger = "1";
var canvas = this.__canvas = new fabric.Canvas('canvas', { hoverCursor: 'pointer',selection: false});
fabric.Object.prototype.transparentCorners = false;
canvas.on('mouse:down', function(o){
if(trigger=="1"){
isDown = true;
var pointer = canvas.getPointer(o.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
startx[temp] = pointer.x;
starty[temp] = pointer.y;
line = new fabric.Line(points, {
strokeWidth: 2,
stroke: 'red',
originX: 'center',
originY: 'center'
});
canvas.add(line);
}else{
canvas.forEachObject(function(o){
o.setCoords();
});
}
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
line.set({ x2: pointer.x, y2: pointer.y });
endx[temp] = pointer.x;
endy[temp] = pointer.y;
if(trigger=="1"){
var px = Calculate.lineLength(startx[temp], starty[temp], endx[temp], endy[temp]).toFixed(2);
var text = new fabric.Text('Length ' + px, { left: endx[temp], top: endy[temp], fontSize: 12 });
canvas.add(text);
if(canvas.getActiveObject().get('type')==="text")
{
canvas.remove(text);
}
}
canvas.renderAll();
});
canvas.on('mouse:up', function(o){
var pointer = canvas.getPointer(o.e);
isDown = false;
});
canvas.on('mouse:over', function(e) {
e.target.setStroke('blue');
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
e.target.setStroke('red');
canvas.renderAll();
});
$("#selec").click(function(){
if(trigger == "1"){
trigger = "0";
}else{
trigger = "1";
}
});
var Calculate={
lineLength:function(x1, y1, x2,y2){//线长
//console.log(x1 + ", "+ y1 +", " + x2 + ", " + y2);
//clearRect(x2, y2);
return Math.sqrt(Math.pow(x2*1-x1*1, 2)+Math.pow(y2*1-y1*1, 2));
}
}
});
You need to remove the text element before re-adding it.
canvas.remove(text);
check this updated fiddler

Fabric.js - Free draw a rectangle

I have the following which doesn't work correctly:
var canvas = new fabric.Canvas('canvas');
canvas.observe('mouse:down', function(e) { mousedown(e); });
canvas.observe('mouse:move', function(e) { mousemove(e); });
canvas.observe('mouse:up', function(e) { mouseup(e); });
var started = false;
var x = 0;
var y = 0;
/* Mousedown */
function mousedown(e) {
var mouse = canvas.getPointer(e.memo.e);
started = true;
x = mouse.x;
y = mouse.y;
var square = new fabric.Rect({
width: 1,
height: 1,
left: mouse.x,
top: mouse.y,
fill: '#000'
});
canvas.add(square);
canvas.renderAll();
canvas.setActiveObject(square);
}
/* Mousemove */
function mousemove(e) {
if(!started) {
return false;
}
var mouse = canvas.getPointer(e.memo.e);
var x = Math.min(mouse.x, x),
y = Math.min(mouse.y, y),
w = Math.abs(mouse.x - x),
h = Math.abs(mouse.y - y);
if (!w || !h) {
return false;
}
var square = canvas.getActiveObject();
square.set('top', y).set('left', x).set('width', w).set('height', h);
canvas.renderAll();
}
/* Mouseup */
function mouseup(e) {
if(started) {
started = false;
}
}
The above logic is from a simple rectangle drawing system I used without fabric.js so I know it works, just not with fabric.js.
It seems the maths is off or I'm setting the incorrect params with the width/height/x/y values, as when you draw the rectangle does not follow the cursor correctly.
Any help is much appreciated, thanks in advance :)
I have written an example for you. Please follow the link below:
http://jsfiddle.net/a7mad24/aPLq5/
var canvas = new fabric.Canvas('canvas', { selection: false });
var rect, isDown, origX, origY;
canvas.on('mouse:down', function(o){
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var pointer = canvas.getPointer(o.e);
rect = new fabric.Rect({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
width: pointer.x-origX,
height: pointer.y-origY,
angle: 0,
fill: 'rgba(255,0,0,0.5)',
transparentCorners: false
});
canvas.add(rect);
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
if(origX>pointer.x){
rect.set({ left: Math.abs(pointer.x) });
}
if(origY>pointer.y){
rect.set({ top: Math.abs(pointer.y) });
}
rect.set({ width: Math.abs(origX - pointer.x) });
rect.set({ height: Math.abs(origY - pointer.y) });
canvas.renderAll();
});
canvas.on('mouse:up', function(o){
isDown = false;
});
Looks like Fabric.js calculates everything from the origin. So, 'Top' and 'Left' are a bit misleading. Check the following link: Canvas Coordinates Have Offset. Also, I've changed a bit of your code:
var canvas = new fabric.Canvas('canvas');
canvas.observe('mouse:down', function(e) { mousedown(e); });
canvas.observe('mouse:move', function(e) { mousemove(e); });
canvas.observe('mouse:up', function(e) { mouseup(e); });
var started = false;
var x = 0;
var y = 0;
/* Mousedown */
function mousedown(e) {
var mouse = canvas.getPointer(e.memo.e);
started = true;
x = mouse.x;
y = mouse.y;
var square = new fabric.Rect({
width: 0,
height: 0,
left: x,
top: y,
fill: '#000'
});
canvas.add(square);
canvas.renderAll();
canvas.setActiveObject(square);
}
/* Mousemove */
function mousemove(e) {
if(!started) {
return false;
}
var mouse = canvas.getPointer(e.memo.e);
var w = Math.abs(mouse.x - x),
h = Math.abs(mouse.y - y);
if (!w || !h) {
return false;
}
var square = canvas.getActiveObject();
square.set('width', w).set('height', h);
canvas.renderAll();
}
/* Mouseup */
function mouseup(e) {
if(started) {
started = false;
}
var square = canvas.getActiveObject();
canvas.add(square);
canvas.renderAll();
}
Here is the detail blog with jsfiddle - https://blog.thirdrocktechkno.com/drawing-a-square-or-rectangle-over-html5-canvas-using-fabricjs-f48beeedb4d3
var Rectangle = (function () {
function Rectangle(canvas) {
var inst=this;
this.canvas = canvas;
this.className= 'Rectangle';
this.isDrawing = false;
this.bindEvents();
}
Rectangle.prototype.bindEvents = function() {
var inst = this;
inst.canvas.on('mouse:down', function(o) {
inst.onMouseDown(o);
});
inst.canvas.on('mouse:move', function(o) {
inst.onMouseMove(o);
});
inst.canvas.on('mouse:up', function(o) {
inst.onMouseUp(o);
});
inst.canvas.on('object:moving', function(o) {
inst.disable();
})
}
Rectangle.prototype.onMouseUp = function (o) {
var inst = this;
inst.disable();
};
Rectangle.prototype.onMouseMove = function (o) {
var inst = this;
if(!inst.isEnable()){ return; }
var pointer = inst.canvas.getPointer(o.e);
var activeObj = inst.canvas.getActiveObject();
activeObj.stroke= 'red',
activeObj.strokeWidth= 5;
activeObj.fill = 'transparent';
if(origX > pointer.x){
activeObj.set({ left: Math.abs(pointer.x) });
}
if(origY > pointer.y){
activeObj.set({ top: Math.abs(pointer.y) });
}
activeObj.set({ width: Math.abs(origX - pointer.x) });
activeObj.set({ height: Math.abs(origY - pointer.y) });
activeObj.setCoords();
inst.canvas.renderAll();
};
Rectangle.prototype.onMouseDown = function (o) {
var inst = this;
inst.enable();
var pointer = inst.canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var rect = new fabric.Rect({
left: origX,
top: origY,
originX: 'left',
originY: 'top',
width: pointer.x-origX,
height: pointer.y-origY,
angle: 0,
transparentCorners: false,
hasBorders: false,
hasControls: false
});
inst.canvas.add(rect).setActiveObject(rect);
};
Rectangle.prototype.isEnable = function(){
return this.isDrawing;
}
Rectangle.prototype.enable = function(){
this.isDrawing = true;
}
Rectangle.prototype.disable = function(){
this.isDrawing = false;
}
return Rectangle;
}());
var canvas = new fabric.Canvas('canvas');
var rect = new Rectangle(canvas);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
Please draw rectangle here
<div id="canvasContainer">
<canvas id="canvas" width="400" height="400" style="border: solid 1px"></canvas>
</div>
The answer above seems to be deprecated. I changed some things on the code below to fix that. And on the mousedown function I add the if to detect the active object to avoid creating a new rectangle when the user's move a selected object.
var canvas = new fabric.Canvas('canvas');
canvas.on('mouse:down', function(options) {
if(canvas.getActiveObject()){
return false;
}
started = true;
x = options.e.clientX;
y = options.e.clientY;
var square = new fabric.Rect({
width: 0,
height: 0,
left: x,
top: y,
fill: '#000'
});
canvas.add(square);
canvas.setActiveObject(square);
});
canvas.on('mouse:move', function(options) {
if(!started) {
return false;
}
var w = Math.abs(options.e.clientX - x),
h = Math.abs(options.e.clientY - y);
if (!w || !h) {
return false;
}
var square = canvas.getActiveObject();
square.set('width', w).set('height', h);
});
canvas.on('mouse:up', function(options) {
if(started) {
started = false;
}
var square = canvas.getActiveObject();
canvas.add(square);
});
Slight modification on the mouse up event to allow the object to be selectable and moveable.
Note: adding the object to canvas again on the mouse:up even will add it twice to the canvase and it is the wrong thing to do.
canvas.on('mouse:up', function(o){
isDown = false;
var square = canvas.getActiveObject();
square.setCoords(); //allows object to be selectable and moveable
});
you can use this simple code
canvas.add(new fabric.Rect({ left: 110, top: 110, fill: '#f0f', width: 50, height: 50 }));

Categories