I'm using fabric js + typescript. I have more then 2 Objects and i want to set Space Evenly between those objects horizontally and Vertically . I can't find any solution.
I'm using fabric js + typescript. I have more then 2 Objects and i want to set Space Evenly between those objects horizontally and Vertically . I can't find any solution.
see this
fabric.Object.prototype.originX = "center";
fabric.Object.prototype.originY = "center";
var canvas = window._canvas = new fabric.Canvas('c');
var circle1 = new fabric.Circle({
radius: 30, fill: 'red', left: 50, top: 70, originX: "left", originY: "top"
});
var circle2 = new fabric.Circle({
radius: 40, fill: 'green', left: 200, top: 100, originX: "left", originY: "top"});
var circle3 = new fabric.Circle({
radius: 30, fill: 'blue', left: 300, top: 150, originX: "left", originY: "top"
});
canvas.add(circle1);
canvas.add(circle2);
canvas.add(circle3);
$('#distribute').click(function () {
var group = canvas.getActiveObject();
var items = canvas.getActiveObject()._objects,
newDim = getNewViewDimensions(group, 'horizontal');
group.set({
// width: newDim.width + (2 * 0),
// height: newDim.height + (items.length - 1) * 10 + (2 * 0)
});
for (var i = 0; i < items.length; i++) {
if (i == 0) {
items[i].set({
left: -group.width / 2 + 0,
top: -group.height / 2 + 0
});
} else {
items[i].set({
left: items[i - 1].left,
top: items[i - 1].top + items[i - 1].height + 10
});
}
}
canvas.renderAll();
});
function getNewViewDimensions(group, val) {
var objects = canvas.getActiveObject()._objects, minWidth = 0, minHeight = 0, w, h;
switch (val) {
case 'vertical':
for (i = 0; i < objects.length; i++) {
minWidth += objects[i].width * objects[i].scaleX;
h = objects[i].height * objects[i].scaleY;
if (h > minHeight) {
minHeight = h;
}
}
break;
case 'horizontal':
for (var i = 0; i < objects.length; i++) {
minHeight += objects[i].height * objects[i].scaleY;
w = objects[i].width * objects[i].scaleX;
if (w > minWidth) {
minWidth = w;
}
}
break;
}
return {
width: minWidth,
height: minHeight
}
}
canvas {
border: 1px solid #999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.5.0/fabric.min.js"></script>
<canvas id="c" width="400" height="300"></canvas>
<small> select all circles and hit the button </small>
<br>
<br>
<button id="distribute"> distribute </button>
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>
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 wrote a code. Combines point of click through on the screen.
HTML
<button id="end">End</button>
<button id="zoomIn">Zoom-In</button>
<button id="zoomOut">Zoom-Out</button>
<canvas id="c" width="500" height="500" ></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)
canvas.add(circle);
point1 = new fabric.Point(x, y);
if(line){
line = new fabric.Line([line.get('x2'), line.get('y2'), x, y], {
stroke: 'black',
hasControls: false,
hasBorders: false,
lockMovementX: false,
lockMovementY: false,
hoverCursor: 'default'
});
}else{
line = new fabric.Line([x, y, x, y], {
stroke: 'black',
hasControls: false,
hasBorders: false,
lockMovementX: false,
lockMovementY: 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([firstLine.get('x2'), firstLine.get('y2'), lastLine.get('x2'), lastLine.get('y2')], {
stroke: 'red',
hasControls: false,
hasBorders: false,
lockMovementX: false,
lockMovementY: false,
hoverCursor: 'default'
});
canvas.add(line);
bool = false;
});
});
$("#zoomIn").click(function() {
canvasScale = canvasScale * SCALE_FACTOR;
canvas.setHeight(canvas.getHeight() * SCALE_FACTOR);
canvas.setWidth(canvas.getWidth() * SCALE_FACTOR);
var objects = canvas.getObjects();
for (var i in objects) {
var scaleX = objects[i].scaleX;
var scaleY = objects[i].scaleY;
var left = objects[i].left;
var top = objects[i].top;
var tempScaleX = scaleX * SCALE_FACTOR;
var tempScaleY = scaleY * SCALE_FACTOR;
var tempLeft = left * SCALE_FACTOR;
var tempTop = top * SCALE_FACTOR;
objects[i].scaleX = tempScaleX;
objects[i].scaleY = tempScaleY;
objects[i].left = tempLeft;
objects[i].top = tempTop;
objects[i].setCoords();
}
canvas.renderAll();
});
$("#zoomOut").click(function() {
canvasScale = canvasScale / SCALE_FACTOR;
canvas.setHeight(canvas.getHeight() * (1 / SCALE_FACTOR));
canvas.setWidth(canvas.getWidth() * (1 / SCALE_FACTOR));
var objects = canvas.getObjects();
for (var i in objects) {
var scaleX = objects[i].scaleX;
var scaleY = objects[i].scaleY;
var left = objects[i].left;
var top = objects[i].top;
var tempScaleX = scaleX * (1 / SCALE_FACTOR);
var tempScaleY = scaleY * (1 / SCALE_FACTOR);
var tempLeft = left * (1 / SCALE_FACTOR);
var tempTop = top * (1 / SCALE_FACTOR);
objects[i].scaleX = tempScaleX;
objects[i].scaleY = tempScaleY;
objects[i].left = tempLeft;
objects[i].top = tempTop;
objects[i].setCoords();
}
canvas.renderAll();
});
But here the constant points.
I want to be like that here:
HTML
<canvas id="c" width="500" height="500" ></canvas>
JavaScript
(function() {
var canvas = this.__canvas = new fabric.Canvas('c', { selection: false });
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2, line3, line4) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 5,
radius: 12,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
c.line4 = line4;
return c;
}
function makeLine(coords) {
return new fabric.Line(coords, {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false
});
}
var line = makeLine([ 250, 125, 250, 175 ]),
line2 = makeLine([ 250, 175, 250, 250 ]),
line3 = makeLine([ 250, 250, 300, 350]),
line4 = makeLine([ 250, 250, 200, 350]),
line5 = makeLine([ 250, 175, 175, 225 ]),
line6 = makeLine([ 250, 175, 325, 225 ]);
canvas.add(line, line2, line3, line4, line5, line6);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), null, line),
makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
makeCircle(line3.get('x2'), line3.get('y2'), line3),
makeCircle(line4.get('x2'), line4.get('y2'), line4),
makeCircle(line5.get('x2'), line5.get('y2'), line5),
makeCircle(line6.get('x2'), line6.get('y2'), line6)
);
canvas.on('object:moving', function(e) {
var p = e.target;
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
canvas.renderAll();
});
})();
The point for which I should move be able to add points. So, I add points must act together with lines.
How can I integrate it above the code?
Zoom Problem
jsFiddle
Example If zoomIn or zoomOut becomes, my line is added corrupted
You need to adjust the coordinates of the saved line when you zoom in our out.
When zooming in
if (line) {
line.x2 = line.x2 * SCALE_FACTOR;
line.y2 = line.y2 * SCALE_FACTOR;
}
When zooming out
if (line) {
line.x2 = line.x2 / SCALE_FACTOR;
line.y2 = line.y2 / SCALE_FACTOR;
}
Fiddle - https://jsfiddle.net/65c3dxvv/
Solution this project--->
<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>
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 am trying to make an interactive map for the website. There are areas under the house, and I want to mark them with different colors. I add canvas, path, and canvasBackground.
var canvas = new fabric.Canvas('c');
var path = new fabric.Path('M 756 349 L 744 308 L 787 310 L 794 346 z');
path.set({
fill: 'red',
opacity: 0.5
});
canvas.add(path);
canvas.setBackgroundImage('phase3.jpg', canvas.renderAll.bind(canvas), {
width: canvas.width,
height: canvas.height,
originX: 'left',
originY: 'top'
});
<canvas id="c" width="1600" height="900"></canvas>
Then i need to fit to screen. I try to use canvas.setDimensions(), but its not work.
Demo
Thx for help. Sorry for bad english)
How about put
canvas.setDimensions({
width: body_w,
height: body_h
});
to just behind var canvas = new fabric.Canvas('c');?
var width = 1600;
var height = 900;
var scaleFactor = 1;
if(screen.width < 1281) {
scaleFactor = 0.8;
} else if (screen.width < 1361) {
scaleFactor = 0.85;
} else if (screen.width < 1441) {
scaleFactor = 0.9;
} else if (screen.width < 1601) {
scaleFactor = 1;
} else {
scaleFactor = 1.2;
}
width = width * scaleFactor;
height = height * scaleFactor;
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(width);
canvas.setHeight(height);
canvas.calcOffset();
if(scaleFactor != 1) {
for(var i=0; i<canvas._objects.length; i++){
canvas._objects[i].scale(scaleFactor);
canvas._objects[i].setLeft(canvas._objects[i].left * scaleFactor);
canvas._objects[i].setTop(canvas._objects[i].top * scaleFactor);
canvas._objects[i].setCoords();
}
canvas.renderAll();
} else {canvas.renderAll();}
I found this tip on stackoverflow, but lost link )