diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png index 4dd10e50ec..f0d38ee0a7 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png differ diff --git a/packages/design-system/src/css/style.css b/packages/design-system/src/css/style.css index abe1f5d558..adf3864892 100644 --- a/packages/design-system/src/css/style.css +++ b/packages/design-system/src/css/style.css @@ -1315,15 +1315,6 @@ audio.comfy-audio.empty-audio-widget { font-size 0.1s ease; } -/* Performance optimization during canvas interaction */ -.transform-pane--interacting .lg-node * { - transition: none !important; -} - -.transform-pane--interacting .lg-node { - will-change: transform; -} - /* ===================== Mask Editor Styles ===================== */ /* To be migrated to Tailwind later */ #maskEditor_brush { diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 125418f3bd..fb3bed1a60 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -57,7 +57,6 @@ @@ -118,7 +117,6 @@ import TopbarBadges from '@/components/topbar/TopbarBadges.vue' import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue' import { useChainCallback } from '@/composables/functional/useChainCallback' import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' -import { useViewportCulling } from '@/composables/graph/useViewportCulling' import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle' import { useNodeBadge } from '@/composables/node/useNodeBadge' import { useCanvasDrop } from '@/composables/useCanvasDrop' @@ -202,7 +200,6 @@ const { shouldRenderVueNodes } = useVueFeatureFlags() // Vue node system const vueNodeLifecycle = useVueNodeLifecycle() -const { handleTransformUpdate } = useViewportCulling() const handleVueNodeLifecycleReset = async () => { if (shouldRenderVueNodes.value) { diff --git a/src/composables/graph/useViewportCulling.ts b/src/composables/graph/useViewportCulling.ts deleted file mode 100644 index 30cf82e569..0000000000 --- a/src/composables/graph/useViewportCulling.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Vue Nodes Viewport Culling - * - * Principles: - * 1. Query DOM directly using data attributes (no cache to maintain) - * 2. Set display none on element to avoid cascade resolution overhead - * 3. Only run when transform changes (event driven) - */ -import { createSharedComposable, useThrottleFn } from '@vueuse/core' -import { computed } from 'vue' - -import { useVueNodeLifecycle } from '@/composables/graph/useVueNodeLifecycle' -import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' -import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' -import { app } from '@/scripts/app' - -type Bounds = [left: number, right: number, top: number, bottom: number] - -function getNodeBounds(node: LGraphNode): Bounds { - const [nodeLeft, nodeTop] = node.pos - const nodeRight = nodeLeft + node.size[0] - const nodeBottom = nodeTop + node.size[1] - return [nodeLeft, nodeRight, nodeTop, nodeBottom] -} - -function viewportEdges( - canvas: ReturnType['canvas'] -): Bounds | undefined { - if (!canvas) { - return - } - const ds = canvas.ds - const viewport_width = canvas.canvas.width - const viewport_height = canvas.canvas.height - const margin = 500 * ds.scale - - const [xOffset, yOffset] = ds.offset - - const leftEdge = -margin / ds.scale - xOffset - const rightEdge = (viewport_width + margin) / ds.scale - xOffset - const topEdge = -margin / ds.scale - yOffset - const bottomEdge = (viewport_height + margin) / ds.scale - yOffset - return [leftEdge, rightEdge, topEdge, bottomEdge] -} - -function boundsIntersect(boxA: Bounds, boxB: Bounds): boolean { - const [aLeft, aRight, aTop, aBottom] = boxA - const [bLeft, bRight, bTop, bBottom] = boxB - - const leftOf = aRight < bLeft - const rightOf = aLeft > bRight - const above = aBottom < bTop - const below = aTop > bBottom - return !(leftOf || rightOf || above || below) -} - -function useViewportCullingIndividual() { - const canvasStore = useCanvasStore() - const { nodeManager } = useVueNodeLifecycle() - - const viewport = computed(() => viewportEdges(canvasStore.canvas)) - - function inViewport(node: LGraphNode | undefined): boolean { - if (!viewport.value || !node) { - return true - } - const nodeBounds = getNodeBounds(node) - return boundsIntersect(nodeBounds, viewport.value) - } - - /** - * Update visibility of all nodes based on viewport - * Queries DOM directly - no cache maintenance needed - */ - function updateVisibility() { - if (!nodeManager.value || !app.canvas) return // load bearing app.canvas check for workflows being loaded. - - const nodeElements = document.querySelectorAll('[data-node-id]') - for (const element of nodeElements) { - const nodeId = element.getAttribute('data-node-id') - if (!nodeId) continue - - const node = nodeManager.value.getNode(nodeId) - if (!node) continue - - const displayValue = inViewport(node) ? '' : 'none' - if ( - element instanceof HTMLElement && - element.style.display !== displayValue - ) { - element.style.display = displayValue - } - } - } - - const handleTransformUpdate = useThrottleFn(() => updateVisibility, 100, true) - - return { handleTransformUpdate } -} - -export const useViewportCulling = createSharedComposable( - useViewportCullingIndividual -) diff --git a/src/renderer/core/layout/__tests__/TransformPane.test.ts b/src/renderer/core/layout/__tests__/TransformPane.test.ts index 310294c5ce..4876115b9b 100644 --- a/src/renderer/core/layout/__tests__/TransformPane.test.ts +++ b/src/renderer/core/layout/__tests__/TransformPane.test.ts @@ -115,89 +115,6 @@ describe('TransformPane', () => { const transformState = useTransformState() expect(transformState.syncWithCanvas).toHaveBeenCalledWith(mockCanvas) }) - - it('should emit transform update timing', async () => { - const mockCanvas = createMockCanvas() - const wrapper = mount(TransformPane, { - props: { - canvas: mockCanvas - } - }) - - await nextTick() - - // Allow RAF to execute - vi.advanceTimersToNextFrame() - - expect(wrapper.emitted('transformUpdate')).toBeTruthy() - }) - }) - - describe('canvas event listeners', () => { - it('should add event listeners to canvas on mount', async () => { - const mockCanvas = createMockCanvas() - mount(TransformPane, { - props: { - canvas: mockCanvas - } - }) - - await nextTick() - - expect(mockCanvas.canvas.addEventListener).toHaveBeenCalledWith( - 'wheel', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( - 'pointerdown', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( - 'pointerup', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.addEventListener).not.toHaveBeenCalledWith( - 'pointercancel', - expect.any(Function), - expect.any(Object) - ) - }) - - it('should remove event listeners on unmount', async () => { - const mockCanvas = createMockCanvas() - const wrapper = mount(TransformPane, { - props: { - canvas: mockCanvas - } - }) - - await nextTick() - wrapper.unmount() - - expect(mockCanvas.canvas.removeEventListener).toHaveBeenCalledWith( - 'wheel', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( - 'pointerdown', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( - 'pointerup', - expect.any(Function), - expect.any(Object) - ) - expect(mockCanvas.canvas.removeEventListener).not.toHaveBeenCalledWith( - 'pointercancel', - expect.any(Function), - expect.any(Object) - ) - }) }) describe('interaction state management', () => { @@ -214,9 +131,7 @@ describe('TransformPane', () => { const transformPane = wrapper.find('[data-testid="transform-pane"]') // Initially should not have interacting class - expect(transformPane.classes()).not.toContain( - 'transform-pane--interacting' - ) + expect(transformPane.classes()).not.toContain('will-change-transform') }) it('should handle pointer events for node delegation', async () => { diff --git a/src/renderer/core/layout/transform/TransformPane.vue b/src/renderer/core/layout/transform/TransformPane.vue index edc6835729..b39f671401 100644 --- a/src/renderer/core/layout/transform/TransformPane.vue +++ b/src/renderer/core/layout/transform/TransformPane.vue @@ -1,12 +1,7 @@