I would like to freedraw but only inside object i drawn before. Eg. i need to draw triangle using my code below, and then free draw lines but only inside this triangle, how to clip it so color wouldn't be outside of it?
var min = 99;
var max = 999999;
var polygonMode = true;
var pointArray = new Array();
var lineArray = new Array();
var activeLine;
var activeShape = false;
var canvas;
$(window).load(function() {
prototypefabric.initCanvas();
$('#create-polygon').click(function() {
prototypefabric.polygon.drawPolygon();
});
});
var prototypefabric = new function() {
this.initCanvas = function() {
canvas = window._canvas = new fabric.Canvas('c');
canvas.setWidth(800);
canvas.setHeight(800);
canvas.setBackgroundImage('https://i.imgur.com/tsnGll2.jpg', canvas.renderAll.bind(canvas));
//canvas.selection = false;
canvas.on('mouse:down', function(options) {
if (options.target && options.target.id == pointArray[0].id) {
prototypefabric.polygon.generatePolygon(pointArray);
}
if (polygonMode) {
prototypefabric.polygon.addPoint(options);
}
});
canvas.on('mouse:up', function(options) {
});
canvas.on('mouse:move', function(options) {
if (activeLine && activeLine.class == "line") {
var pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
var points = activeShape.get("points");
points[pointArray.length] = {
x: pointer.x,
y: pointer.y
};
activeShape.set({
points: points
});
canvas.renderAll();
}
canvas.renderAll();
});
};
};
prototypefabric.polygon = {
drawPolygon: function() {
polygonMode = true;
pointArray = new Array();
lineArray = new Array();
activeLine;
},
addPoint: function(options) {
var random = Math.floor(Math.random() * (max - min + 1)) + min;
var id = new Date().getTime() + random;
var circle = new fabric.Circle({
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',
id: id
});
if (pointArray.length == 0) {
circle.set({
fill: 'red'
})
}
var points = [(options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom()), (options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom())];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: '#999999',
stroke: 'green',
class: 'line',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
if (activeShape) {
var pos = canvas.getPointer(options.e);
var points = activeShape.get("points");
points.push({
x: pos.x,
y: pos.y
});
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
console.log(points);
} else {
var polyPoint = [{
x: (options.e.layerX / canvas.getZoom()),
y: (options.e.layerY / canvas.getZoom())
}];
var polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(circle);
lineArray.push(line);
canvas.add(line);
canvas.add(circle);
canvas.selection = false;
},
generatePolygon: function(pointArray) {
var points = new Array();
$.each(pointArray, function(index, point) {
points.push({
x: point.left,
y: point.top,
});
canvas.remove(point);
});
$.each(lineArray, function(index, line) {
canvas.remove(line);
});
canvas.remove(activeShape).remove(activeLine);
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 0.5,
fill: 'rgba(255, 255, 255, 0.5)',
opacity: 1,
hasBorders: false,
hasControls: false
});
canvas.add(polygon);
activeLine = null;
activeShape = null;
polygonMode = false;
canvas.selection = true;
}
};
// Removing active object on backspace
$(document).keydown(function(event) {
if (event.which == 8) {
if (canvas.getActiveObject()) {
canvas.getActiveObject().remove();
}
}
});
// end of Removing active object on backspace
// Freedrawing
function enableFreeDrawing() {
removeEvents();
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = e.target.value;
}
document.getElementById('colorpicker').addEventListener('click', function(e) {
canvas.freeDrawingBrush.color = e.target.value;
canvas.freeDrawingBrush.width = 2;
});
function removeEvents() {
canvas.isDrawingMode = false;
canvas.selection = false;
canvas.off('mouse:down');
canvas.off('mouse:up');
canvas.off('mouse:move');
}
// Manipulate canvas
$(function() {
$('#zoomIn').click(function() {
canvas.setZoom(canvas.getZoom() * 1.1);
});
$('#zoomOut').click(function() {
canvas.setZoom(canvas.getZoom() / 1.1);
});
$('#goRight').click(function() {
var units = 10;
var delta = new fabric.Point(units, 0);
canvas.relativePan(delta);
});
$('#goLeft').click(function() {
var units = 10;
var delta = new fabric.Point(-units, 0);
canvas.relativePan(delta);
});
$('#goUp').click(function() {
var units = 10;
var delta = new fabric.Point(0, -units);
canvas.relativePan(delta);
});
$('#goDown').click(function() {
var units = 10;
var delta = new fabric.Point(0, units);
canvas.relativePan(delta);
});
$('#zoomIn').click(function() {
canvas.setZoom(canvas.getZoom() * 1.1);
});
$('#zoomOut').click(function() {
canvas.setZoom(canvas.getZoom() / 1.1);
});
});
// End of manipulate canvas
canvas {
border: 1px solid black;
border-radius: 27px;
}
* {
font-family: "Roboto", sans-serif;
font-weight: 100;
}
body {
overflow: hidden;
}
/*#c {*/
/* border: 1px solid black;*/
/* background: url(assets/geoportal.JPG);*/
/* background-size: cover;*/
/* border-radius: 27px;*/
/*}*/
.canvas-container {
margin: 0 auto;
margin-top: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="c" width="800" height="800"></canvas>
<ul class="action-list">
<li>
<button class="thmb" href="#" id="create-polygon">Narysuj polygon</button>
</li>
<li>
<div id="colorpicker">
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#FFFF00">Yellow</button>
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#00FF00">Green</button>
<button class="thmb" href="#" onclick="enableFreeDrawing()" value="#F00000">Red</button>
<button class="thmb" href="#" onclick="removeEvents()">Stop Drawing</button>
</div>
</li>
<li>
<button id="goLeft" class="thmb"><</button>
<button id="goRight" class="thmb">></button>
<button id="goUp" class="thmb">Up</button>
<button id="goDown" class="thmb">Down</button>
<button id="zoomIn" class="thmb">+</button>
<button id="zoomOut" class="thmb">-</button>
</li>
</ul>
</div>
Link: https://codepen.io/Basstalion/pen/gOpKOjP
Related
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>
Has anyone ever tried to create a custom shape class for Fabric.js that is a combination of shapes? I want to create something like a ruler, where the left and right ends are vertical lines and is attached to the middle, |---| .
I'd also like to have a text box follow along in the center of this ruler. The ends should also have the ability to reposition.
I've done this without a class, but it won't save or load, since I made my version from multiple shapes.
Anyone have any thoughts if it's possible to create a custom shape like this?
Below is an example of some code that I pulled together from another example I found a while ago. 3 lines are created to make up my ruler. If I export the canvas data I'll get data to recreate as 3 lines, so if I clear the canvas and reload with this data, I'll get my 3 lines, but they will be independent 3 lines. I guess I need to figure out how to save their relationship so they can retain their ruler behavior when reloaded. I was thinking the best approach would be to create a custom shape with a type of "ruler".
window.onload = function() {
var canvas;
var started = false;
var mode = 0;
var lastX = 0;
var lastY = 0;
canvas = new fabric.Canvas('c');
canvas.isDrawingMode = false;
canvas.isTouchSupported = true;
canvas.selection = false;
canvas.on("mouse:down", function(option) {
if (mode === 1) {
var mouse = canvas.getPointer(option.e);
lastX = mouse.x;
lastY = mouse.y;
started = true;
var ln = new fabric.Line([lastX, lastY, lastX, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
var rAnchor = new fabric.Line([lastX - 20, lastY, lastX + 20, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
var tip = new fabric.Line([lastX - 20, lastY, lastX + 20, lastY], {
fill: 'red',
stroke: 'red',
strokeWidth: 4,
originX: 'center',
originY: 'center',
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasBorders: false,
hasControls: false,
perPixelTargetFind: true
});
tip.line = ln;
rAnchor.line = ln;
ln.tip = tip;
ln.rAnchor = rAnchor;
canvas.add(ln);
canvas.add(rAnchor);
canvas.add(tip);
canvas.setActiveObject(ln);
canvas.renderAll();
}
});
canvas.on("mouse:move", function(e) {
if (!started) {
return false;
}
var mouse = canvas.getPointer(e.e);
var line = canvas.getActiveObject();
line.set('x2', mouse.x).set('y2', mouse.y);
var tip = line.get('tip');
tip.set('left', mouse.x).set('top', mouse.y);
var rAnchor = line.get('rAnchor');
// Get the angle for pointing the tip at the mouse location.
var rad = Math.atan2((line.y1 - line.y2), (line.x1 - line.x2));
var ang = rad * (180 / Math.PI);
tip.set('angle', (ang - 90));
rAnchor.set('angle', (ang - 90));
line.setCoords();
tip.setCoords();
rAnchor.setCoords();
canvas.renderAll();
});
canvas.on("mouse:up", function() {
if (started) {
started = false;
}
canvas.discardActiveObject();
canvas.renderAll();
});
// Event Handler for Drawn Object move
canvas.on('object:moving', function(e) {
var p = e.target;
// move line
if (p.tip) {
var oldCenterX = (p.x1 + p.x2) / 2;
var oldCenterY = (p.y1 + p.y2) / 2;
var deltaX = p.left - oldCenterX;
var deltaY = p.top - oldCenterY;
p.tip.set({
'left': p.x2 + deltaX,
'top': p.y2 + deltaY
}).setCoords();
p.rAnchor.set({
'left': p.x1 + deltaX,
'top': p.y1 + deltaY
});
p.set({
'x1': p.x1 + deltaX,
'y1': p.y1 + deltaY
});
p.set({
'x2': p.x2 + deltaX,
'y2': p.y2 + deltaY
});
p.set({
'left': (p.x1 + p.x2) / 2,
'top': (p.y1 + p.y2) / 2
});
}
// move tip
if (p.line) {
var tip = p.line.tip;
var rAnchor = p.line.rAnchor;
p.line.set({
'x2': tip.left,
'y2': tip.top,
'x1': rAnchor.left,
'y1': rAnchor.top
});
// Get the angle for pointing the tip at the mouse location.
var rad = Math.atan2((p.line.y1 - p.line.y2), (p.line.x1 - p.line.x2));
var ang = rad * (180 / Math.PI);
tip.set('angle', (ang - 90));
rAnchor.set('angle', (ang - 90));
tip.setCoords();
rAnchor.setCoords();
p.line.setCoords();
}
canvas.renderAll();
});
// Button Event Handlers
document.getElementById("btnEdit").addEventListener("click", function(e) {
mode = 0;
});
document.getElementById("btnArrow").addEventListener("click", function(e) {
mode = 1;
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas style="border: 2px solid; " height="500" width="600" id="c"></canvas>
<button id="btnEdit">Edit</button>
<button id="btnArrow">Ruler</button>
I want to do something. Example, I will put a point when I click on the screen. After When I click somewhere else to put point there. Finally, automatic line to be drawn between these two points. How do I do this?
Thank you for your help:)
HTML
<canvas id="c" height="200" width="300"></canvas>
JS
var canvas = new fabric.Canvas('c');
var point1;
canvas.on('mouse:down', function (options) {
var x = options.e.clientX - canvas._offset.left;
var y = options.e.clientY - canvas._offset.top;
var circle = new fabric.Circle({
left: x,
top: y,
fill: 'red',
originX: 'center',
originY: 'center',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
radius: 5,
hoverCursor: 'default'
});
canvas.add(circle);
if (point1 === undefined) {
point1 = new fabric.Point(x, y)
} else {
canvas.add(new fabric.Line([point1.x, point1.y, x, y], {
stroke: 'blue',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
hoverCursor: 'default'
}))
point1 = undefined;
}
});
Fiddle - http://jsfiddle.net/zdaax418/
Solution;
HTML
<button id="end">End</button>
<button id="zoomIn">Zoom-In</button>
<button id="zoomOut">Zoom-Out</button>
<canvas id="c" width="1800" height="910" ></canvas>
JavaScript
var canvas = new fabric.Canvas('c');
var point1;
var line=null;
var canvasScale = 1;
var SCALE_FACTOR = 1.2;
var bool = true;
canvas.on('mouse:down', function (options) {
var x = options.e.clientX - canvas._offset.left;
var y = options.e.clientY - canvas._offset.top;
var circle = new fabric.Circle({
left: x,
top: y,
fill: 'red',
originX: 'center',
originY: 'center',
hasControls: false,
hasBorders: false,
lockMovementX: false,
lockMovementY: false,
radius: 5,
hoverCursor: 'default'
});
if(bool) {
circle.line1 = null;
circle.line2 = null;
canvas.add(circle);
}
point1 = new fabric.Point(x, y);
if(line){
line = new fabric.Line([line.get('x2'), line.get('y2'), x, y], {
fill: 'grey',
stroke: 'grey',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
selectable: false,
hoverCursor: 'default'
});
}else{
line = new fabric.Line([x, y, x, y], {
fill: 'grey',
stroke: 'grey',
hasControls: false,
hasBorders: false,
lockMovementX: true,
lockMovementY: true,
selectable: false,
hoverCursor: 'default'
});
}
if(bool)
canvas.add(line);
});
$("#end").click(function(){
var lines = canvas.getObjects('line');
var firstLine = lines[1];
var lastLine = lines[lines.length - 1];
line = new fabric.Line([lastLine.get('x2'), lastLine.get('y2'),firstLine.get('x1'), firstLine.get('y1')], {
fill: 'grey',
stroke: 'grey',
hasControls: false,
hasBorders: false,
lockMovementX: false,
lockMovementY: false,
selectable: false,
hoverCursor: 'default'
});
canvas.add(line);
var lines = canvas.getObjects('line');
var circles = canvas.getObjects('circle');
for(i = 1; i < circles.length - 1; i++) {
for(j = 0; j < lines.length; j++) {
if(circles[i].getLeft() == lines[j].get('x1') && circles[i].getTop() == lines[j].get('y1')) {
circles[i].line2 = lines[j];
circles[i].line1 = lines[j - 1];
}
}
}
circles[0].line1 = lines[lines.length - 1];
circles[0].line2 = lines[1];
circles[circles.length - 1].line2 = lines[lines.length - 1];
circles[circles.length - 1].line1 = lines[0];
bool = false;
});
$("#zoomIn").click(function() {
canvasScale = canvasScale * SCALE_FACTOR;
canvas.setHeight(canvas.getHeight() * SCALE_FACTOR);
canvas.setWidth(canvas.getWidth() * SCALE_FACTOR);
var circles = canvas.getObjects('circle');
for (var i in circles) {
var scaleX = circles[i].scaleX;
var scaleY = circles[i].scaleY;
var left = circles[i].left;
var top = circles[i].top;
circles[i].scaleX = scaleX * SCALE_FACTOR;
circles[i].scaleY = scaleY * SCALE_FACTOR;
circles[i].left = left * SCALE_FACTOR;
circles[i].top = top * SCALE_FACTOR;
circles[i].setCoords();
var coord = circles[i].getCenterPoint();
circles[i].line1.set({
'x2': coord.x,
'y2': coord.y
});
circles[i].line2.set({
'x1': coord.x,
'y1': coord.y
});
}
canvas.renderAll();
canvas.calcOffset();
});
$("#zoomOut").click(function() {
canvasScale = canvasScale / SCALE_FACTOR;
canvas.setHeight(canvas.getHeight() * (1 / SCALE_FACTOR));
canvas.setWidth(canvas.getWidth() * (1 / SCALE_FACTOR));
var circles = canvas.getObjects('circle');
for (var i in circles) {
var scaleX = circles[i].scaleX;
var scaleY = circles[i].scaleY;
var left = circles[i].left;
var top = circles[i].top;
circles[i].scaleX = scaleX * (1 / SCALE_FACTOR);
circles[i].scaleY = scaleY * (1 / SCALE_FACTOR);
circles[i].left = left * (1 / SCALE_FACTOR);
circles[i].top = top * (1 / SCALE_FACTOR);
circles[i].setCoords();
var coord = circles[i].getCenterPoint();
circles[i].line1.set({
'x2': coord.x,
'y2': coord.y
});
circles[i].line2.set({
'x1': coord.x,
'y1': coord.y
});
}
canvas.renderAll();
canvas.calcOffset();
});
canvas.on('object:moving', function(e) {
var p = e.target;
var coord = p.getCenterPoint();
p.line1.set({ 'x2': coord.x, 'y2': coord.y });
p.line2.set({ 'x1': coord.x, 'y1': coord.y });
canvas.renderAll();
});
I want to increase mobile drag and drop performance of canvas objects. I have a Group which contains different Shapes (Images, Stars, Recs,...). I want to use caching to increase the performance when I drag and drop or rotate the entire Group.
Here is a JSFiddle I created: http://jsfiddle.net/confile/k4Lsv73d/
function setFPSMeter() {
var RAF = (function () {
return window["requestAnimationFrame"] || window["webkitRequestAnimationFrame"] || window["mozRequestAnimationFrame"] || window["oRequestAnimationFrame"] || window["msRequestAnimationFrame"] || FRAF;
})();
function FRAF(callback) {
window.setTimeout(callback, 1000 / 60);
}
var fpsDiv = document.createElement("div");
fpsDiv.style.position = "absolute";
fpsDiv.style.zIndex="40000";
document.body.appendChild(fpsDiv);
var meter = new window.FPSMeter(fpsDiv, {
position: 'fixed',
zIndex: 10,
right: '5px',
top: '5px',
left: 'auto',
bottom: 'auto',
margin: '0 0 0 0',
interval: 1000,
graph: true,
history: 20,
theme: 'colorful',
heat: true
});
var tick = function () {
meter.tick();
RAF(tick);
};
tick();
}
setFPSMeter();
console.log(!!window.FPSMeter);
Kinetic.pixelRatio = 1;
//console.log(window.requestAnimationFrame);
// http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js
var width = $("#container").width();
var height = $("#container").height();
console.log("width: "+width+" height: "+height);
var stage = new Kinetic.Stage({
container: 'container',
width: width,
height: 400
});
var layer = new Kinetic.Layer();
// add the layer to the stage
stage.add(layer);
var blob;
var imageObj = new Image();
imageObj.onload = function () {
var group = new Kinetic.Group({
draggable: true
});
var star = new Kinetic.Star({
innerRadius: 50,
outerRadius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 5,
numPoints: 20,
x: 200,
y: 100,
shadowOffset: 5,
shadowColor: 'black',
shadowBlur: 5,
shadowOpacity: 0.5
});
blob = new Kinetic.Image({
image: imageObj,
x: 50,
y: 40,
// draggable: true,
width: 300,
height: 300
});
// performance
// blob.transformsEnabled('position');
group.add(blob);
group.add(star);
// setTimeout(function () {
// add the shape to the layer
//layer.add(blob);
layer.add(group);
// blob.cache();
layer.batchDraw();
// }, 50);
loadEvents();
/*
blob.cache({
width: 500,
height: 500,
drawBorder: true
}).offset({
x: 10,
y: 10
});
*/
/*
blob.cache({
drawBorder: true
});
*/
};
imageObj.src = "https://dl.dropboxusercontent.com/u/47067729/sandwich2.svg";
function getDistance(p1, p2) {
return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2));
}
function loadEvents() {
var lastDist = 0;
var startScale = 1;
stage.getContent().addEventListener('touchstart', function (evt) {
console.log("touchstart");
blob.clearCache();
blob.cache();
}, false);
stage.getContent().addEventListener('touchmove', function (evt) {
clearCache
var touch1 = evt.touches[0];
var touch2 = evt.touches[1];
if (touch1 && touch2) {
// blob.draggable(false);
var dist = getDistance({
x: touch1.clientX,
y: touch1.clientY
}, {
x: touch2.clientX,
y: touch2.clientY
});
if (lastDist == 0) {
lastDist = dist;
}
console.log("touchmove dist: "+dist);
console.log("touchmove lastDist: "+lastDist);
console.log("blob.getScale().x: "+blob.getScale().x);
// scale
var scale = blob.getScale().x * dist / lastDist;
console.log("scale: "+scale);
blob.setScale(scale);
lastDist = dist;
//// rotate
///
layer.batchDraw();
}
}, false);
stage.getContent().addEventListener('touchend', function (evt) {
console.log("touchend");
// blob.draggable(true);
lastDist = 0;
}, false);
}
How can I use caching on a Group of Shapes in KineticJS?
group.cache({
width: blob.width(),
height: blob.height(),
x : blob.x(),
y : blob.y(),
drawBorder: true
}).offset({
x: 10,
y: 10
});
http://jsfiddle.net/k4Lsv73d/2/
You should better config x, y, width and height attrs for caching.
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 }));