Reputation: 120
I'am trying to draw a line to connect two shape based on mouse move. And I konw how to relize it by using native canvas. But have no idea how to realize it by using KonvaJS. Please help me on this.
This image shows what result i what : enter image description here
And this is the code that i try to realize what I want. But it doesn't work.
stage.on('mousedown', function(e) {
const a = e.target instanceof Konva.Rect;
if (!a) {
return;
} else {
group.draggable(false);
group2.draggable(false);
clickdot1 = e.target;
drawingLine = true;
}
});
stage.on('mousemove', function(e) {
if (!drawingLine) {
return;
}else{
if(clickdot1!=null&&drawingLine){
let lastLine = new Konva.Line({
stroke: '#df4b26',
strokeWidth: 5,
lineCap: 'round',
lineJoin: 'round',
points: [e.target.x(), e.target.y()],
});
connections.push(lastLine);
drawthings();
}
}
});
function drawthings(){
for(let i = 0;i<connections.length;i++){
animLayer.add(connections[i]);
animLayer.batchDraw();
}
}
Upvotes: 1
Views: 5064
Reputation: 9545
It seems that your real question is how to check if there is a shape under the mouse during mouse move or mouse up operations.
Konva has a hit detection approach that I will let @lavarton explain. If you are dealing with pure rectangles - as opposed to for example circles - you can do your own hit testing using shape position and running some simple math checks. See my solution to this question about 'Selecting by drawing a box around objects in konva' which covers the same ground for hit testing and should show you a simple way forward.
The point about 'pure rectangles' concerns the fact that this approach works easily for non-rotated, rectangular shapes. However, rotated rectangles or non-rectangular shapes would require more work and if that were your use-case then Konva's built-in hit testing would offer a lower time-cost for learning and future support of your code.
Regarding @lavrton's answer missing the requirement to place the line into the centre position on the connected shape, change the stage.on('mouseup') listener in his code as below to achieve that.
stage.on('mouseup', (e) => {
if (!line) {
return;
}
if (!e.target.hasName('target')) {
line.destroy();
layer.draw();
line = null;
} else {
let pos = e.target.getClientRect();
const points = line.points().slice();
points[2] = pos.x + (e.target.width()/2);
points[3] = pos.y + (e.target.height()/2);;
line.points(points);
layer.batchDraw();
line = null;
}
});
This works by getting the topleft of the target shape (the getClientRect value), then adding half the shape width to the x and half the shape height to the y value to give the centre point. We than get the current line points array, set the values in slot 2 & 3 which are the end.x and end.y, give that back to the line and redraw the layer.
@lavrton should modify his example as above and be awarded the correct answer.
Upvotes: 0
Reputation: 20373
There are many ways to do that. The basic idea:
mousedown
on your source objectmousemove
eventmouseup
. If that is something "connectable" keep a line, otherwise destroy it.const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
layer.add(new Konva.Text({ text: 'try to drag a green source into any red target', padding: 10}))
const source = new Konva.Circle({
x: 20,
y: 50,
radius: 10,
fill: 'green'
});
layer.add(source);
const target1 = new Konva.Circle({
x: 20,
y: 220,
radius: 10,
fill: 'red',
name: 'target'
});
layer.add(target1);
const target2 = new Konva.Circle({
x: 120,
y: 220,
radius: 10,
fill: 'red',
name: 'target'
});
layer.add(target2);
let drawingLine = false;
let line;
source.on('mousedown', () => {
drawingLine = true;
const pos = stage.getPointerPosition();
line = new Konva.Line({
stroke: 'black',
// remove line from hit graph, so we can check intersections
listening: false,
points: [source.x(), source.y(), pos.x, pos.y]
});
layer.add(line);
});
stage.on('mouseover', (e) => {
if (e.target.hasName('target')) {
e.target.stroke('black');
layer.draw();
}
});
stage.on('mouseout', (e) => {
if (e.target.hasName('target')) {
e.target.stroke(null);
layer.draw();
}
});
stage.on('mousemove', (e) => {
if (!line) {
return;
}
const pos = stage.getPointerPosition();
const points = line.points().slice();
points[2] = pos.x;
points[3] = pos.y;
line.points(points);
layer.batchDraw();
});
stage.on('mouseup', (e) => {
if (!line) {
return;
}
if (!e.target.hasName('target')) {
line.destroy();
layer.draw();
line = null;
} else {
line = null;
}
});
layer.draw();
https://jsbin.com/rumizocise/1/edit?html,js,output
Upvotes: 3