How to create a resizable konva shape when user hovers - konvajs

I am working on konva js. I am working on an app that lets users create custom shapes. and after creation they can resize it by mouse, connect two shapes by a line. So far the feature drawing custom shape and their connecting by line is complete.
Now I want to show dots around the custom shape when user hovers on the custom shape just like this
P.S I have seen this from https://app.diagrams.net/. I want to build drawing app same like it.If someone can navigate me to the resources from where I can build drawing app like this, it would be really helpful.

You can use mouseenter and mouseleave events to show/hide anchors for the shape.
It is up to you to choose how to implement anchors. It can be custom anchors like in https://konvajs.org/docs/sandbox/Modify_Curves_with_Anchor_Points.htm or it can be Konva.Transformer https://konvajs.org/docs/select_and_transform/Basic_demo.html.
On mouseenter you can show anchors. Hiding anchors is a bit tricker for the demo I will hide anchors when mouse is too far away from the shape. We can't use mouseleave as is here, because it will trigger hide when we simply hover Konva.Transformer.
In the demo, try to hover the circle.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const shape = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 50,
fill: 'green'
});
layer.add(shape);
const tr = new Konva.Transformer();
layer.add(tr);
// from https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
function getDistance(rect, p) {
var dx = Math.max(rect.x - p.x, 0, p.x - (rect.x + rect.width));
var dy = Math.max(rect.y - p.y, 0, p.y - (rect.y + rect.height));
return Math.sqrt(dx*dx + dy*dy);
}
shape.on('mouseenter', () => {
tr.nodes([shape]);
});
stage.on('mousemove', () => {
if (!tr.nodes().length) {
return;
}
if (tr.isTransforming()) {
return;
}
const rect = shape.getClientRect();
const dist = getDistance(rect, stage.getPointerPosition());
if (dist > 60) {
tr.nodes([]);
}
});
layer.draw();
<script src="https://unpkg.com/konva#^8/konva.min.js"></script>
<div id="container"></div>

Related

yAxis resizer to change svgrenderer position also highcharts

I have a chart with some indicators below it. Each indicator area consists
of a svg renderer button. so when I use resize property to drag and resize
the panes, the series resized perfectly but the button remains in its same
position, Can we move the button with the resizer?
Here I created a sample link to regenerate
https://jsfiddle.net/q0ybpnvx/2/
Any help will be appreciated. I am having great trouble. Thank you
You can add and position the custom button in render event:
chart: {
events: {
render: function() {
var chart = this;
if (chart.customBtn) {
chart.customBtn.attr({
y: chart.yAxis[1].top,
});
} else {
chart.customBtn = chart.renderer.button(
'sometext',
5,
chart.yAxis[1].top,
function() {
console.log('some task')
}).add()
}
}
}
},
Live demo: https://jsfiddle.net/BlackLabel/b7vq4ecy/
API Reference:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr
I was able to do something by manually changing the x/y attributes of my svgRenderer label, the same should apply to buttons.
I'm in angular and have a listener for screen resizing:
Also Note that you can change the entire SVGRenderer label with the attr.text property.
this.chart = Highcharts.chart(.....);
// I used a helper method to create the label
this.chart.myLabel = this.labelCreationHelperMethod(this.chart, data);
this.windowEventService.resize$.subscribe(dimensions => {
if(dimensions.x < 500) { //this would be your charts resize breakpoint
// here I was using a specific chart series property to tell where to put my x coordinate,
// you can traverse through the chart object to find a similar number,
// or just use a hardcoded number
this.chart.myLabel.attr({ y: 15, x: this.chart.series[0].points[0].plotX + 20 });
} else {
this.chart.myLabel.attr({ y: 100, x: this.chart.series[0].points[0].plotX + 50 });
}
}
//Returns the label object that we keep a reference to in the chart object.
labelCreationHelperMethod() {
const y = screen.width > 500 ? 100 : 15; 
const x = screen.width > 500 ? this.chart.series[0].points[0].plotX + 50 :
this.chart.series[0].points[0].plotX + 20
// your label
const label = `<div style="color: blue"...> My Label Stuff</div>`
return chart.renderer.label(label, x, y, 'callout', offset + chart.plotLeft, chart.plotTop + 80, true)
.attr({
fill: '#e8e8e8',
padding: 15,
r: 5,
zIndex: 6
})
.add();
}

Konvajs - how to take a screenshot without transparency

Im just trying to let my user take a screenshot using the toDataUrl() function.
but, without a background rectangle, all pixel are transparent , and appear black .
so the solution is to dynamicly add a rectangle, generate the image, destroy the rectangle
saveImage(){
const stage=this.$parent.$refs.stage.getStage()
var stageRect = new Konva.Rect({
x:0,
y:0,
width: stage.attrs.width,
height: stage.attrs.height,
fill: 'green',
})
console.log(stage)
const backg=new Konva.Layer();
backg.add(stageRect)
stage.add(backg)
backg.setZIndex(0)
const dataURL = stage.toDataURL({ pixelRatio: 1, mimeType:"image/png" });
backg.destroy();
this.downloadURI(dataURL, 'stage.png');
},
it works (rectangle is created before all other layer) but... i can't get the size of the stage, i mean, the viewport because the user can zoom/dezoom the stage ....
any idea ?
Just use a scale to calculate background properties:
var stageRect = new Konva.Rect({({
x: -stage.x()/ stage.scaleX(),
y: -stage.y()/ stage.scaleY(),
width: stage.width() / stage.scaleX(),
height: stage.height() / stage.scaleY(),
fill: 'green',
});
Demo: https://jsbin.com/lehasitaje/2/edit?html,js,output

Konvajs custom shapes and transformers

What I am trying to achieve is showing the transformer around the custom shape itself. I took the code directly from the API docs for creating the custom shape and adding the transformer. The transformer works great for rectangles, circles, etc but for custom shapes, it doesn't seem to appear properly.
Here is a link to a demo app with the issue with custom shapes and transformer:
https://jsfiddle.net/5zpua740/
var stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
var layer = new Konva.Layer();
/*
* create a triangle shape by defining a
* drawing function which draws a triangle
*/
var triangle = new Konva.Shape({
sceneFunc: function (context) {
context.beginPath();
context.moveTo(120, 150);
context.lineTo(320, 180);
context.quadraticCurveTo(250, 200, 360, 270);
context.closePath();
// Konva specific method
context.fillStrokeShape(this);
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
// add the triangle shape to the layer
layer.add(triangle);
// add the layer to the stage
stage.add(layer);
stage.on('click', function (e) {
// if click on empty area - remove all transformers
if (e.target === stage) {
stage.find('Transformer').destroy();
layer.draw();
return;
}
// remove old transformers
// TODO: we can skip it if current rect is already selected
stage.find('Transformer').destroy();
// create new transformer
var tr = new Konva.Transformer();
layer.add(tr);
tr.attachTo(e.target);
layer.draw();
})
In this sample, you can see that if you click on the object, the transformer appears in the corner. You can still use it to manipulate the object but it's not around the object itself.
Any help appreciated! Thanks in advance.
Konva can't detect bounding box of a custom shape. But we can simply help it. We just need to define a method getSelfRect.
The method should return bounding box of a shape without transforming applied (like the shape has no rotation, no scaling and placed in x =0, y=0).
We can do this by just looking at sceneFunc:
triangle.getSelfRect = function() {
return {
// sceneFunc started from moving to 120, 150 point
// so it is our top left point
x: 120,
y: 150,
// the bottom right point finished with quadraticCurveTo
// I will use the coordinates to calculate size of the shape
width: 360 - 120,
height: 270 - 150
};
}
Demo: http://jsbin.com/lazuhowezi/2/edit?js,output

Konvajs shape stroke color lingers after change

I'm trying to change the stroke color of a hexagon on mouseover, and then back to the original color on mouseout.
My problem is that, if I redraw only the hexagon after updating the stroke color, the previous color lingers around the edges of the stroke.
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
});
hexagon.on('mouseout', function(e) {
e.target.stroke('gray');
e.target.draw();
});
Demo at https://codepen.io/jsgarvin/pen/dmRJXj
Here the original color is gray, and it changes to red on mouse over, but on mouse out it changes back to gray with a red dusting around all of the edges.
If I redraw the entire layer though, it seems to do what I expect, but in my particular use case I expect to have several thousand hexagons, among other things, on the layer, and that seems inefficient to redraw the entire layer if I just need to update one hexagon. Is there a more correct way to do this that I'm overlooking? Thanks!
You need to draw the layer.
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
I found this out as I was coding an alternative which I include below in the snippet. It uses a second shape and we show & hide the two so as to provide the mouseover effect. I can imagine that this will not be viable in all cases but it might help someone out so here is a working snippet.
Left hand is your example copied from your Pen with the fix included, the right is the shape switcher, just for fun.
var stage = new Konva.Stage({
container: 'container',
width: 400,
height: 150
});
var layer = new Konva.Layer();
stage.add(layer);
var c = layer.getCanvas();
var ctx = c.getContext();
var hexagon = new Konva.RegularPolygon({
x: 75,
y: 75,
radius: 55,
sides: 6,
stroke: 'gray',
strokeWidth: 10
});
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
hexagon.on('mouseout', function(e) {
e.target.stroke('gray');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
var hexagon2 = new Konva.RegularPolygon({
x: 250,
y: 75,
radius: 55,
sides: 6,
stroke: 'gray',
strokeWidth: 10
});
hexagon2.on('mouseover', function(e) {
e.target.visible(false);
hexagon3.visible(true);
layer.draw();
});
var hexagon3 = new Konva.RegularPolygon({
x: 250,
y: 75,
radius: 55,
sides: 6,
stroke: 'red',
strokeWidth: 10,
visible: false
});
hexagon3.on('mouseout', function(e) {
e.target.visible(false);
hexagon2.visible(true);
layer.draw();
});
layer.add(hexagon);
layer.add(hexagon2);
layer.add(hexagon3);
stage.draw();
<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.6.5/konva.min.js"></script>
<p>Left image is OP's version, right is shape-switching. Mouse-over the hexagons.</p>
<div id='container'></div>

3D rotation using Jquery .draggable() on 3D css element

I'm trying to figure out how to enable visitors to drag to rotate a 3D div using .draggable(). Currently the div rotates but also moves vertically and horizontally making the process touchy and unpredictable. I would like the origin of the div to stay fixed, and the "dragging" to only affect the rotation, so users can "spin" the div around to see the other sides.
here is link to the codepen: http://codepen.io/armandhammer8/pen/IiBga
$('.anime').draggable({
drag: function(event, ui){
var rotateCSS = 'rotate(' + ui.position.left + 'deg)';
$(this).css({
'transform': rotateCSS,
'-moz-transform': rotateCSS,
'-webkit-transform': rotateCSS
});
Thanks in advance!
The div element is a little house:
I want to be able to spin it around
The built in functionality of draggable is giving you the problems.
It's not so hard to get the functionality by yourself and stop using draggable.
Javascript:
var offset = 0, startX;
var elem = document.getElementById("element");
$('.draggable').on('mousedown', function (e) {
startX = e.pageX - offset;
})
.on('mouseup', function() {
startX = null;
})
.on('mousemove', function (e) {
if(startX) {
offset = e.pageX - startX;
elem.style['-webkit-transform'] = 'rotateY(' + offset + 'deg)';
}
});
demo

Resources