I'm finding a potential bug in KonvaJS. Or I'm not sure about the capabilities...
1) Create a layer and add it to stage
2) Create a group and add it to layer
3) Create an Image node and add it to group (Note my images are loaded using Konva.Image.fromURL which waits for the image to load then adds it to the group.)
Result: Image does not appear.
But if you add the image to the group then add the group to the layer, the image appears. This is going to cause problems if I want to attach an image to a group dynamical if it just disappears.
I'm trying to create the concept of a tray or plate. Where the user can place items onto the plate. If the user drags the plate it creates a group with all the intersecting nodes and moves them all together. At drag end it releases all the objects back to the user.
EDIT: The problem I was experiencing had to do with group coordinates as I mentioned in my comment bellow.
"I think I misunderstood, for the longest time how positioning works with groups. Read the comments: jsfiddle.net/jusatx6s"
LL: Make sure you're checking the position of the nodes your are rendering and that they do appear on screen.
I have created a plunkr and followed steps which you have mentioned. Everything is working fine. Here's my code.
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// 1. created layer added it to stage.
var layer = new Konva.Layer();
stage.add(layer);
// 2. created group added it to layer
var group = new Konva.Group({
x: 120,
y: 40,
rotation: 20
});
layer.add(group);
var src = 'https://konvajs.github.io/assets/yoda.jpg';
// 3. Create an Image node and add it to group
Konva.Image.fromURL(src, function(yoda) {
console.log(yoda);
yoda.setAttrs({
x: 50,
y: 50,
width: 106,
height: 118
});
// 4. Add it to group.
group.add(yoda);
layer.batchDraw(); // It's required to draw changes.
});
Here's the plnkr to play around. Please let me know if I have missed anything.
Edited in light of OP's edit re confusion over co-ordinates.
Principles:
layers use the same co-ordinate system as the stage. So setting a shape position to {X: 10, y: 20} is at 10,20 in relation to the top left of the stage.
Adding a shape to a group is different, but you will not notice this if you do not set the group x & y position. For shapes that are added to the group, the shape positions are ADDED to the parent group positions.
Here is an example using a layer and a group. The gold rect is on the layer only. The group is illustrated by the green rect and has been set to position (60, 50). The red rect is a copy of the gold rect except the color. It has the same x & y position as red, BUT it is added to the group. And so its position (5, 5) is added to the group position (60, 50) to give its absolute position on the stage as (65, 55).
// add a stage
var s = new Konva.Stage({
container: 'container',
width: 400,
height: 400
});
// add a layer
var l = new Konva.Layer();
s.add(l);
// Add a gold rect to the LAYER
var gold = new Konva.Rect({stroke: 'gold', width: 40, height: 50, x: 5, y: 5});
l.add(gold);
// add a group
var g = new Konva.Group({x:60, y:50});
l.add(g);
// Add a green rect to the LAYER then add to the group
var green = new Konva.Rect({stroke: 'lime', width:100, height: 100, x: 0, y: 0});
g.add(green);
// Add a red rect, same apparent co-ords as red, but this time to the group
var red = new Konva.Rect({stroke: 'red', width: 40, height: 50, x: 5, y: 5});
g.add(red);
var abspos = red.getAbsolutePosition();
var pos = red.position();
console.log('Abs position of red is (' + abspos.x + ', ' + abspos.y + ') but its co-ords are ' + '(' + pos.x + ', ' + pos.y + ')');
l.draw(); // redraw the layer it all sits on.
#container {
border: 1px solid #ccc;
}
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.2/konva.min.js"></script>
<body>
<div id="container"></div>
</body>
Related
Posting this self-answer question for my own future reference and in case it helps anyone else passing this way.
I have a straight line with circles at each end to act as anchors for positioning the line. These shapes are in a group. I want the user to be able to click and drag a circle and have the line follow the movement of the circle.
The group is draggable and when I drag either circle the entire group is moved - what I want is to drag the circles independently of the group.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
}),
layer = new Konva.Layer({name: 'layer'}),
group = new Konva.Group({name: 'group', draggable: true}),
line = new Konva.Line({
name: 'the line',
x: 20,
y: 20,
points: [0, 0, 500, 200],
strokeWidth: 5,
stroke: 'cyan'
}),
circle1 = new Konva.Circle({
name: 'circle1',
x: 20,
y: 20,
radius: 10,
fill: 'magenta'
}),
circle2 = circle1.clone({name: 'circle2', x: 520, y: 220, fill: 'magenta'});
group.add(line, circle1, circle2)
stage.add(layer)
layer.add(group);
// What next ?
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#8.3.2/konva.min.js"></script>
<div id="container"></div>
Found the answer: Make the circles draggable, then use the position of the dragged circle to give the position to move the appropriate end of the line to.
When shapes are on a group, their position is relative to the group parent. I wondered if this would be a complication. However since the line and circles are all children of the same group parent, we can get the plain shape.position() call to find the position we need. shape.position() gets the position relative to the parent. For future reference, you would use getAbsolutePosition() to find the position in relation to the stage but you don't need that for this case because all shapes are on the same parent and therefore everything is relative to that parent.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
}),
layer = new Konva.Layer({name: 'layer'}),
group = new Konva.Group({name: 'group', draggable: true}),
line = new Konva.Line({
name: 'the line',
x: 20,
y: 20,
points: [0, 0, 500, 200],
strokeWidth: 5,
stroke: 'cyan'
}),
circle1 = new Konva.Circle({
name: 'circle1',
x: 20,
y: 20,
radius: 10,
fill: 'magenta',
draggable: true
}),
circle2 = circle1.clone({name: 'circle2', x: 520, y: 220, fill: 'magenta'});
group.add(line, circle1, circle2)
stage.add(layer)
layer.add(group);
circle1.on('dragmove', function(event){
const circle1Pos = circle1.getPosition(),
circle2Pos = circle2.getPosition();
line.position(circle1Pos);
line.points([0, 0, circle2Pos.x - circle1Pos.x, circle2Pos.y - circle1Pos.y])
})
circle2.on('dragmove', function(event){
const linePos = line.getPosition(),
circle2Pos = circle2.getPosition();
line.points([0, 0, circle2Pos.x - linePos.x, circle2Pos.y - linePos.y])
})
<script src="https://unpkg.com/konva#8.3.2/konva.min.js"></script>
<p>Click & drag circles to reposition the line. Drag line to move group.</p>
<div id="container"></div>
I am using Konva.js to render some colourful boxes.
I have a Stage with some set size and regions within that Stage, bound by points (e.g. rectangles, but they can be any polygons).
I then need to add some number of smaller rects inside this area such that they look nice and are uniformly distributed.
E.g.
// these numbers are an example only, irrelevant
let width = 500 * 1.936402653140851;
let height = 500;
// setup stage, boilerplate
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// region layer - boundaries
var regionLayer = new Konva.Layer({
scale: {x: width/4963, y: height/2563} // these numbers are an example only, irrelevant
});
stage.add(regionLayer);
// for this example, a single region, L-shaped on it's side
let box = new Konva.Line({
name: 'region-1',
points: [1067, 681,
3337, 681,
3337, 987,
3037, 987,
3037, 787,
1067, 787],
closed: true,
fill: 'blue',
opacity: 0.5
});
regionLayer.add(box);
regionLayer.draw();
// layer to draw rects in
let rectsLayer = new Konva.Layer({
scale: {x: width/4963, y: height/2563} // these numbers are an example only, irrelevant
});
for (let i = 0; i < 90; i++) { // e.g. some number up to X
let rect = new Konva.Rect({
width: 20,
height: 20,
fill: 'red',
x: 1067 + 40 * (i + 1),
y: 681 + 20
});
rectsLayer.add(rect);
}
stage.add(rectsLayer);
rectsLayer.draw();
The result of the above is something like this
What I'd like to have, is those boxes filling free area within the boundaries of my polygon only.
I read a whole heap about Delauney triangulation, Voronoi diagrams and sequencing for normal distribution to make it happen, but sadly I came up with zilch on practical approach of doing this.
https://jsfiddle.net/x4aksz1y/2/
I want to drag the entire group from the center red square, (see fiddle)
but restrict dragging from the outer rectangle, while maintaining the inner rectangle's position inside the group.
Possibly when mouse is down I want the crosshair cursor to be dead in the center of the inner red rectangle.
Any help would be appreciated, thanks.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
let layer = new Konva.Layer();
let group = new Konva.Group({
draggable: true,
height:50,
width: 50
});
const containerRect = new Konva.Rect({
width:50,
height: 50,
fill: 'yellow'
});
const rect = new Konva.Rect({
x: 15,
y: 15,
width: 20,
height: 20,
fill: 'red'
});
stage.add(layer);
group.add(containerRect);
group.add(rect);
layer.add(group);
layer.draw();
I suggest making the red rectangle draggable. Once you start a drag, you can stop it and start dragging of the group:
rect.on('dragstart', () => {
// stop rect dragging
rect.stopDrag();
// move group to the center
const size = group.getClientRect();
const pos = stage.getPointerPosition();
group.setAttrs({
x: pos.x - size.width / 2,
y: pos.y - size.height / 2,
});
group.startDrag();
});
https://jsfiddle.net/0cdw3ya7/21/
In KonvaJS how to make a text appear from 0 to 100% (fold out effect)
i want to create same effect as in below video, but with text
https://www.youtube.com/watch?v=HO6mco2MGH0
i tried giving width 0 to the node and then increased the width to 100% in the tween. But it is not giving me the desired effect. Text is appear character by character.
TIA
For that case, you can use group clipping.
const group = new Konva.Group({
clipX: 100,
clipY: 0,
clipWidth: 100,
clipHeight: 100
});
layer.add(group);
const circle = new Konva.Circle({
x: 0,
y: 50,
radius: 50,
fill: 'green'
});
group.add(circle);
circle.to({
x: 150,
duration: 1
});
Demo: http://jsbin.com/miketotuvo/1/edit?html,js,output
I have an app where I am panning and zooming multiple layers.
However, when I set the position of the layers they are being updated to the wrong values.
For instance, after calling:
layer.position({
x: 12,
y: 233,
});
I would expect
layer.getClientRect();
to return
{
x: 12,
y: 233,
}
Instead it returns
{
x: -22,
y: 220,
}
Is there any known reason this is happening?
I realised I wasn't taking into account all the offsets. The x position of a circle is set from the centre while getClientRect() returns the width and height including negatives. So getClientRect() on a circle with a radius of 50 and x of 0 will actually return {x: -25, y: -25, width: 50, height: 50}.
Seems to work for me as advertised in this cut down test. Did you move the stage or something ?
In the snippet below I draw a layer with 4 'corner' rects, display its value from grtClientRect() in 'Layer pos #1' which gives (0, 0), then move the stage to 12, 233 and display its value from grtClientRect() in 'Layer pos #2' which shows (12, 233). I move the layer back up to (12, 23) just for fun thereafter.
var stage = new Konva.Stage({
container: 'container',
width: 1600,
height: 400
});
// add a layer
var layer = new Konva.Layer();
stage.add(layer);
// add 4 corner rects - code I happened to have to hand
var rectCorner = [];
for (i=0;i<4;i=i+1){
rectCorner[i] = new Konva.Rect({
x: 0,
y: 0,
width: 50,
height: 50,
fill: 'red',
opacity: 0.5,
strokeWidth: 0})
layer.add(rectCorner[i]);
}
// put the rects in the corners to give a known layer space
rectCorner[1].position({x:500, y: 0});
rectCorner[2].position({x:500, y: 150});
rectCorner[3].position({x:0, y: 150});
layer.draw();
// get the client rect now
var p = layer.getClientRect();
$('#info1').html('Layer pos #1=' + p.x + ', ' + p.y);
// move the layer...
layer.position({
x: 12,
y: 233,
});
// get the client rect now
var p = layer.getClientRect();
$('#info2').html('Layer pos #2=' + p.x + ', ' + p.y);
// move the layer back to the top...
layer.position({
x: 12,
y: 23,
});
stage.draw();
p
{
padding: 4px;
}
#container
{
background-color: silver;
overflow: hidden;
}
<div id='info1'></div>
<div id='info2'></div>
<div id="container"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<script type="text/javascript" src="test.js"></script>