Draw a line automaticlly tied to circle on Konva JS - javascript

i want to draw a line beetween a circle everytime i add a circle on konva js. and the circle is dragable. so i want the line follow the circle when i drag it. How to do it? or maybe i'm doing it wrong. Here my code https://jsfiddle.net/nutsi/2L7v6hy3/13/
var stage = new Konva.Stage({
height: window.innerHeight * 0.85,
width: window.innerWidth * 0.85,
container: "konva"
});
var layer = new Konva.Layer();
stage.on("click", function(){
var points = [];
var pos = this.getRelativePointerPosition();
var dot = new Konva.Circle({
x: pos.x,
y: pos.y,
fill: 'red',
radius: 5,
id: "seljal",
draggable: true
});
layer.add(dot)
var a = layer.find("#seljal")
for(let x = 0; x < a.length; x++){
points.push(a[x].attrs.x, a[x].attrs.y)
}
var line = new Konva.Line({
points: points,
stroke: "red",
strokeWidth: 2,
dash: [5, 5],
opacity: 1,
closed: !0,
id: "line"
})
var b = layer.find("#line")[0]
if(b) b.destroy();
layer.add(line);
})
stage.add(layer);
stage.draw();

Related

Place node centred on rotated note

I have following example stage.
const rectangle = new Konva.Rect({
width: 300,
height: 100,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140
});
layer.add(rectangle);
layer.draw();
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(tooltip);
layer.draw();
When the rectangle has no rotation I am able to place the tooltip correctly in the centre of the node. When the rectangle has some rotation the tooltip is not centred any more.
Without changing the attributes of the rectangle or rotating the tooltip, how can I place the tooltip in the centre of the rectangle node?
JSBIN example:
https://jsbin.com/wopoxuligi/edit?html,js,output
First, you need to update your declaration for Konva in the html script tag as you are referencing an old version.
<script src="https://unpkg.com/konva#7.0.3/konva.min.js"></script>
Because this shape is a rect you can call rectangle.getClientRect() to get the position on the stage, then use simple center-math to place your label. Slip this code in at the end of your current code where you do the last layer.draw();
const cliRect = rectangle.getClientRect();
let pos = {
x: cliRect.x + (cliRect.width )/2,
y: cliRect.y + (cliRect.height )/2
};
tooltip.x(pos.x)
tooltip.y(pos.y)
layer.draw();
This will leave the pointer of the label pointing at the centre of the rotated rectangle, with the label being non-rotated. Working snippet below - run it full screen.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
draggable: true
});
const layer = new Konva.Layer();
stage.add(layer);
const rectangle = new Konva.Rect({
width: 300,
height: 100,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140
});
layer.add(rectangle);
layer.draw();
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(tooltip);
const cliRect = rectangle.getClientRect();
// Extra red rect to illustrate client rect. Not needed in solution
const rectangle1 = new Konva.Rect({
width: cliRect.width,
height: cliRect.height,
x: cliRect.x,
y: cliRect.y,
stroke: 'red'
});
layer.add(rectangle1);
let pos = {
x: cliRect.x + (cliRect.width )/2,
y: cliRect.y + (cliRect.height )/2
};
// Set tooltip position taking account of stage draggind
tooltip.x(pos.x - stage.x())
tooltip.y(pos.y - stage.y())
console.log(pos)
layer.draw();
<script src="https://unpkg.com/konva#7.0.3/konva.min.js"></script>
<div id="container"></div>
EDIT: In a comment the OP pointed out that the real use case requires the stage to be draggable before the label is added and that after the drag the calc of the label position fails. This is because getClientRect() gives the position in the client container without adjustment for the stage movement. To make the code work with a dragged stage, change the two lines that set tooltip.x & y to adjust by the stage.x & y, as follows:
tooltip.x(pos.x - stage.x())
tooltip.y(pos.y - stage.y())
I added offset to rectangle, to rotate it by its center point and managed to have the tooltip centered.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
var group1 = new Konva.Group({
draggable: true
});
const rectWidth = 300;
const rectHeight = 100;
const rectangle = new Konva.Rect({
width: rectWidth,
height: rectHeight,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140,
offset: {
x: rectWidth / 2,
y: rectHeight / 2,
},
});
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
offset: {
x: rectangle.width() / 2,
y: rectangle.height() / 2,
},
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(rectangle);
layer.add(tooltip);
layer.draw();
<script src="https://unpkg.com/konva#^2/konva.min.js"></script>
<div id="container"></div>

Konva group X&Y different than shape X&Y

I am trying to edit another example that was posted on here but something doesn't seem right. I have a shape and then a couple of shapes that are grouped together with an arrow that is pointing in between the two shapes. As one of the shapes is dragged the arrow position should move as well.
The Problem
The problem is that the shape seems to be on a different coordinate system where its 0,0 point is in the upper left corner, whereas the groups coordinate system where its 0,0 point is right in the middle of the screen. What am I doing wrong?
Code
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Circle 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();
var group = new Konva.Group({
x:120,
y:120,
draggable: true,
});
var circle = new Konva.Circle({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 40,
fill: 'green',
stroke: 'black',
strokeWidth: 2,
});
var circleA = new Konva.Circle({
x: stage.getWidth() / 5,
y: stage.getHeight() / 5,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 2,
draggable: true
});
var arrow = new Konva.Arrow({
points: [circle.getX(), circle.getY(), circleA.getX(), circleA.getY()],
pointerLength: 10,
pointerWidth: 10,
fill: 'black',
stroke: 'black',
strokeWidth: 4
});
var star = new Konva.Star({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
numPoints: 5,
innerRadius: 30,
outerRadius: 50,
fill: '#89b717',
opacity: 0.8,
scale: {
x : 1.4,
y : 1.4
},
rotation: Math.random() * 180,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {
x : 5,
y : 5
},
shadowOpacity: 0.6,
});
//layer.add(star);
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
function adjustPoint(e){
var p=[circle.getX(), circle.getY(), circleA.getX(), circleA.getY()];
arrow.setPoints(p);
layer.draw();
console.log(group.getX(),group.getY());
console.log(circleA.getX(),circleA.getY());
}
//circle.on('dragmove', adjustPoint);
group.on('dragmove', adjustPoint);
circleA.on('dragmove', adjustPoint);
group.add(star,circle);
//group.add(circle);
layer.add(group);
layer.add(circleA);
// add the shape to the layer
//layer.add(circle);
layer.add(arrow);
//layer.add(star);
// add the layer to the stage
stage.add(layer);
</script>
</body>
</html>
When you put a shape on a group, it's getX() and getY() functions return values relative to the origin point of the group. Your error was to assume that the X, Y position of the circle in the group would change as the group is dragged.
In the working code below, based on your posted code, I changed only the first line of the AdjustPoint() function so that the X, Y position of the circle has added to it the X, Y position of the group.
This fixes your issue.
Tip: as you start using groups be aware that their extent on the page is that of the minimum rectangle needed to contain the group shapes. If you want to specifically control the size and location of a group, add to it a Rect() shape of specific width and height to give a known size to the group.
I also added a call to that function at the end of the code so that the arrow joins when the code initially runs.
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var group = new Konva.Group({
x:120,
y:10,
draggable: true,
});
var circle = new Konva.Circle({
x: stage.getWidth() / 2,
y: 60,
radius: 40,
fill: 'green',
stroke: 'black',
strokeWidth: 2,
});
var circleA = new Konva.Circle({
x: stage.getWidth() / 5,
y: stage.getHeight() / 5,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 2,
draggable: true
});
var arrow = new Konva.Arrow({
points: [circle.getX(), circle.getY(), circleA.getX(), circleA.getY()],
pointerLength: 10,
pointerWidth: 10,
fill: 'black',
stroke: 'black',
strokeWidth: 4
});
var star = new Konva.Star({
x: stage.getWidth() / 2,
y: 60,
numPoints: 5,
innerRadius: 30,
outerRadius: 50,
fill: '#89b717',
opacity: 0.8,
scale: {
x : 1.4,
y : 1.4
},
rotation: Math.random() * 180,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {
x : 5,
y : 5
},
shadowOpacity: 0.6,
});
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
function adjustPoint(e){
// changes here
var p=[ group.getX() + circle.getX(), group.getY() + circle.getY(), circleA.getX(), circleA.getY()];
// changes here
arrow.setPoints(p);
layer.draw();
stage.draw();
// console.log('group = ' + group.getX() + ', ' + group.getY());
// console.log('red circle = ' + circleA.getX() + ', ' + circleA.getY());
}
//circle.on('dragmove', adjustPoint);
group.on('dragmove', adjustPoint);
circleA.on('dragmove', adjustPoint);
group.add(star,circle);
//group.add(circle);
layer.add(group);
layer.add(circleA);
// add the shape to the layer
//layer.add(circle);
layer.add(arrow);
//layer.add(star);
// add the layer to the stage
stage.add(layer);
// changes here
adjustPoint();
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
<body>
<div id="container"></div>
</body>

How to hide one element from KONVA group

I have a KONVA group with three object in it. I want to show/hide one object of the group.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const group = new Konva.Group();
layer.add(group);
const circle1 = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 50,
fill: 'green',
visible: true
});
const circle2 = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 30,
fill: 'red',
visible: true
});
const circle3 = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 10,
fill: 'blue',
visible: true
});
group.add(circle1);
group.add(circle2);
group.add(circle3);
layer.draw();
//group.hide(); // if I use this it will hide entire group but i want to hide only one object
layer.draw();
I want to show/hide only the circle2 from the Konva group. can any one please help me?
just call hide method of circle2.
circle2.hide();
This circle2 is added in your group by reference. So if you make any change in circle2, it will reflect in group.

Get X and Y of a shape after dragging in Konva JS

I'm using Konva JS in my project. I'm adding a shape on a button click which is draggable. On click of the shape i need to get X and Y positions of the shape. getX and getY functions are returning the original X and Y. How can I update the X and Y after dragging.
Example code below.
var stage = new Konva.Stage({
container: 'canvas', // id of container <div>
width: 500,
height:300
});
jQuery("#add-shape").click(function(){
addShape();
});
var addShape = function(){
console.log("add shape");
var layer = new Konva.Layer();
var parentContainer = new Konva.Rect({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
width: 200,
height: 60,
cornerRadius: 10,
fill: '#ccc'
});
var smallCircle = new Konva.Circle({
x: stage.getWidth() / 2 + 200,
y: stage.getHeight() / 2 + 30,
radius: 15,
fill: "#F2784B",
stroke: "white",
strokeWidth: 2
});
smallCircle.on('click', function() {
console.log(this.getX(),this.getY());
addArrow(this.getX(),this.getY());
//get current X and Y here instead of origina X and Y
});
layer.add(parentContainer);
layer.add(smallCircle);
layer.draggable('true');
stage.add(layer);
}
var addArrow = function(arrowX,arrowY){
var connectorLayer = new Konva.Layer();
var connector = new Konva.Arrow({
points: [arrowX,arrowY,arrowX+10,arrowY],
pointerLength: 4,
pointerWidth: 4,
fill: 'black',
stroke: 'black',
strokeWidth: 2
});
connectorLayer.add(connector);
stage.add(connectorLayer);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.0.2/konva.min.js"></script>
<button id="add-shape">
Add shape
</button>
<div id="canvas">
</div>
If you need to get a mouse position you can use:
smallCircle.on('click', function() {
console.log(stage.getPointerPosition());
});
box.on('mouseout', function () {
document.body.style.cursor = 'default';
console.log(box.attrs.x);
console.log(box.attrs.y);
});
I don't know if this is what you're looking for and it's too late but i'll post it anyway for future developers..
Lets say this is my watermark image inside the layer and bluh bluh and i want it's position getX() and getY(): I use the group like this:
First the regular stuff to add the image:
function update(activeAnchor) {
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('Image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case 'topLeft':
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
bottomLeft.setY(anchorY);
topRight.setX(anchorX);
break;
case 'bottomLeft':
bottomRight.setY(anchorY);
topLeft.setX(anchorX);
break;
}
image.position(topLeft.position());
var width = topRight.getX() - topLeft.getX();
var height = bottomLeft.getY() - topLeft.getY();
if (width && height) {
image.width(width);
image.height(height);
}
}
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
var anchor = new Konva.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
anchor.on('dragmove', function () {
update(this);
layer.draw();
});
anchor.on('mousedown touchstart', function () {
group.setDraggable(false);
this.moveToTop();
});
anchor.on('dragend', function () {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function () {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function () {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
var stage = new Konva.Stage({
container: 'container',
width: 595,
height: 842
});
var layer = new Konva.Layer();
stage.add(layer);
//WaterMark
var WaterMarkImg = new Konva.Image({
width: 595,
height: 842
});
var WaterMarkGroup = new Konva.Group({
x: 0,
y: 0,
draggable: true
});
layer.add(WaterMarkGroup);
WaterMarkGroup.add(WaterMarkImg);
addAnchor(WaterMarkGroup, 0, 0, 'topLeft');
addAnchor(WaterMarkGroup, 595, 0, 'topRight');
addAnchor(WaterMarkGroup, 595, 842, 'bottomRight');
addAnchor(WaterMarkGroup, 0, 842, 'bottomLeft');
var imageObj1 = new Image();
imageObj1.onload = function () {
WaterMarkImg.image(imageObj1);
layer.draw();
};
imageObj1.src = "some image Url";
and this is simply the getX(): very simple
var x = WaterMarkGroup.getX();
alert(x);
I hope this helps anyone looking for it.
This is not the exact answer for the question. point is the draggable shape.
point.on('dragmove', (t) => {
console.log("dragmove", t.target.x(), t.target.y());
});
use shape.getAttr("x") and shape.getAttr("y"), following is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!--mobile friendly-->
<meta name="viewport" content="width=device-width, user-scalable=yes">
</head>
<body>
<div id="c"></div>
<script type="module">
import "../../node_modules/konva/konva.js"
var stage = new Konva.Stage({
container: "#c",
width: 500,
height: 500
})
var layer = new Konva.Layer()
stage.add(layer)
var c = (x, y) => {
return new Konva.Circle({
x: x,
y: y,
stroke: "lightblue",
strokeWidth: 2,
radius: 10,
draggable: true
})
}
let c1 = c(50, 50)
layer.add(c1)
var c2 = c(100, 50)
layer.add(c2)
c1.on("dragmove", () => {
c2.setAttr("y", c1.getAttr("y"))
})
layer.draw()
</script>
</body>
</html>

Kinetic.js Text gets drawn under shape with fill value set sometimes

In this fiddle: http://jsfiddle.net/m1erickson/fu5LP/
A group of wedges are drawn with text in the middle to produce the above image on the canvas.
When you set the fill value of the wedge object however, the output is rather odd:
Some text values are being drawn under the wedge's fill, I have no idea why.
The code for the fiddle is here:
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
var cx = 175;
var cy = 175;
var wedgeRadius = 140;
var accumAngle = 0;
var center = new Kinetic.Circle({
x: cx,
y: cy,
radius: 5,
fill: 'red'
});
layer.add(center);
for (var i = 0; i < 12; i++) {
newTextWedge(30, "Element # " + i);
}
function newTextWedge(angle, text) {
var wedge = new Kinetic.Wedge({
fill : 'black',
x: cx,
y: cy,
//If I add this fill the above output of hiding text occurs
//fill: 'black',
radius: wedgeRadius,
angleDeg: angle,
stroke: 'gray',
strokeWidth: 1,
rotationDeg: -accumAngle + angle / 2
});
layer.add(wedge);
if (accumAngle > 90 && accumAngle < 270) {
var offset = {
x: wedgeRadius - 10,
y: 7
};
var textAngle = accumAngle - 180;
} else {
var offset = {
x: -50,
y: 7
};
var textAngle = accumAngle;
}
var text = new Kinetic.Text({
x: cx,
y: cy,
text: text,
fill: 'red',
offset: offset,
rotationDeg: textAngle
});
layer.add(text);
layer.draw();
accumAngle += angle;
}
Anyone able to give any insight as to why this happens?
Debugging and watching the chart created step by step shows what is happening. The wedge layer is rendered and then the text layer is rendered. The next wedge is rendered on top of that last text layer.
Here is a fork of the fiddle:
http://jsfiddle.net/smurphy/UvdWt/
Kinetic Shape has a method moveToBottom() that you can call on the filled wedge that will force it to the bottom of the stack. See documentation
layer.add(wedge);
wedge.moveToBottom();

Categories