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"
|
<v-stage class="h-full w-full" ref="stage" :config="stageSize" @mousedown="handleMouseDown"
|
||||||
@mousemove="handleMouseMove" @mouseup="handleMouseUp" @click="handleStageClick">
|
@mousemove="handleMouseMove" @mouseup="handleMouseUp" @click="handleStageClick">
|
||||||
<v-layer ref="layer">
|
<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="{
|
<v-group :config="{
|
||||||
x: item.x,
|
x: item.x,
|
||||||
y: item.y,
|
y: item.y,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
id: `group-${item.id}`,
|
id: `group-${item.id}`,
|
||||||
}" @dragstart="handleDragStart" @dragend="handleDragEnd">
|
}" @dragstart="handleDragStart" @dragend="handleDragEnd" @mouseover="handleCanvasObjectMouseOver">
|
||||||
<v-rect :config="{
|
<v-rect :config="item.config" />
|
||||||
x: item.box.x,
|
<v-rect v-show="!isUndefined(item.box)" :config="{
|
||||||
y: item.box.y,
|
...item.box,
|
||||||
width: item.box.width,
|
visible: !isUndefined(item.box),
|
||||||
height: item.box.height,
|
|
||||||
stroke: 'red',
|
stroke: 'red',
|
||||||
strokeWidth: 1,
|
strokeWidth: 3,
|
||||||
}">
|
}">
|
||||||
</v-rect>
|
</v-rect>
|
||||||
</v-group>
|
</v-group>
|
||||||
|
@ -56,50 +55,88 @@ const stageSize = {
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CanvasObjectBox = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
type CanvasObject = {
|
type CanvasObject = {
|
||||||
|
type: "Rect";
|
||||||
|
config: Konva.RectConfig;
|
||||||
id: string;
|
id: string;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
// rotation: number;
|
box?: CanvasObjectBox;
|
||||||
// scale: number;
|
|
||||||
object: Konva.Node;
|
|
||||||
box: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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(() => {
|
onMounted(() => {
|
||||||
for (let n = 0; n < 100; n++) {
|
for (let n = 0; n < 100; n++) {
|
||||||
const id = Math.round(Math.random() * 10000).toString();
|
const id = Math.round(Math.random() * 10000).toString();
|
||||||
const x = Math.random() * stageSize.width;
|
const x = Math.random() * stageSize.width;
|
||||||
const y = Math.random() * stageSize.height;
|
const y = Math.random() * stageSize.height;
|
||||||
|
const width = 30 + Math.random() * 30;
|
||||||
|
const height = 30 + Math.random() * 30;
|
||||||
const rotation = Math.random() * 180;
|
const rotation = Math.random() * 180;
|
||||||
const scale = Math.random();
|
|
||||||
|
|
||||||
const star = new Konva.Star({
|
objMap.set(id, {
|
||||||
rotation: rotation,
|
type: "Rect",
|
||||||
id: id,
|
config: {
|
||||||
numPoints: 5,
|
width: width,
|
||||||
innerRadius: 30,
|
height: height,
|
||||||
outerRadius: 50,
|
rotation: rotation,
|
||||||
fill: "#89b717",
|
fill: "grey",
|
||||||
opacity: 0.8,
|
id: id,
|
||||||
scaleX: scale,
|
},
|
||||||
scaleY: scale,
|
|
||||||
});
|
|
||||||
|
|
||||||
list.value.push({
|
|
||||||
id: id,
|
id: id,
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
// rotation: rotation,
|
|
||||||
// scale: scale,
|
|
||||||
object: star,
|
|
||||||
box: star.getClientRect(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -130,46 +167,6 @@ onMounted(() => {
|
||||||
selectedBox.rotateEnabled(false);
|
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) {
|
function handleCacheChange(e: Event) {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
const shouldCache = isNull(target) ? false : target.checked;
|
const shouldCache = isNull(target) ? false : target.checked;
|
||||||
|
@ -192,12 +189,12 @@ function handleDragStart(e: Event) {
|
||||||
dragItemId.value = target.id();
|
dragItemId.value = target.id();
|
||||||
|
|
||||||
// move current element to the top:
|
// move current element to the top:
|
||||||
const item = list.value.find((i) => i.id === dragItemId.value);
|
// const item = list.value.find((i) => i.id === dragItemId.value);
|
||||||
if (isUndefined(item)) return;
|
// if (isUndefined(item)) return;
|
||||||
|
//
|
||||||
const index = list.value.indexOf(item);
|
// const index = list.value.indexOf(item);
|
||||||
list.value.splice(index, 1);
|
// list.value.splice(index, 1);
|
||||||
list.value.push(item);
|
// list.value.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDragEnd() {
|
function handleDragEnd() {
|
||||||
|
@ -335,6 +332,43 @@ function handleMouseUp() {
|
||||||
|
|
||||||
selectedIds.value = selected.map((node: Konva.Node) => node.id());
|
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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in New Issue