I have a canvas where I can click to place icons and link those icons with a line, but I can't manage to get the lines to follow when moving the icon objects. I tried the following, but I can't manage to lock the ends of the lines to the icon objects.
My effort so far: JSFIDDLE
canvas.on('mouse:move', function (obj) {
var line = canvas.getItemByName('line');
var objEl = canvas.getActiveObject();
var type = objEl.get('type');
var leftEl = objEl.left;
var topEl = objEl.top;
canvas.getObjects().forEach((obj) => {
var lineX1 = line.get('x1');
var lineY1 = line.get('y1');
var lineX2 = line.get('x2');
var lineY2 = line.get('y2');
if (lineX1 == leftEl && lineY1 == topEl) {
line.set({
x1: leftEl,
y1: topEl
});
canvas.renderAll();
};
});
line.set('opacity', 1);
});
With the help of this answer I was able to reference to any canvas object and lock the lines as I go.
var canvas = new fabric.Canvas('c');
fabric.Canvas.prototype.getItemByName = function(name) {
var object = null,
objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i].name && objects[i].name === name) {
object = objects[i];
break;
}
}
return object;
};
var currentColor;
var defaultIcon = {
width: 40,
height: 30,
originX: 'center',
originY: 'center',
hasControls: false,
};
var iconTriangle = new fabric.Triangle(defaultIcon);
setColor('green');
canvas.add(iconTriangle);
//disable icon & hide when hovering over existing icon
canvas.on('mouse:over', function(obj) {
iconTriangle.set('opacity', 0);
canvas.renderAll();
});
//restor icon & unhide
canvas.on('mouse:out', function(e) {
if (document.getElementById("on").checked == true) {
// if 'target' is null, means mouse is out of canvas
if (e.target) {
iconTriangle.set('opacity', 1);
} else {
iconTriangle.left = -100;
iconTriangle.top = -100;
}
canvas.renderAll();
};
});
//move pointer icon
canvas.on('mouse:move', function(obj) {
iconTriangle.top = obj.e.y - 100;
iconTriangle.left = obj.e.x - 10;
canvas.renderAll();
});
//count each by type and place new icon
canvas.on('mouse:up', function(e) {
if (e.target) {
return
}
var red = getObjectsBy((obj) => obj.fill === 'red').length;
var green = getObjectsBy((obj) => obj.fill === 'green').length;
var yellow = getObjectsBy((obj) => obj.fill === 'yellow').length;
document.getElementById("greentally").value = green;
document.getElementById("yellowtally").value = yellow;
document.getElementById("redtally").value = red;
addIcon(e.e.x - 10, e.e.y - 100, currentColor);
});
function setColor(color) {
currentColor = color;
iconTriangle.setColor(currentColor);
canvas.renderAll();
}
function getObjectsBy(fn) {
return canvas.getObjects().filter(fn)
}
function addIcon(x, y, color) {
var icon = new fabric.Triangle(defaultIcon);
if (document.getElementById("icon").checked == true) {
icon.setColor(color);
icon.left = x;
icon.top = y;
canvas.add(icon);
} else if (document.getElementById("link").checked == true) {
iconTriangle.set('opacity', 0);
icon = null;
};
canvas.renderAll();
}
//set icon type
$(".switch").click(function() {
if (document.getElementById("green").checked == true) {
setColor('green');
} else if (document.getElementById("yellow").checked == true) {
setColor('yellow');
} else if (document.getElementById("red").checked == true) {
setColor('red');
}
});
document.getElementById("icon").checked = true;
//link icons with line
$("input").change(function () {
mode = "draw";
canvas.on('mouse:up', function (o) {
isDown = true;
var icon = canvas.getItemByName('icon');
if (document.getElementById("link").checked == true) {
if (canvas.getActiveObjects().length == 1) {
mode = "draw";
var pointer = canvas.getActiveObject();
var points = [pointer.left, pointer.top, pointer.left, pointer.top];
if (mode == "draw") {
line1 = new fabric.Line(points, {
strokeWidth: 5,
fill: '#39ff14',
stroke: '#39ff14',
originX: 'center',
originY: 'center',
selectable: false,
hasControls: false,
hasBorders: false,
evented: false,
targetFindTolerance: true,
name: 'line1',
});
canvas.add(line1);
line1.sendToBack();
};
canvas.renderAll();
} else {
return;
};
};
});
if (document.getElementById("link").checked == true) {
canvas.on('mouse:move', function (o) {
line1.set('opacity', 0.4);
if (o.target !== null) {
var x2 = o.target.left;
var y2 = o.target.top;
if (mode == "draw") {
line1.set({
x2: x2,
y2: y2
});
canvas.renderAll();
}
} else {
var pointer = canvas.getPointer(o.e);
if (mode == "draw") {
line1.set({
x2: pointer.x,
y2: pointer.y
});
canvas.renderAll();
}
}
line1.set('opacity', 1);
});
canvas.on('mouse:down', function (e) {
if (document.getElementById("link").checked == true) {
canvas.selection = false;
var line1 = canvas.getItemByName('line1');
var objEl = e.target;
var type = objEl.get('type');
if (type !== 'text') {
canvas.remove(line1);
}
};
});
};
canvas.renderAll();
});
$("input").change(function () {
if (document.getElementById("link").checked == true) {
// we need this here because this is when the canvas gets initialized
['object:moving', 'object:scaling'].forEach(addChildMoveLine);
function addChildLine(options) {
if (document.getElementById("link").checked == true) {
canvas.off('object:selected', addChildLine);
// add the line
var fromObject = canvas.addChild.start;
var toObject = options.target;
var from = fromObject.getCenterPoint();
var to = toObject.getCenterPoint();
var line = new fabric.Line([from.x, from.y, to.x, to.y], {
strokeWidth: 5,
fill: '#39ff14',
stroke: '#39ff14',
originX: 'center',
originY: 'center',
selectable: false,
hasControls: false,
hasBorders: false,
evented: false,
targetFindTolerance: true,
name: 'line',
});
canvas.add(line);
// so that the line is behind the connected shapes
line.sendToBack();
// add a reference to the line to each object
fromObject.addChild = {
// this retains the existing arrays (if there were any)
from: (fromObject.addChild && fromObject.addChild.from) || [],
to: (fromObject.addChild && fromObject.addChild.to)
}
fromObject.addChild.from.push(line);
toObject.addChild = {
from: (toObject.addChild && toObject.addChild.from),
to: (toObject.addChild && toObject.addChild.to) || []
}
toObject.addChild.to.push(line);
// to remove line references when the line gets removed
line.addChildRemove = function () {
fromObject.addChild.from.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
toObject.addChild.to.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
}
canvas.discardActiveObject();
};
}
function addChildMoveLine(event) {
if (document.getElementById("link").checked == true) {
canvas.on(event, function (options) {
var object = options.target;
var objectCenter = object.getCenterPoint();
// udpate lines (if any)
if (object.addChild) {
if (object.addChild.from)
object.addChild.from.forEach(function (line) {
line.set({
'x1': objectCenter.x,
'y1': objectCenter.y
});
})
if (object.addChild.to)
object.addChild.to.forEach(function (line) {
line.set({
'x2': objectCenter.x,
'y2': objectCenter.y
});
})
}
canvas.renderAll();
});
};
}
function addChild(o) {
if (document.getElementById("link").checked == true) {
var line = canvas.getItemByName('line');
var objEl = canvas.getActiveObject();
var type = objEl.get('type');
canvas.addChild = {
start: canvas.getActiveObject()
}
// for when addChild is clicked twice
canvas.off('object:selected', addChildLine);
canvas.on('object:selected', addChildLine);
};
};
canvas.on('mouse:down', function (e) {
if (document.getElementById("link").checked == true) {
var line = canvas.getItemByName('line');
canvas.selection = false;
if (e.target !== null) {
addChild();
} else {
canvas.discardActiveObject();
}
canvas.on('mouse:over', function () {
canvas.discardActiveObject();
});
canvas.on('mouse:out', function () {
canvas.forEachObject(function (obj) {
if (obj.get('name') !== 'cursor') {
obj.set('opacity', 1);
}
});
});
};
});
};
});
canvas {
border: 1px solid #ccc;
}
.tally {
position: fixed;
width: 50px;
left: 255px;
}
<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.4.0/fabric.js"></script>
<b>PLACE:</b>
<input class="switch" name="iconOn" type="radio" id="icon" />
<b>ICON</b>
<input class="switch" name="iconOn" type="radio" id="link" />
<b>LINK</b>
<br>
<input class="switch" checked name="iconType" type="radio" id="green" />Green
<input class="tally" disabled id="greentally" type="text" value="0">
<br>
<input class="switch" name="iconType" type="radio" id="yellow" /> Yellow
<input class="tally" disabled id="yellowtally" type="text" value="0">
<br>
<input class="switch" name="iconType" type="radio" id="red" /> red
<input class="tally" disabled id="redtally" type="text" value="0">
<canvas id="c" width="600" height="300"></canvas>