Reputation: 13
we (team & I) discovered Konva and it helped us tremendously so far, thanks a lot. However we can't figure how out to create a zoom¢er effect.
We added several object to a Layer itself added to a Stage, both defined as such:
let newStageArea = new Konva.Stage({
container: "mapContainer",
width: canvasWidth ,
height: canvasHeight,
x: 0,
y: 0,
draggable: true,
});
let newLayerArea = new Konva.Layer({
width: canvasWidth,
height: canvasHeight,
x: 0,
y: 0,
//since we parse object coordinates from an external source
//that uses a centered origin we have to offset the layer
offset: {
x: -(canvasWidth / 2),
y: -(canvasHeight / 2),
},
});
We are now tying to implement a method that allows, when an object belonging to the layer is clicked, to zoom on it so that it's centered and fully visible. This is is what we have:
const handleAreaClick = (e) => {
e.evt.preventDefault();
let parkingSpace = e.target;
//Get middle of object no matter its shape
let parkingSpaceBox = parkingSpace.getClientRect({
relativeTo: layerArea,
});
let parkingSpaceCenterX = parkingSpaceBox.x + parkingSpaceBox.width / 2;
let parkingSpaceCenterY = -1 * (parkingSpaceBox.y + parkingSpaceBox.height / 2);
//Zoom¢er on object
const padding = 10;
const focusZoomScale = Math.min(
windowWidth / (parkingSpaceBox.width + padding * 2),
canvasHeight/ (parkingSpaceBox.height + padding * 2));
newLayerArea.setAttrs({
x: -parkingSpaceBox.x * focusZoomScale + padding * focusZoomScale,
y: parkingSpaceBox.y * focusZoomScale + padding * focusZoomScale,
scaleX: focusZoomScale,
scaleY: focusZoomScale
})
}
No matter how we change the x and y formulas (positive/negative...), the layer does not move to the correct position, but to the left and top of the object. However the coordinates for the middle of the object are correct.
Thanks a lot for taking the time to read through.
Upvotes: 0
Views: 173
Reputation: 9525
Here is a function to scale and centre a shape in the viewport.
/* Function to scale & position the stage as needed to have
** the target shape fill the viewport.
** padding parameter is optional - if given this number
** of pixels is used as padding around the target shape.
*/
function centerAndFitShape(shape, padding) {
// set padding if not provided.
padding = padding ? padding : 0;
const
// raw rect around shape, no padding applied.
// Note: getClientRect gives size based on scaling, but
// we want the unscaled size so use 'relativeTo: stage' param
// to ensure consistent measurements.
shapeRectRaw = shape.getClientRect({relativeTo: stage}),
// Add padding to make a larger rect - this is what we want to fill the view
shapeRect = {
x: shapeRectRaw.x - padding,
y: shapeRectRaw.y - padding,
width: shapeRectRaw.width + (2 * padding),
height: shapeRectRaw.height + (2 * padding)
},
// Get the space we can see in the web page = size of div containing stage
// or stage size, whichever is the smaller
viewRect = {
width: stage.width() < stage.container().offsetWidth ? stage.width() : stage.container().offsetWidth,
height: stage.height() < stage.container().offsetHeight ? stage.height() : stage.container().offsetHeight},
// Get the ratios of target shape v's view space widths and heights
widthRatio = viewRect.width / shapeRect.width,
heightRatio = viewRect.height / shapeRect.height,
// decide on best scale to fit longest side of shape into view
scale = widthRatio > heightRatio ? heightRatio : widthRatio,
// calculate the final adjustments needed to make
// the shape centered in the view
centeringAjustment = {
x: (viewRect.width - shapeRect.width * scale)/2,
y: (viewRect.height - shapeRect.height * scale)/2
},
// and the final position is...
finalPosition = {
x: centeringAjustment.x + (-shapeRect.x * scale),
y: centeringAjustment.y + (-shapeRect.y * scale)
};
// Apply the final position and scale to the stage.
stage.position(finalPosition);
stage.scale({x: scale, y: scale});
}
There is an explanation of the approach in this blog post https://longviewcoder.com/2022/07/13/konva-making-a-shape-fill-the-view/
Upvotes: 1