kinetic js group position is not correct when dragging - javascript

I am trying to create group object that include text,image .
I want to get right position(x,y) when dragging the group object.
Now i get minus value {x:-26,y:-18} like that.
var text2=new Kinetic.Text({
x: 40,
y: 125,
text: 'Time:4:20',
fontFamily: 'Calibri',
fontSize: 12,
padding: 5,
fill: 'red',
draggable: false
});
var group = new Kinetic.Group({
width:94,
height:45,
draggable: true
});
group.on('dragend', function() {
alert("X:"+group.getAbsolutePosition().x+"Y:"+group.getAbsolutePosition().y);
});
group.add(text2);
layer.add(group);
stage.add(layer);

Your code is working normally.
You have a non-draggable text on the draggable group.
This causes the text to act as a "handle" to drag whole group.
(the text does not move relative to the group. Instead the whole group is moved by dragging the text).
So if you drag the text up and left a bit the group will be pulled up and left a bit.
Therefore the group reports negative coordinates.

Related

Text's `fill` color is overriden by `stroke` in Konva

I have some Konva Text node, whose fill attribute is set to black and stroke is set to some other color.
In my canvas it looks as expected. The problem happens when I convert the canvas to base64 image to download it as an image file - in that case the downloaded image looks like in the screenshot below:
(the canvas is on the left side, the right one is the downloaded image)
As you can see, the stroke color is applied to the text's own color as well.
I created a Codesandbox example with a Text node that has the same attributes as in my environment, here is how that node is created:
const text = new Konva.Text({
x: 26.330053300533024,
y: 128,
text: "Add a body text",
defaultText: "Add a body text",
fontSize: 22,
fontFamily: "Montserrat",
draggable: true,
name: "text",
fontStyle: "300",
id: "textkgrsl68w",
is_settings: true,
wrap: "word",
padding: 5,
fill: "black",
opacity: 1,
isPremium: false,
width: 183.5,
visible: true,
rotation: 0,
scaleX: 1,
scaleY: 1,
offsetX: 0,
offsetY: 0,
skewX: 0,
skewY: 0,
stroke: "rgba(255,0,0,1)",
strokeWidth: 3.75
});
As you can see, it has the same (incorrect) look - without the inline color. I played with fontSize in Codesandbox and when I set it to very big value (like 100), the text's inline color became visible.
I assumed it can be some ratio problem, but as we see on the screenshot, the text's size is similar in both canvas and downloaded image.
How can this be fixed, maybe there is some extra attribute that should also be set?
konva: 7.1.4
react-konva: 16.12.0-0
UPDATED:
You can use text.fillAfterStrokeEnabled(true); to draw fill over stroke.
Old answer:
By default Konva is drawing fill first then stroke. So it will be a text itself and then stroke around it (that goes overfill part).
Probably one-day Konva will support a different order, but at the current moment konva#7.1.4 can't do that.
As a workaround just use two Konva.Text instances. First for stroke, second for fill.
https://codesandbox.io/s/download-konva-text-node-with-stroke-as-base-64-image-forked-w0z4v

Adding transformer to dynamically generated shapes?

I have the button which add new group which have square, to layer when clicked very simple code I guess no need to post. But my question is that how can I add transformer to it when on clicked?, I have done it with this mouseleave and mouseenter functions.
group.on('mouseenter', () => {
transformer.borderEnabled(true);
transformer.resizeEnabled(true);
layer.draw();
});
group.on('mouseleave', () => {
transformer.borderEnabled(false);
transformer.resizeEnabled(false);
layer.draw();
});
It is in loop which creates new group named "group", It works fine but in circle when I hover it the transformer appears but then when I go to transformer's boxes to resize it consider as it is mouseleave but this is doing only in circle not in line text.
So can I have solution for active transformer on element which is clicked or for considering hover on transformer boxes as a hover on node? Thanks
The mouseleave() will always fire because the pointer must leave the group to use the transformer handles or spinner.
An alternative approach would be
click to enable the transformer,
leave the transformer in place even when the mouse moves away
wait for a click on some other shape to know you can hide the transformer.
That is the standard GUI approach I believe.
If you need to show hover focus then stick a transparent rectangle the size of the groups clientrect into the group and change its stroke from transparent to some colour in the mouseenter and back in the mouseleave. You will also maybe want to set the rect.listening to false so as it coes not interfere with mouse events on the shapes in the group, but then again it might help in dragging.
Demo below.
// Set up the canvas and shapes
let stage = new Konva.Stage({container: 'container1', width: 300, height: 200});
let layer = new Konva.Layer({draggable: false});
stage.add(layer);
// Add a transformer.
let transFormer1 = new Konva.Transformer();
layer.add(transFormer1);
// Create a sample group
let group1 = new Konva.Group();
layer.add(group1);
group1.add(new Konva.Circle({x: 20, y: 30, radius: 15, fill: 'magenta', stroke: 'black'}))
group1.add(new Konva.Circle({x: 60, y: 40, radius: 15, fill: 'magenta', stroke: 'black'}))
group1.add(new Konva.Rect({x: 90, y: 60, width: 25, height: 25, fill: 'magenta', stroke: 'black'}));
let pos = group1.getClientRect();
let boundRect1 = new Konva.Rect({name: 'boundRect', x: pos.x, y: pos.y, width: pos.width, height: pos.height, fill: 'transparent', stroke: 'transparent'});
group1.add(boundRect1);
// When mouse enters the group show a border
group1.on('mouseenter', function(){
let boundRect = this.find('.boundRect');
boundRect[0].stroke('red');
layer.draw();
})
// and remove border when mouse leaves
group1.on('mouseleave', function(){
let boundRect = this.find('.boundRect');
boundRect[0].stroke('transparent');
layer.draw();
})
// If the group is clicked, enable the transformer on that group.
group1.on('click', function(){
transFormer1.attachTo(this)
layer.batchDraw();
})
// For a more pleasing demo let us have 2 groups.
// Make a copy of group1, offset new group, and change fill on its child shapes except the bound rect
let group2 = group1.clone();
layer.add(group2)
group2.position({x: 120, y: 30});
for (let i = 0, shapes = group2.getChildren(); i < shapes.length; i = i + 1){
shapes[i].fill(shapes[i].fill() !== 'transparent' ? 'cyan' : 'transparent');
}
stage.draw();
<script src="https://unpkg.com/konva#^3/konva.min.js"></script>
<p>Move mouse over the shapes to see the group borders, click a group to apply the transformer.
</p>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
Got the answer!, I just create a public transformer and on stage click I am adding nodes to it no transformer to each group just one public transformer which hold one node at a time.

How to add Kineticjs shape Circle on Kineticjs Text onclick?

I wanna be able to click on var textradie text "Show radie" and then add two circles kinGroups[index].add(circle); and kinGroups[index].add(circlered);. Im adding the two circles on my Group kinGroups[index].
All my jsonObjects[i].radie == false, so I dont know why only one object in kinGroups[index] have two circles. All my three objects should have two circles, but only one object have two circles.
var textradius = new Kinetic.Text({
x: 1000,
y: 500,
fontFamily: 'Calibri',
fontSize: 18,
text: 'Show radius',
fill: 'black'
});
kinGroups[index].add(textradie);
textradius.on('click', function() {
for(i=0; i<jsonObjects.length; i++) {
console.log("testing");
if(kinGroups[index].getName() == jsonObjects[i].name) {
if(jsonObjects[i].radie == false) {
kinGroups[index].add(circle);
kinGroups[index].add(circlered);
}
}
}
});
You have to add new circle and circlered objects to the groups.
You could use the clone() method to do this:
kinGroups[index].add(circle.clone());
kinGroups[index].add(circlered.clone());
[ Addition: example code ]
Demo: http://jsfiddle.net/m1erickson/4uAdc/
Assume you have:
A group,
A text element in that group. The text element is named "One".
Other elements in that group. Some of the other elements are named "One".
If you click on the text named "One", here's how to draw a double-circle around the other elements that are also named "One".
// define a circle that can be cloned
var circle=new Kinetic.Circle({
stroke: 'black',
strokeWidth: 2,
});
// get all children with the same name as the clicked text
var children=group.find("."+text.getName());
// iterate those children and add cloned circles
for(var i=0;i<children.length;i++){
// get the x,y of the other elements with the same name as the text
var child=children[i];
var x=child.getX();
var y=child.getY();
// add cloned circles around those other elements
var red =circle.clone({x:x,y:y,radius:10,stroke:"red"});
var blue=circle.clone({x:x,y:y,radius:15,stroke:"blue"});
text.group.add(red);
text.group.add(blue);
}
layer.draw();

removing nodes (objects, groups, etc) from group

I want to make temporary group and add nodes to it, so that they become draggable (because group is).
Then i want to remove those nodes from that group and add other nodes to that group, make them movable and others unmovable.
Group is movableGroup:
var movableGroup= new Kinetic.Group({
draggable: true,
});
I want to add shapes to it.
I want to add new shapes to it and remove shapes that was added before these.
To remove all child nodes of a group:
moveableGroup.removeChildren();
Then you can add some new nodes to the group:
var box = new Kinetic.Rect({
x: 0,
y: 0,
width: width,
height: height,
stroke : 'black',
strokeWidth: 1
});
moveableGroup.add(box);

editable Text option in kinetic js

I want to add Textbox or editable element to give the user the option to edit the text.
This is my current code:
var text = new Kinetic.Text({
text: "Sample Text", ---> i want to edit this text
x: 50,
y: 10,
fill: "transparent",
fontSize: 10,
fontFamily: "Helvetica Neue",
textFill: "#000",
align: "center",
verticalAlign: "middle",
name:'TEXT'
});
At the moment there does not seem to be any way to create editable text with Kinetic JS (see several threads about this at stackoverflow), some people suggest using an input field next to the canvas to edit the text, but my solution would be the following:
create a text with your code
on text click [text.on("click", function...], create an input field right at your mouse cursor
Well, that´s the plan. Maybe it´s easier to use a "save" button text to the input field, so you know exactly when to close it and when to store the input field data to the Kinetic text. you would also need a "close" function if you don´t want to edit it.
A very easy solution would also be a simple JavaScript prompt:
var xy = prompt("gimme your text");
So, something like this would be the best Solution imho:
myText.on('click', function(evt) {
this.setText(prompt('New Text:'));
layer.draw(); //redraw the layer containing the textfield
});
I've made an attempt for an actual KinetiJS plugin with editable text functionality.
I know it's reinventing the wheel, when you can just hover a textarea, but why not have it only in canvas too.
Check the plugin out at: https://github.com/nktsitas/kineticjs-editable-text
I did this some days back on my project. Hare are the code snippets. Basically we first draw the rectangle and then put a text area inside it and finally convert it into kinetic.text node.
drawText: function ( color )
{
var layer1=this.model.stage.getLayers()[0];
var $this=this;
console.log('drawText: color: ' + color);
if($this.rectangleDrawn==true)
{
var down = false, oPoint;
layer1.off("mousedown touchstart mousemove touchmove mouseup touchend");
if(group!=undefined && group!='')
{
$this.hideAnchors(group);
}
console.log("textX: "+textX);
//after rectangle is drawn we now have to add the editablesection
$('<textarea id="text" type="text" width='+textWidth +'px height='+textHeight+'px style="font-size: 30px;font-family:Calibri;height:'+textHeight+'px;width:'+textWidth+'px;position:absolute'+';left:'+textX+'px'+';top:'+textY+'px'+';z-index:5'+';background-color:#ffcc00;"></textarea>')
.insertBefore('.kineticjs-content');
$('#text').on('blur',function()
{
console.log("textchange");
text = new Kinetic.Text( {
x: textX,
y: textY,
stroke: color,
strokeWidth: 1,
fontSize: 30,
fontFamily: 'Calibri',
clearBeforeDraw: false,
name: 'image'+layer1.getName(),
draggable: true,
height: textHeight,
width: textWidth,
text: $('#text').val()
} );
text.on( 'mouseleave dbltap', function ()
{
text=this;
} );
$('#text').remove();
layer1.add( text );
layer1.draw();
})
},drawRectangle: function ( opacity, colorFill,stroke,textColor ){rect = new Kinetic.Rect({
x: mousePos.x,
y: mousePos.y,
width: 0,
height: 0,
stroke: stroke,
strokeWidth: 4,
opacity: opacity,
clearBeforeDraw: false,
name: 'image'+layer1.getName()
});
layer1.on( "mouseup touchend", function ( e )
{
console.log("rectangle: mouseup");
console.log("width: "+rect.getWidth( ));
rect.setOpacity(opacity);
rect.setFill(colorFill);
layer1.draw();
down = false;
console.log("textColor: "+textColor)
if(textColor!=undefined)
{
textWidth=rect.getWidth( );
textHeight=rect.getHeight( );
textX = rect.getAbsolutePosition().x;
textY=rect.getAbsolutePosition().y;
$this.rectangleDrawn=true;
$this.drawText(textColor);
console.log("textdrawn ");
$this.group.remove();
}
},

Categories

Resources