Custom Hit Detection on Konva Group - konvajs

I'm attempting to expand the click area of a group of shapes, but there appears to be no hitFunc property on groups.
var patternControl = new Konva.Group();
patternControl.hitFunc(function(context) {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(this);
});
Is there any way to do apply custom hit functions to a group?

Only shapes can be used for hit detection. As a workaround, you can disable hits for all shapes with shape.listeting(false) and then add a "fake" shape to the group that will be used as a hit area:
var patternControl = new Konva.Group();
var hitShape = new Konva.Shape({
// make it transparent, so it is not visible
fill: 'rgba(0,0,0,0)',
hitFunc: (context, shape) => {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(shape);
}
});
patternControl.add(hitShape);

Related

How to get clicked element's position and id to change color and draw lines on it

I'm developing a basic window frame configurator. I splitted glasses in function below. I want to change color when i clicked and get the position of clicked glass to draw openin direction lines.
I tried to understand and implement Lavrton's method(https://codesandbox.io/s/0xj7zml2zl?file=/src/index.js) but i couldn't succeed.
function glassDraw(piece, frameWidth, frameHeight) {
var glassGroup = new Konva.Group();
for (i = 0; i <piece; i++) {
var glass = new Konva.Rect({
name: 'Rect',
x: padding + (frameWidth / piece) * i,
y: padding,
width: frameWidth / piece,
height: frameHeight - padding * 2,
fill: 'lightblue',
id: 'glass'+i,
});
glassGroup.add(glass);
}
glassGroup.find("Rect").on('click', function (e) {
// get id of the cube i drag
var clickedId = e.target.attrs.id;
$('#theId').html(clickedId);
})
return glassGroup;
}
When i use getelementbyid method with id of konva element("glass"+i), it returns null. I think i need to get as a konva element.
You have to create a click listener for all of your rectangles.
for (let rect of glassGroup.children) {
rect.on('click', () => {
console.log(rect.x(), rect.y()); // current position of the object
console.log(rect.id()); // log id of the object
rect.fill('green'); // set color to green
layer.batchDraw(); // update layer (batchDraw just for better performance .draw() would work to)
})
}
Make sure you always update the stage by either call stage.draw() or layer.draw() (or batchDraw() for better performance) after changing something, otherwise your stage will not show anything of what you do.
If something with this code don't work feel free to ask.
Have a nice day.

Konva - visualize custom hit area

Is there an easy way to visualize a custom hit area shape?
As described here
https://konvajs.github.io/docs/events/Custom_Hit_Region.html
the hitFunc attribute can be set to a function that uses the supplied context to draw a custom hit area / region. Something like this:
var star = new Konva.Star({
...
hitFunc: function (context) {
context.beginPath()
context.arc(0, 0, this.getOuterRadius() + 10, 0, Math.PI * 2, true)
context.closePath()
context.fillStrokeShape(this)
}
})
For debugging purposes, I would like an easy way to toggle visual rendering of the shape (circle in this case), eg by filling it yellow.
Thanks :)
Currently, there is no public API for that. But you still can add hit canvas into the page somewhere and see how it looks:
const hitCanvas = layer.hitCanvas._canvas;
document.body.appendChild(hitCanvas);
// disable absolute position:
hitCanvas.style.position = '';
http://jsbin.com/mofocagupi/1/edit?js,output
Or you can add hit canvas on top of the stage and apply an opacity to make scene canvases visible:
const hitCanvas = layer.hitCanvas._canvas;
stage.getContainer().appendChild(hitCanvas);
hitCanvas.style.opacity = 0.5;
hitCanvas.style.pointerEvents = 'none';
http://jsbin.com/gelayolila/1/edit?js,output

How to configure how close together features need to be to form a cluster in openlayers3

Is there a parameter to set a minimum radius for features to cluster? - so that when a set of points or features are within some minimum distance, they form a cluster, otherwise not?
ol.source.Cluster() has two parameters that look promising, but don't seem to work as expected.
distance: Minimum distance in pixels between clusters. Default is 20.
extent: An array of numbers representing an extent: [minx, miny,
maxx, maxy].
Not quite sure what "but don't seem to work as expected." means?? How does it not work as expected??
distance property of ol.source.Cluster tells the layer when to group objects based on the distance set. It can be changed when creating the cluster layer. For example:
var locationSource = new ol.source.Vector({
url: loc_url,
format: new ol.format.GeoJSON({
defaultDataProjection :'EPSG:3857'
}),
loader: vectorLoader,
strategy: ol.loadingstrategy.all
});
var LOCclusterSource = new ol.source.Cluster({
distance: 5,
source: locationSource
});
I usually change the distance object until I find the desired distance so group/cluster object look right on the map.
The radius of a group object on a map layer can be change via a style function for the map layer. Many example of style functions exist here on stack.
Here is a hacked up example that I use to increase the radius of cluster/group objects on the map layer so it's visually obvious that it is a group/cluster object:
NOTE: You can have different shapes on the same layer too using a style function. https://openlayers.org/en/latest/examples/regularshape.html
// Location Map Layer Properties
var locLyrProps = {
"radius": 8,
"CORadius": 12,
"groupRadius": 10,
"borderWidth": 2,
"color": [0, 0, 0, 0.5],
"txtMaxRes": 20,
"txtOffsetY": -20
};
var styleFunction = function() {
return function(feature,resolution) {
var style;
var props = locLyrProps;
var radius;
var lyrTyp;
var gotGroup = false;
var features = feature.get('features');
if (features.length == 1) { //Individual map object because length = 1
style = new ol.style.Style({ //Square layer object
image: new ol.style.RegularShape({
radius: radius,
points: 4,
angle: Math.PI / 4,
fill: createFillStyle(feature),
stroke: createStrokeStyle(feature, resolution)
}),
text: createTextStyle(feature, resolution)
});
} else {
var rad = props.radius;
if (features.length > 1) { //If cluster/group of features increase radius of group object so group objects stand out a bit
rad = props.groupRadius; //If cluster/group object is found, set cluster/group radius for it
gotGroup = true;
}
console.log('circle radius: ' + rad);
style = new ol.style.Style({
image: new ol.style.Circle({
radius: rad,
fill: createFillStyle(feature),
stroke: createStrokeStyle(feature, resolution, gotGroup)
}),
text: createTextStyle(feature, resolution, props, gotGroup)
});
}
return [style];
};
};

How to draw bezier curve randomly for every second? also using the transition.dart

I want to know how to draw bezier curve randomly that change every second with animation.
That's a very specific question. To generic answer is that each draw command on the graphics class returns an object. You can use this object to change the properties (x, y, color, width, ...) of the draw command later. This example should give you an idea:
import 'dart:html' as html;
import 'package:stagexl/stagexl.dart';
void main() {
var canvas = html.querySelector('#stage');
var stage = new Stage(canvas, width: 800, height: 600);
var renderLoop = new RenderLoop();
renderLoop.addStage(stage);
var shape = new Shape();
var movetoCommand = shape.graphics.moveTo(100, 100);
var bezierCommand = shape.graphics.bezierCurveTo(500, 200, 200, 500, 500, 500);
var strokeCommand = shape.graphics.strokeColor(Color.Red, 15);
stage.addChild(shape);
stage.juggler.translation(500, 200, 5.0, Transition.sine).listen((v) {
// change "controlX1" of the bezier draw command in an animation
bezierCommand.controlX1 = v.toDouble();
});
stage.juggler.translation(200, 500, 5.0, Transition.sine).listen((v) {
// change "controlX2" of the bezier draw command in an animation
bezierCommand.controlX2 = v.toDouble();
});
stage.juggler.translation(15, 50, 15.0, Transition.sine).listen((v) {
// change "width" of the stroke draw command in an animation
strokeCommand.width = v.toDouble();
});
}
I won't describe how to make the positions of the bezier curve random, that's just a specialized case of the example shown above.

Two ribbon with same z, they covered each other

I'm drawing map roads with THREE.Ribbon, every road has border with different color, so I use two ribbons, one is wider with the color of border, one is narrower, then I can achieve my purpose.
I also draw cap on two ends of ribbon with circles, certainly draw two circles, the smaller one covers bigger one, just like linecap in canvas2D.
I can't use THREE.Line as my alternative, because the maximum value of width of Line is 1 in my webgl.
My codes are just like this:
var circleShape = new THREE.Shape();
var circleRadius = lineWidth/2;
circleShape.moveTo( 0, circleRadius );
circleShape.quadraticCurveTo( circleRadius, circleRadius, circleRadius, 0 );
circleShape.quadraticCurveTo( circleRadius, -circleRadius, 0, -circleRadius );
circleShape.quadraticCurveTo( -circleRadius, -circleRadius, -circleRadius, 0 );
circleShape.quadraticCurveTo( -circleRadius, circleRadius, 0, circleRadius );
var circle = new THREE.ShapeGeometry( circleShape);
var material = new THREE.MeshBasicMaterial( { color: color, depthWrite: false} );
var mesh = new THREE.Mesh( circle, material );
this.parent.add( mesh );
var material = new THREE.MeshBasicMaterial( { color: widerColor, depthWrite: false} );
var widerRibbon = new THREE.Ribbon( widerGeometry, material );
this.parent.add( widerRibbon );
var material = new THREE.MeshBasicMaterial( { color: narrowerColor, depthWrite: false} );
var narrowerRibbon = new THREE.Ribbon( narrowerGeometry, material );
this.parent.add( narrowerRibbon );
My logic is the latter will cover the former. so narrower ribbon will cover wider ribbon, ribbon will cover half of circle.
My difficulty:
The consequence is they cover each other(without depthWrite:false).
I have found that THREE.MeshBasicMaterial can set depthWrite to false, I add it, then I find that narrower ribbon covered wider ribbon successfully, but what is weird is the cap on the ends of ribbon seems abnormal, when I look at the ribbon right on top, it works well, but when I look it with a angle, I find that the ribbon can't cover circle.
I don't know how to deal this issue, I just want the later drawing covers the former drawing when they all have same z coordinate.
Thanks for your guidance!
If you want to use "painter's algorith" (whatevr is painted last is always what you see), just turn Z off in the material(s). This is, set the "depthTest" attribute to false when declaring the material

Resources