fix(editor): keep loop badges on nodes while dragging
Badge centroids now read Vue Flow's live computedPosition instead of the store, which commits positions only on drop — so badges no longer lag a node drag. Centres use real node dimensions, dropping the hard-coded nudge.
This commit is contained in:
@@ -6,10 +6,12 @@
|
|||||||
* wire, toggle polarity, or delete. Hovering a badge lights up that loop's nodes
|
* wire, toggle polarity, or delete. Hovering a badge lights up that loop's nodes
|
||||||
* and links via the shared highlight.
|
* and links via the shared highlight.
|
||||||
*
|
*
|
||||||
* Badges live in screen space: each loop's centroid (the average of its node
|
* Badges live in screen space: each loop's centroid (the mean of its nodes'
|
||||||
* positions, in flow coords) is mapped through the live viewport transform so it
|
* live centres, read straight from Vue Flow) is mapped through the viewport
|
||||||
* tracks pan/zoom, and colliding badges are stacked so overlapping loops stay
|
* transform, so a badge tracks pan, zoom, and dragging a member node — the store
|
||||||
* legible (the accepted cost of on-canvas badges).
|
* only commits positions on drop, so reading it would lag a drag. Colliding
|
||||||
|
* badges are stacked so overlapping loops stay legible (the cost of on-canvas
|
||||||
|
* badges).
|
||||||
*/
|
*/
|
||||||
import { useVueFlow } from "@vue-flow/core"
|
import { useVueFlow } from "@vue-flow/core"
|
||||||
import { computed } from "vue"
|
import { computed } from "vue"
|
||||||
@@ -18,7 +20,7 @@ import type { Loop } from "@/model/loops"
|
|||||||
import { useModelStore } from "@/store/model"
|
import { useModelStore } from "@/store/model"
|
||||||
|
|
||||||
const store = useModelStore()
|
const store = useModelStore()
|
||||||
const { viewport } = useVueFlow("meadows")
|
const { viewport, getNodes } = useVueFlow("meadows")
|
||||||
const { highlight, clear } = useLoopHighlight()
|
const { highlight, clear } = useLoopHighlight()
|
||||||
|
|
||||||
const nameOf = computed(() => {
|
const nameOf = computed(() => {
|
||||||
@@ -34,24 +36,34 @@ interface Badge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const badges = computed<Badge[]>(() => {
|
const badges = computed<Badge[]>(() => {
|
||||||
const positions = new Map(store.model.nodes.map((n) => [n.id, n.position]))
|
// Live node centres from Vue Flow (computedPosition updates during a drag).
|
||||||
|
const centres = new Map(
|
||||||
|
getNodes.value.map((n) => [
|
||||||
|
n.id,
|
||||||
|
{
|
||||||
|
x: n.computedPosition.x + n.dimensions.width / 2,
|
||||||
|
y: n.computedPosition.y + n.dimensions.height / 2,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
)
|
||||||
const { x: vx, y: vy, zoom } = viewport.value
|
const { x: vx, y: vy, zoom } = viewport.value
|
||||||
const placed: Badge[] = []
|
const placed: Badge[] = []
|
||||||
|
|
||||||
for (const loop of store.loops) {
|
for (const loop of store.loops) {
|
||||||
let sx = 0
|
let sx = 0
|
||||||
let sy = 0
|
let sy = 0
|
||||||
|
let count = 0
|
||||||
for (const id of loop.nodeIds) {
|
for (const id of loop.nodeIds) {
|
||||||
const p = positions.get(id)
|
const c = centres.get(id)
|
||||||
if (p) {
|
if (c) {
|
||||||
sx += p.x
|
sx += c.x
|
||||||
sy += p.y
|
sy += c.y
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const n = loop.nodeIds.length || 1
|
if (count === 0) continue
|
||||||
// node positions are top-left; nudge toward node centre, then to screen.
|
const x = (sx / count) * zoom + vx
|
||||||
const x = (sx / n + 24) * zoom + vx
|
let y = (sy / count) * zoom + vy
|
||||||
let y = (sy / n + 18) * zoom + vy
|
|
||||||
while (placed.some((b) => Math.abs(b.x - x) < 26 && Math.abs(b.y - y) < 26)) y += 26
|
while (placed.some((b) => Math.abs(b.x - x) < 26 && Math.abs(b.y - y) < 26)) y += 26
|
||||||
placed.push({ loop, x, y })
|
placed.push({ loop, x, y })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user