mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-13 09:00:16 +00:00
fix(minimap): minimap re-render/perf issue (#9741)
## Summary Fix #9732 To clarify how preventing the 60 FPS object assignment solves the `vue-i18n` (intlify) issue, here is the complete chain reaction leading to the performance loop: 1. The Root Cause: In `useMinimapViewport.ts`, `useRafFn` acts as a timer bound to the browser's **refresh rate** (60 FPS). In the original code, it unconditionally executed the `viewportTransform.value = { ... } `assignment 60 times a second. 2. Vue's Reactivity Interception: Because `viewportTransform` is a reactive variable (`ref`), updating it causes its corresponding **computed** property (`viewportStyles`) to register a data dependency update. 3. Forced Re-rendering: The `<template> ` in `MiniMap.vue` is bound to `:style="viewportStyles"`. Since the dependent value changed, Vue's Virtual DOM decides: "I must re-render the entire `MiniMap.vue` interface **60 times** per second to ensure the element positions are up-to-date!" 4. The Victim Emerges: Inside the template of `MiniMap.vue`, there are several internationalization translation functions: `<button :aria-label="$t('g.settings')" ...> <button :aria-label="$t('g.close')" ...> `In Vue, whenever a component re-renders, all functions within its template (including `$t()`) must be re-evaluated. Because the component was being forced to re-render **60 times** per second, and there are approximately **6 calls** to `$t()` within this UI, it multiplied into 60 × 6 = **360** intlify compilation and evaluate events per second. ## Solution Only assemble objects and hand them over to Vue for rendering when the mouse is actually dragging the canvas. By extracting the math into **stack-allocated** primitive variables `(x, y, w, h) `and strictly comparing them, it completely halts the CPU burn at the source with minimal runtime overhead. ## Screenshot before <img width="1820" height="908" alt="image" src="https://github.com/user-attachments/assets/b48d1e76-6498-47c0-af41-e0594d4e7e2f" /> after <img width="1566" height="486" alt="image" src="https://github.com/user-attachments/assets/5848b7b7-c57c-494f-a99e-4f7c92889ed0" />
This commit is contained in:
@@ -187,6 +187,46 @@ describe('useMinimapViewport', () => {
|
||||
expect(transform.height).toBeCloseTo(viewportHeight * 0.5) // 300 * 0.5 = 150
|
||||
})
|
||||
|
||||
it('should maintain strict reference equality for viewportTransform when canvas state is unchanged', () => {
|
||||
vi.mocked(calculateNodeBounds).mockReturnValue({
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 500,
|
||||
maxY: 400,
|
||||
width: 500,
|
||||
height: 400
|
||||
})
|
||||
|
||||
vi.mocked(enforceMinimumBounds).mockImplementation((bounds) => bounds)
|
||||
vi.mocked(calculateMinimapScale).mockReturnValue(0.5)
|
||||
|
||||
const canvasRef = ref(mockCanvas) as Ref<MinimapCanvas | null>
|
||||
const graphRef = ref(mockGraph) as Ref<LGraph | null>
|
||||
|
||||
const viewport = useMinimapViewport(canvasRef, graphRef, 250, 200)
|
||||
|
||||
mockCanvas.ds.scale = 2
|
||||
mockCanvas.ds.offset = [-100, -50]
|
||||
|
||||
viewport.updateBounds()
|
||||
viewport.updateCanvasDimensions()
|
||||
viewport.updateViewport()
|
||||
|
||||
const initialTransform = viewport.viewportTransform.value
|
||||
|
||||
viewport.updateViewport()
|
||||
const transformAfterIdle = viewport.viewportTransform.value
|
||||
|
||||
expect(transformAfterIdle).toBe(initialTransform)
|
||||
|
||||
mockCanvas.ds.offset = [-150, -50]
|
||||
viewport.updateViewport()
|
||||
const transformAfterPan = viewport.viewportTransform.value
|
||||
|
||||
expect(transformAfterPan).not.toBe(initialTransform)
|
||||
expect(transformAfterPan.x).not.toBe(initialTransform.x)
|
||||
})
|
||||
|
||||
it('should center view on world coordinates', () => {
|
||||
const canvasRef = ref(mockCanvas) as Ref<MinimapCanvas | null>
|
||||
const graphRef = ref(mockGraph) as Ref<LGraph | null>
|
||||
|
||||
@@ -90,12 +90,16 @@ export function useMinimapViewport(
|
||||
const centerOffsetX = (width - bounds.value.width * scale.value) / 2
|
||||
const centerOffsetY = (height - bounds.value.height * scale.value) / 2
|
||||
|
||||
viewportTransform.value = {
|
||||
x: (worldX - bounds.value.minX) * scale.value + centerOffsetX,
|
||||
y: (worldY - bounds.value.minY) * scale.value + centerOffsetY,
|
||||
width: viewportWidth * scale.value,
|
||||
height: viewportHeight * scale.value
|
||||
}
|
||||
const x = (worldX - bounds.value.minX) * scale.value + centerOffsetX
|
||||
const y = (worldY - bounds.value.minY) * scale.value + centerOffsetY
|
||||
const w = viewportWidth * scale.value
|
||||
const h = viewportHeight * scale.value
|
||||
|
||||
const curr = viewportTransform.value
|
||||
if (curr.x === x && curr.y === y && curr.width === w && curr.height === h)
|
||||
return
|
||||
|
||||
viewportTransform.value = { x, y, width: w, height: h }
|
||||
}
|
||||
|
||||
const updateBounds = () => {
|
||||
|
||||
Reference in New Issue
Block a user