pikaso
pikaso copied to clipboard
New center and zoom options
Resume
I plan to change the way in which Pikaso creates the container, to give the possibility to center the image anything natively with an algorithm instead of using a css trick to center the background image to the stage, and additionally the function of zoomIn and zoomOut
Example of implementation to center any shape in stage for example in file Background.ts:125
if (options.size === 'resize') {
this.board.centerAndFitShape(this.image.node)
}
/**
* Centers the stage and fit especific shape
* @param {Konva.Shape} shape
* @param {number} padding = 2
* @example
* ```ts
* editor.board.centerAndFitShape(Konva.Shape)
* ```
*/
centerAndFitShape(shape, padding = 2) {
padding = padding || 0
const rawShapeRect = shape.getClientRect({ relativeTo: this.stage }), // Note: getClientRect gives size based on scaling - we want unscaled size so use 'relativeTo: stage' param to ensure consistent measurements.
paddedShapeRect = {
x: rawShapeRect.x - padding,
y: rawShapeRect.y - padding,
width: rawShapeRect.width + (2 * padding),
height: rawShapeRect.height + (2 * padding)
},
viewRect = {
width: Math.min(this.stage.width(), this.container.offsetWidth),
height: Math.min(this.stage.height(), this.container.offsetHeight)
},
widthRatio = viewRect.width / paddedShapeRect.width, heightRatio = viewRect.height / paddedShapeRect.height,
scale = widthRatio > heightRatio ? heightRatio : widthRatio,
centeringAjustment = {
x: (viewRect.width - paddedShapeRect.width * scale) / 2,
y: (viewRect.height - paddedShapeRect.height * scale) / 2
},
finalPosition = {
x: centeringAjustment.x + (-paddedShapeRect.x * scale),
y: centeringAjustment.y + (-paddedShapeRect.y * scale)
}
this.stage.to({
x: finalPosition.x,
y: finalPosition.y,
scaleX: scale,
scaleY: scale,
duration: 0.1
})
}
/**
* Zoom the stage
* @param {number} scaleBy
* @see zoomIn = 1.2, zoomOut = 0.8
*/
zoom (scaleBy) {
const oldScale = this.stage.scaleX()
// Calculate the center of the stage
const pos = {
x: this.stage.width() / 2,
y: this.stage.height() / 2
}
// Calculate the position of the mouse relative to the stage
const mousePointTo = {
x: pos.x / oldScale - this.stage.x() / oldScale,
y: pos.y / oldScale - this.stage.y() / oldScale
}
// Calculate the new scale of the stage
const newScale = Math.max(0.05, oldScale * scaleBy)
// Calculate the new position of the stage to keep it centered
const newPos = {
x: -(mousePointTo.x - pos.x / newScale) * newScale,
y: -(mousePointTo.y - pos.y / newScale) * newScale
}
// Update the stage with the new position and scale
this.stage.to({
x: newPos.x,
y: newPos.y,
scaleX: newScale,
scaleY: newScale,
duration: 0.1
})
}
I am going to work on an algorithm that serves to pan and zoom with the mouse to integrate it if these recommendations are approved
Hey @gniuslab It is fantastic that you are proposing adding Zooming to the core project, which is also being requested here: https://github.com/pikasojs/pikaso/issues/28
However, I am having difficulty understanding what the real use case is for centerAndFitShape
.
Let's talk about that in more detail.
Hi @raminious , the use case of the centerAndFitShape function is quite trivial to make things uncenterable if they go off-canvas, you can see how it works in detail here...
https://codepen.io/JEE42/pen/ExEgeMX
Do this exercise, move one of the red squares out of your vision and click on Fit rect group to stage
@gniuslab Is there any progress on the zooming feature?
@raminious Yes, I can implement it, but I don't know the process of how to do it, in your main branch...
think you ,You guys are the best.
@raminious This only solves the scaling problem, but does not solve the box selection x y offset problem.Sorry, my English is not good
This is my code
`stage.on('wheel', (e) => { e.evt.preventDefault(); let oldScale = stage.scaleX(); let scaleBy=1.1 let mousePointTo = { x: stage.getPointerPosition().x / oldScale - stage.x() / oldScale, y: stage.getPointerPosition().y / oldScale - stage.y() / oldScale, };
let newScale = e.evt.deltaY > 0 ? oldScale / scaleBy : oldScale * scaleBy ;
stage.scale({ x: newScale, y: newScale });
let newPos = {
x: -(mousePointTo.x - stage.getPointerPosition().x / newScale) * newScale,
y: -(mousePointTo.y - stage.getPointerPosition().y / newScale) * newScale,
};
stage.position(newPos);
stage.batchDraw();
});`
@ProjetNice Use getRelativePointerPosition
I still can't do this effect, I would like to add this feature and add it in the documentation, which is very helpful for beginners
I solved that problem, and this is my demo, using the vue framework
<template>
<button @click="addPolygon">addPolygon</button>
<button @click="changeDrag">changeDrag</button>
<div id="editor" ref="root"></div>
</template>
<script setup>
import {ref, onMounted} from "vue";
import {Pikaso} from "pikaso";
let editor;
let stage;
let layer
let board
let selection
const root = ref(null);
let dragFlag=false
onMounted(() => {
editor = new Pikaso({
container: root.value,
selection: {
transformer: {
borderStroke: "#1890ff",
borderStrokeWidth: 0.5,
anchorFill: "#1890ff",
anchorSize: 10,
},
zone:{
fill:"#1890ff50",
stroke:'#1890ff',
}
},
});
editor.board.background.fill('rgba(0,0,0,0.5)')
stage=editor.board.stage
layer=editor.board.layer
board=editor.board
selection=board.selection
/**
* Used to calibrate mouse position
*/
stage.on("mousemove touchmove",(e)=>{
let zoom=selection.zone
let x=zoom.attrs.x
let y=zoom.attrs.y
let width=zoom.attrs.width
let height=zoom.attrs.height
let sx=stage.x()
let sy=stage.y()
let scx=stage.scaleX()
let scy=stage.scaleY()
zoom.attrs.x = (x - sx) / scx;
zoom.attrs.y = (y - sy) / scy;
zoom.attrs.width=width/scx
zoom.attrs.height=height/scy
})
/**
* Resize the canvas
*/
stage.on('wheel', (e) => {
e.evt.preventDefault();
let oldScale = stage.scaleX();
let scaleBy=1.1
let mousePointTo = {
x: stage.getPointerPosition().x / oldScale - stage.x() / oldScale,
y: stage.getPointerPosition().y / oldScale - stage.y() / oldScale,
};
let newScale = e.evt.deltaY > 0 ? oldScale / scaleBy : oldScale * scaleBy ;
stage.scale({ x: newScale, y: newScale });
let newPos = {
x: -(mousePointTo.x - stage.getPointerPosition().x / newScale) * newScale,
y: -(mousePointTo.y - stage.getPointerPosition().y / newScale) * newScale,
};
stage.position(newPos);
stage.batchDraw();
});
})
function addPolygon() {
editor.shapes.polygon.insert({
x: 250,
y: 250,
radius: 90,
sides: 6,
fill: "#fafafa",
});
}
function changeDrag(){
stage.draggable(!dragFlag)
dragFlag=!dragFlag
}
</script>
<style>
#editor {
width: 100%;
height: 100vh;
background: #ccc;
}
</style>