fix: correct calculate rect bounding
This commit is contained in:
parent
1538bb9d07
commit
dcadb97a7f
|
@ -3,20 +3,19 @@
|
|||
<v-stage class="h-full w-full" ref="stage" :config="stageSize" @mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove" @mouseup="handleMouseUp" @click="handleStageClick">
|
||||
<v-layer ref="layer">
|
||||
<template v-for="item in list" :key="item.id">
|
||||
<template v-for="item in objMap.values()" :key="item.id">
|
||||
<v-group :config="{
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
draggable: true,
|
||||
id: `group-${item.id}`,
|
||||
}" @dragstart="handleDragStart" @dragend="handleDragEnd">
|
||||
<v-rect :config="{
|
||||
x: item.box.x,
|
||||
y: item.box.y,
|
||||
width: item.box.width,
|
||||
height: item.box.height,
|
||||
}" @dragstart="handleDragStart" @dragend="handleDragEnd" @mouseover="handleCanvasObjectMouseOver">
|
||||
<v-rect :config="item.config" />
|
||||
<v-rect v-show="!isUndefined(item.box)" :config="{
|
||||
...item.box,
|
||||
visible: !isUndefined(item.box),
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
strokeWidth: 3,
|
||||
}">
|
||||
</v-rect>
|
||||
</v-group>
|
||||
|
@ -56,50 +55,88 @@ const stageSize = {
|
|||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
type CanvasObjectBox = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type CanvasObject = {
|
||||
type: "Rect";
|
||||
config: Konva.RectConfig;
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
// rotation: number;
|
||||
// scale: number;
|
||||
object: Konva.Node;
|
||||
box: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
box?: CanvasObjectBox;
|
||||
};
|
||||
|
||||
const list = ref<CanvasObject[]>([]);
|
||||
function calculateRectBounding(
|
||||
width: number,
|
||||
height: number,
|
||||
rotation: number,
|
||||
padding?: number,
|
||||
) {
|
||||
// calculate bounding box for rotated rectangle
|
||||
const radians = (rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
|
||||
// calculate corners of the rectangle
|
||||
const corners = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: width, y: 0 },
|
||||
{ x: width, y: height },
|
||||
{ x: 0, y: height },
|
||||
].map((point) => ({
|
||||
x: point.x * cos - point.y * sin,
|
||||
y: point.x * sin + point.y * cos,
|
||||
}));
|
||||
|
||||
// find bounding box dimensions
|
||||
const minX = Math.min(...corners.map((p) => p.x));
|
||||
const maxX = Math.max(...corners.map((p) => p.x));
|
||||
const minY = Math.min(...corners.map((p) => p.y));
|
||||
const maxY = Math.max(...corners.map((p) => p.y));
|
||||
|
||||
if (padding)
|
||||
return {
|
||||
x: minX - padding,
|
||||
y: minY - padding,
|
||||
width: maxX - minX + padding * 2,
|
||||
height: maxY - minY + padding * 2,
|
||||
};
|
||||
else
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
|
||||
const objMap = reactive<Map<string, CanvasObject>>(new Map());
|
||||
onMounted(() => {
|
||||
for (let n = 0; n < 100; n++) {
|
||||
const id = Math.round(Math.random() * 10000).toString();
|
||||
const x = Math.random() * stageSize.width;
|
||||
const y = Math.random() * stageSize.height;
|
||||
const width = 30 + Math.random() * 30;
|
||||
const height = 30 + Math.random() * 30;
|
||||
const rotation = Math.random() * 180;
|
||||
const scale = Math.random();
|
||||
|
||||
const star = new Konva.Star({
|
||||
rotation: rotation,
|
||||
id: id,
|
||||
numPoints: 5,
|
||||
innerRadius: 30,
|
||||
outerRadius: 50,
|
||||
fill: "#89b717",
|
||||
opacity: 0.8,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
});
|
||||
|
||||
list.value.push({
|
||||
objMap.set(id, {
|
||||
type: "Rect",
|
||||
config: {
|
||||
width: width,
|
||||
height: height,
|
||||
rotation: rotation,
|
||||
fill: "grey",
|
||||
id: id,
|
||||
},
|
||||
id: id,
|
||||
x: x,
|
||||
y: y,
|
||||
// rotation: rotation,
|
||||
// scale: scale,
|
||||
object: star,
|
||||
box: star.getClientRect(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -130,46 +167,6 @@ onMounted(() => {
|
|||
selectedBox.rotateEnabled(false);
|
||||
});
|
||||
|
||||
function degToRad(angle: number) {
|
||||
return (angle / 180) * Math.PI;
|
||||
}
|
||||
|
||||
function getCorner(
|
||||
pivotX: number,
|
||||
pivotY: number,
|
||||
diffX: number,
|
||||
diffY: number,
|
||||
angle: number,
|
||||
) {
|
||||
const distance = Math.sqrt(diffX * diffX + diffY * diffY);
|
||||
angle += Math.atan2(diffY, diffX);
|
||||
const x = pivotX + distance * Math.cos(angle);
|
||||
const y = pivotY + distance * Math.sin(angle);
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
function getClientRect(element: Konva.Node) {
|
||||
const { x, y, width, height, rotation } = element;
|
||||
const rad = degToRad(rotation() ?? 0);
|
||||
|
||||
const p1 = getCorner(x(), y(), 0, 0, rad);
|
||||
const p2 = getCorner(x(), y(), width(), 0, rad);
|
||||
const p3 = getCorner(x(), y(), width(), height(), rad);
|
||||
const p4 = getCorner(x(), y(), 0, height(), rad);
|
||||
|
||||
const minX = Math.min(p1.x, p2.x, p3.x, p4.x);
|
||||
const minY = Math.min(p1.y, p2.y, p3.y, p4.y);
|
||||
const maxX = Math.max(p1.x, p2.x, p3.x, p4.x);
|
||||
const maxY = Math.max(p1.y, p2.y, p3.y, p4.y);
|
||||
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
|
||||
function handleCacheChange(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const shouldCache = isNull(target) ? false : target.checked;
|
||||
|
@ -192,12 +189,12 @@ function handleDragStart(e: Event) {
|
|||
dragItemId.value = target.id();
|
||||
|
||||
// move current element to the top:
|
||||
const item = list.value.find((i) => i.id === dragItemId.value);
|
||||
if (isUndefined(item)) return;
|
||||
|
||||
const index = list.value.indexOf(item);
|
||||
list.value.splice(index, 1);
|
||||
list.value.push(item);
|
||||
// const item = list.value.find((i) => i.id === dragItemId.value);
|
||||
// if (isUndefined(item)) return;
|
||||
//
|
||||
// const index = list.value.indexOf(item);
|
||||
// list.value.splice(index, 1);
|
||||
// list.value.push(item);
|
||||
}
|
||||
|
||||
function handleDragEnd() {
|
||||
|
@ -335,6 +332,43 @@ function handleMouseUp() {
|
|||
|
||||
selectedIds.value = selected.map((node: Konva.Node) => node.id());
|
||||
}
|
||||
|
||||
function handleCanvasObjectMouseOver(evt: Event) {
|
||||
if (isNull(evt.target)) return;
|
||||
const target = evt.target;
|
||||
|
||||
let object = null;
|
||||
if (target instanceof Konva.Group) {
|
||||
if (!target.hasChildren()) return;
|
||||
object = target.children[0];
|
||||
} else if (target instanceof Konva.Rect) {
|
||||
object = target;
|
||||
} else {
|
||||
console.trace(`Not Konva class: ${target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client rect
|
||||
const objectConfig = objMap.get(object.id());
|
||||
if (isUndefined(objectConfig)) {
|
||||
console.error(`Not found object id: ${object.id()}`);
|
||||
return;
|
||||
}
|
||||
if (isUndefined(objectConfig.box)) {
|
||||
if (
|
||||
objectConfig.config.width &&
|
||||
objectConfig.config.height &&
|
||||
objectConfig.config.rotation
|
||||
) {
|
||||
objectConfig.box = calculateRectBounding(
|
||||
objectConfig.config.width,
|
||||
objectConfig.config.height,
|
||||
objectConfig.config.rotation,
|
||||
5,
|
||||
);
|
||||
} else console.error("Could not calculate rect bounding");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in New Issue