Merge pull request #21 from AEmotionStudio/bolt/optimize-render-overlays-1998194206652878890
⚡ Bolt: Optimize rendering loop by hoisting getBoundingClientRect
This commit is contained in:
@@ -454,10 +454,10 @@ export class MagnifyGlass {
|
||||
|
||||
if (elementToProcess && (isTextElement || isVideoElement || isImageElement)) {
|
||||
const widgetRect = elementToProcess.getBoundingClientRect();
|
||||
const canvasRect = this.litegraphCanvas!.getBoundingClientRect();
|
||||
// Optimization: Reuse canvasRect and dpr from top level scope
|
||||
const canvasRect = rect;
|
||||
|
||||
// DPR = backing pixels / CSS pixels
|
||||
const dpr = canvasRect.width > 0 ? (this.litegraphCanvas!.width / canvasRect.width) : 1;
|
||||
// DPR is already calculated at function start
|
||||
const currentScale = this.state.canvasScale;
|
||||
const isVirtualZoomMode = currentScale < 0.7;
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
|
||||
// Create hoisted mocks
|
||||
const { appMock } = vi.hoisted(() => {
|
||||
return {
|
||||
appMock: {
|
||||
registerExtension: vi.fn(),
|
||||
ui: {
|
||||
settings: {
|
||||
getSettingValue: vi.fn(),
|
||||
setSettingValue: vi.fn(),
|
||||
}
|
||||
},
|
||||
canvas: {
|
||||
ds: {
|
||||
scale: 1,
|
||||
offset: [0, 0]
|
||||
}
|
||||
},
|
||||
graph: {
|
||||
_nodes: [] as any[]
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Attach to global for files that rely on global variable 'app'
|
||||
// @ts-ignore
|
||||
globalThis.app = appMock;
|
||||
|
||||
vi.mock('/scripts/app.js', () => ({
|
||||
app: appMock
|
||||
}));
|
||||
|
||||
// Import after mocks are set up
|
||||
import { MagnifyGlass } from '../../src/magnify-glass/MagnifyGlass';
|
||||
|
||||
describe('MagnifyGlass Rendering Optimization', () => {
|
||||
let magnifyGlass: MagnifyGlass;
|
||||
let canvasMock: HTMLCanvasElement;
|
||||
let canvasGetBoundingClientRectSpy: any;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset appMock state
|
||||
appMock.graph._nodes = [];
|
||||
|
||||
// Setup document body
|
||||
document.body.innerHTML = '<canvas id="graph-canvas" width="1000" height="800"></canvas>';
|
||||
canvasMock = document.getElementById('graph-canvas') as HTMLCanvasElement;
|
||||
|
||||
// Mock getBoundingClientRect
|
||||
canvasGetBoundingClientRectSpy = vi.spyOn(canvasMock, 'getBoundingClientRect').mockReturnValue({
|
||||
x: 0, y: 0, width: 1000, height: 800, top: 0, left: 0, right: 1000, bottom: 800, toJSON: () => {}
|
||||
});
|
||||
|
||||
// Initialize MagnifyGlass manually
|
||||
magnifyGlass = new MagnifyGlass();
|
||||
magnifyGlass.litegraphCanvas = canvasMock;
|
||||
|
||||
// Mock UI container
|
||||
magnifyGlass.ui.htmlOverlayContainer = document.createElement('div');
|
||||
|
||||
// Setup state
|
||||
magnifyGlass.state.active = true;
|
||||
magnifyGlass.state.canvasScale = 1;
|
||||
magnifyGlass.state.x = 500;
|
||||
magnifyGlass.state.y = 400;
|
||||
|
||||
// Setup config
|
||||
magnifyGlass.config.glassSize = 300;
|
||||
magnifyGlass.config.zoomFactor = 2;
|
||||
|
||||
// Trigger calculateSourceRegion
|
||||
magnifyGlass.calculateSourceRegion();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('should call getBoundingClientRect multiple times (inefficiently) before optimization', () => {
|
||||
// Setup nodes and widgets
|
||||
const numNodes = 5;
|
||||
const widgetsPerNode = 3;
|
||||
|
||||
const nodes = [];
|
||||
for (let i = 0; i < numNodes; i++) {
|
||||
const widgets = [];
|
||||
for (let j = 0; j < widgetsPerNode; j++) {
|
||||
const el = document.createElement('div');
|
||||
const videoEl = document.createElement('video');
|
||||
el.appendChild(videoEl);
|
||||
|
||||
widgets.push({
|
||||
type: 'custom',
|
||||
element: el
|
||||
});
|
||||
}
|
||||
|
||||
nodes.push({
|
||||
pos: [400, 300],
|
||||
size: [200, 200],
|
||||
widgets: widgets
|
||||
});
|
||||
}
|
||||
|
||||
appMock.graph._nodes = nodes;
|
||||
|
||||
// Mock widget getBoundingClientRect
|
||||
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({
|
||||
x: 450, y: 350, width: 100, height: 100, top: 350, left: 450, right: 550, bottom: 450, toJSON: () => {}
|
||||
});
|
||||
|
||||
// Run renderHtmlOverlays
|
||||
magnifyGlass.renderHtmlOverlays();
|
||||
|
||||
// Calculate expected calls
|
||||
// 1 call in setup (calculateSourceRegion)
|
||||
// 1 call in renderHtmlOverlays (top level)
|
||||
// 0 calls inside loop (optimization)
|
||||
const expectedCalls = 2;
|
||||
|
||||
// After optimization, this should pass
|
||||
expect(canvasGetBoundingClientRectSpy).toHaveBeenCalledTimes(expectedCalls);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user