Connect objects HTML5 canvas with Konva

Get last news, demos, posts from Konva

How to connect two objects with a line or arrow?

Konva can’t connect two objects with a line and update its position automatically. You have to update a line manually as you need it. Usually we need to update line position when a user drag one of the connected objects. In simple cases in can be that:

const obj1 = new Konva.Circle({ ...obj1Props })
const obj2= new Konva.Circle({ ...obj2Props });

const line = new Konva.Line({ ...lineProps });

obj1.on('dragmove', updateLine);
obj2.on('dragmove', updateLine);

function updateLine() {
line.points([obj1.x(), obj1.y(), obj2.x(), obj2.y]);
layer.batchDraw();
}

But in that demo I will create a more complex case with the state of the app and many connected objects.

Instructions: try to drag a circle.

Konva 20000 Nodes Demoview raw
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/[email protected]/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Connect Objects demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>

<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});

var layer = new Konva.Layer();
stage.add(layer);

// function to generate a list of "targets" (circles)
function generateTargets() {
var number = 10;
var result = [];
while (result.length < number) {
result.push({
id: 'target-' + result.length,
x: stage.width() * Math.random(),
y: stage.height() * Math.random()
});
}
return result;
}

var targets = generateTargets();

// function to generate arrows between targets
function generateConnectors() {
var number = 10;
var result = [];
while (result.length < number) {
var from = 'target-' + Math.floor(Math.random() * targets.length);
var to = 'target-' + Math.floor(Math.random() * targets.length);
if (from === to) {
continue;
}
result.push({
id: 'connector-' + result.length,
from: from,
to: to
});
}
return result;
}

function getConnectorPoints(from, to) {
const dx = to.x - from.x;
const dy = to.y - from.y;
let angle = Math.atan2(-dy, dx);

const radius = 50;

return [
from.x + -radius * Math.cos(angle + Math.PI),
from.y + radius * Math.sin(angle + Math.PI),
to.x + -radius * Math.cos(angle),
to.y + radius * Math.sin(angle)
];
}

var connectors = generateConnectors();

// update all objects on the canvas from the state of the app
function updateObjects() {
targets.forEach(target => {
var node = layer.findOne('#' + target.id);
node.x(target.x);
node.y(target.y);
});
connectors.forEach(connect => {
var line = layer.findOne('#' + connect.id);
var fromNode = layer.findOne('#' + connect.from);
var toNode = layer.findOne('#' + connect.to);

const points = getConnectorPoints(
fromNode.position(),
toNode.position()
);
line.points(points);
});
layer.batchDraw();
}

// generate nodes for the app
connectors.forEach(connect => {
var line = new Konva.Arrow({
stroke: 'black',
id: connect.id,
fill: 'black'
});
layer.add(line);
});

targets.forEach(target => {
var node = new Konva.Circle({
id: target.id,
fill: Konva.Util.getRandomColor(),
radius: 20 + Math.random() * 20,
shadowBlur: 10,
draggable: true
});
layer.add(node);

node.on('dragmove', () => {
// mutate the state
target.x = node.x();
target.y = node.y();

// update nodes from the new state
updateObjects();
});
});

updateObjects();
</script>
</body>
</html>
Next