perf(editor): skip floorplan viewport sync while the 2D panel is hidden
Camera-zoom hitch: the FloorplanPanel stays mounted (display:none in 3D mode), so its navigation-pose subscriber fired every camera onUpdate. During zoom the view-width changes continuously, so the epsilon guard never short-circuited and `syncFloorplanViewportToNavigationPose` ran each frame → setViewport/ setFloorplanUserRotationDeg → a full re-render of the ~10k-line floorplan SVG, even though nothing is visible (React reconciles display:none subtrees). Gate the viewport sync on `isFloorplanOpen` via a ref the per-frame subscriber reads, and re-run the mount catch-up effect when the panel reopens so the viewport snaps to the current camera. The compass is unaffected — it's portaled to the always-visible viewer area and still receives the pose; only the panel's own viewport sync is skipped while hidden. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bbe5b9a8eb
commit
b384bc8178
@@ -5046,6 +5046,11 @@ export function FloorplanPanel({
|
||||
// the user closes and re-opens the 2D editor instead of restoring the
|
||||
// stale viewport from before they closed it.
|
||||
const isFloorplanOpen = useEditor((state) => state.isFloorplanOpen)
|
||||
// Mirror for callbacks that fire outside React's render (the per-frame
|
||||
// navigation-pose subscriber): when the 2D panel is hidden (`display:none` in
|
||||
// 3D mode) it must NOT re-render on every camera-zoom frame.
|
||||
const isFloorplanOpenRef = useRef(isFloorplanOpen)
|
||||
isFloorplanOpenRef.current = isFloorplanOpen
|
||||
const selectedReferenceId = useEditor((state) => state.selectedReferenceId)
|
||||
const setSelectedReferenceId = useEditor((state) => state.setSelectedReferenceId)
|
||||
const setMode = useEditor((state) => state.setMode)
|
||||
@@ -6441,6 +6446,13 @@ export function FloorplanPanel({
|
||||
|
||||
const syncFloorplanViewportToNavigationPose = useCallback(
|
||||
(pose: NavigationSyncPose) => {
|
||||
// Skip the viewport sync while the 2D panel is hidden (3D mode). It writes
|
||||
// React state (`setViewport`) that re-renders the whole floorplan SVG, so
|
||||
// doing it every camera-zoom frame for an invisible panel was a needless
|
||||
// per-frame stall. The catch-up effect below re-syncs on reopen.
|
||||
if (!isFloorplanOpenRef.current) {
|
||||
return
|
||||
}
|
||||
if (floorplanRotationStateRef.current) {
|
||||
return
|
||||
}
|
||||
@@ -6471,9 +6483,12 @@ export function FloorplanPanel({
|
||||
|
||||
latestNavigationSyncPoseRef.current = pose
|
||||
if (pose.source === '3d') {
|
||||
// Re-runs when the panel reopens (`isFloorplanOpen`) so the viewport
|
||||
// catches up to the camera after the per-frame sync was skipped while
|
||||
// hidden; a no-op while closed (the sync early-returns).
|
||||
syncFloorplanViewportToNavigationPose(pose)
|
||||
}
|
||||
}, [syncFloorplanViewportToNavigationPose])
|
||||
}, [isFloorplanOpen, syncFloorplanViewportToNavigationPose])
|
||||
|
||||
useEffect(() => {
|
||||
return useEditor.subscribe((state) => {
|
||||
|
||||
Reference in New Issue
Block a user