Defer vue node layout calculations on hidden browser tabs (#8805)

## Summary

If you load the window in Nodes 2.0, then switch tabs while it is still
loading, the position of the nodes is calculated incorrectly due to
useElementBounding returning left=0, top=0 for the canvas element in a
hidden tab, causing clientPosToCanvasPos to miscalculate node positions
from the ResizeObserver measurements

## Changes

- **What**: 
- Store observed elements while document is in hidden state
- Re-observe when tab becomes visible

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8805-Defer-vue-node-layout-calculations-on-hidden-browser-tabs-3046d73d365081019ae6c403c0ac6d1a)
by [Unito](https://www.unito.io)
This commit is contained in:
pythongosssss
2026-02-12 19:48:20 +00:00
committed by GitHub
parent 138fa6a2ce
commit 44ce9379eb

View File

@@ -8,9 +8,11 @@
* Supports different element types (nodes, slots, widgets, etc.) with
* customizable data attributes and update handlers.
*/
import { getCurrentInstance, onMounted, onUnmounted, toValue } from 'vue'
import { getCurrentInstance, onMounted, onUnmounted, toValue, watch } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
import { useDocumentVisibility } from '@vueuse/core'
import { useSharedCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
@@ -59,9 +61,37 @@ const trackingConfigs: Map<string, ElementTrackingConfig> = new Map([
]
])
// Elements whose ResizeObserver fired while the tab was hidden
const deferredElements = new Set<HTMLElement>()
const visibility = useDocumentVisibility()
watch(visibility, (state) => {
if (state !== 'visible' || deferredElements.size === 0) return
// Re-observe deferred elements to trigger fresh measurements
for (const element of deferredElements) {
if (element.isConnected) {
resizeObserver.observe(element)
}
}
deferredElements.clear()
})
// Single ResizeObserver instance for all Vue elements
const resizeObserver = new ResizeObserver((entries) => {
if (useCanvasStore().linearMode) return
// Skip measurements when tab is hidden — bounding rects are unreliable
if (visibility.value === 'hidden') {
for (const entry of entries) {
if (entry.target instanceof HTMLElement) {
deferredElements.add(entry.target)
resizeObserver.unobserve(entry.target)
}
}
return
}
// Canvas is ready when this code runs; no defensive guards needed.
const conv = useSharedCanvasPositionConversion()
// Group updates by type, then flush via each config's handler