Place node centred on rotated note - javascript

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>

Related

Draw a line automaticlly tied to circle on Konva JS

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();

How to fix FabricJS scaled polygon clipping offset

I'am trying to clip the FabricJS rect shape to the polygon shape. The clipping works okay until the polygon shape which need to be clipped is now scaled. After this there is some weird offset that is caused by the polygon clipping.
Can anyone help me how can i fix the function to prevent the polygon offset issue when clip object is scaled.
This is how it looks before scalling. The clipping works fine
Image => https://i.imgur.com/Eop2YJh.png
And then there is the problem when the polygon is scaled.
2: Image => https://i.imgur.com/ICkP8SG.png
Here is the code on fiddle with the clipping function
https://jsfiddle.net/0xpvc9uq/
So if there is anyone who knows whats the point and how can I fix it I would appriciate it.
Thx
var canvas = new fabric.Canvas('c');
var rect1 = new fabric.Rect({
left: 0, top: 0,
width: 900, height: 900,
fill: 'blue',
selectable: false,
clipTo: clipRegion,
scaleX: 1.5,
scaleY: 1.5
});
var clipPoly = new fabric.Polygon([
{ x: 180, y: 10 },
{ x: 300, y: 50 },
{ x: 300, y: 180 },
{ x: 180, y: 220 }
], {
originX: 'left',
originY: 'top',
left: 180,
top: 10,
fill: 'transparent', /* use transparent for no fill */
strokeWidth: 0,
selectable: false,
strokeWidth: 1,
stroke: "red",
scaleX: 1.3,
scaleY: 1.3
});
canvas.add(rect1, clipPoly);
function clipRegion (ctx) {
rect1.setCoords();
const clipObj = clipPoly;
const scaleXTo1 = (1 / rect1.scaleX);
const scaleYTo1 = (1 / rect1.scaleY);
ctx.save();
const ctxLeft = -( rect1.width / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
const ctxTop = -( rect1.height / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.rotate((rect1.angle * -1) * (Math.PI / 180));
ctx.beginPath();
const matrix = clipPoly.calcTransformMatrix();
let points = [];
clipObj.points.forEach( (point) => {
points.push({
x: ((point.x * matrix[0]) + (clipObj.strokeWidth * clipObj.scaleX)) - rect1.oCoords.tl.x,
y: ((point.y * matrix[3]) + (clipObj.strokeWidth * clipObj.scaleY)) - rect1.oCoords.tl.y
});
});
ctx.moveTo(points[0].x, points[0].y);
points.forEach((point) => {
ctx.lineTo(point.x, point.y);
});
ctx.lineTo(points[0].x, points[0].y);
ctx.closePath();
ctx.restore();
}
I discovered that there is also another approach that can be used and that solves all the problem.
The clipRegion function now looks like:
function clipRegion (ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clipPoly.render(ctx);
ctx.restore();
}
Which makes the rendering okay. If still anyone have other way to fix the above problem, I would like to see the answer

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.

KineticJS: Setting Shadow on shape causing error in tween

I am creating pie chart using multiple Kinetic.Wedges and a tween that moves the pieces out.
This works fine, but when I attempt to add
shadowColor: 'black',
shadowBlur: 2,
shadowOffset: 10,
shadowOpacity: 0.5
in the wedge instantiation it bugs out - the error "TypeError: b is undefined" comes up. If I comment out the tween instantiation the shape loads without error (with shadows) so I suspect something in tween instantiation is causing it.
Here is code for this purpose.
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200});
var splits = [{perc: 30, colour: "red"},
{perc: 15, colour: "blue"},
{perc: 20, colour: "green"},
{perc: 35, colour: "yellow"}];
var layer = new Kinetic.Layer();
var startPos = 0;
for (var i = 0; i < splits.length; i++){
(function(){
var wedge = new Kinetic.Wedge({
x: stage.getWidth() / 3,
y: stage.getHeight() / 2,
radius: 70,
angleDeg: toAngleDeg(splits[i].perc),
fill: splits[i].colour,
stroke: 'black',
strokeWidth: 1,
rotationDeg: startPos});
layer.add(wedge);
wedge.tween = new Kinetic.Tween({
node: wedge,
rotationDeg: 90 + (180 - (toAngleDeg(splits[i].perc)/2)),
x: stage.getWidth() / 1.5,
easing: Kinetic.Easings.EaseInOut});
startPos += toAngleDeg(splits[i].perc);
wedge.on('mouseup', function(){
wedge.setZIndex(100);
wedge.tween.play();}
);
})()}
stage.add(layer);
What am i doing wrong here?

Categories