2121 * @ai -context: 3D 场景核心(R3F):SceneProvider。
2222 */
2323import { Canvas , useThree } from '@react-three/fiber' ;
24- import { Suspense , useEffect } from 'react' ;
24+ import { Suspense , useEffect , useLayoutEffect } from 'react' ;
2525import { Preload } from '@react-three/drei' ;
2626import { PerformanceMonitor } from './PerformanceMonitor' ;
2727import { QualityController } from './QualityController' ;
@@ -30,6 +30,7 @@ import { ContextRecovery } from './ContextRecovery';
3030import { useOrbitalStore } from '../navigation/OrbitalStore' ;
3131import { usePerformanceModeStore } from '@/lib/performance/usePerformanceMode' ;
3232import { PERFORMANCE_MODE_CONFIG } from '@/lib/performance/performanceMode' ;
33+ import { useSceneTheme } from '../hooks/useSceneTheme' ;
3334import * as THREE from 'three' ;
3435
3536interface SceneProviderProps {
@@ -39,23 +40,54 @@ interface SceneProviderProps {
3940}
4041
4142/**
42- * 渲染循环唤醒器 — 修复“多次切换功能后页面无渲染”
43+ * 主题感知雾效/背景 — 深色用深蓝雾,浅色用浅蓝雾
44+ * 确保主题切换时雾色和背景色同步更新
45+ */
46+ function ThemeAwareEnvironment ( ) {
47+ const { scene } = useThree ( ) ;
48+ const theme = useSceneTheme ( ) ;
49+
50+ useEffect ( ( ) => {
51+ if ( theme === 'deep-sea' ) {
52+ scene . fog = new THREE . FogExp2 ( '#0a0a2e' , 0.03 ) ;
53+ scene . background = new THREE . Color ( '#0a0a2e' ) ;
54+ } else {
55+ scene . fog = new THREE . FogExp2 ( '#e8f4f8' , 0.015 ) ;
56+ scene . background = new THREE . Color ( '#e8f4f8' ) ;
57+ }
58+ } , [ theme , scene ] ) ;
59+
60+ return null ;
61+ }
62+
63+ /**
64+ * 渲染循环唤醒器 — 修复"多次切换功能后页面无渲染"
4365 *
4466 * R3F 的循环在 frameloop='never' 下会 cancelAnimationFrame 彻底停止;
4567 * 切回 'always' 时 setFrameloop 只改 store 值,不会重启循环,
4668 * 而期间所有 invalidate() 又因 frameloop 尚为 'never' 被早退丢弃。
4769 * 因此在相位/档位迁移后(此时 frameloop prop 已应用)显式 invalidate:
4870 * 'always' 下 render 每帧返回 1,一次唤醒即可让循环永续运行;
4971 * docked('never')时 invalidate 自动早退,无副作用。
72+ *
73+ * 修复:使用 useLayoutEffect(同步执行,确保 frameloop prop 已挂载)
74+ * + setTimeout 兜底:React 18 中 useEffect 为异步微任务,frameloop
75+ * prop 可能在同一个微任务中尚未被 R3F 内部处理,导致 invalidate()
76+ * 因 frameloop 仍为 'never' 被早退丢弃。
5077 */
5178function LoopResumer ( ) {
5279 const invalidate = useThree ( ( s ) => s . invalidate ) ;
5380 const phase = useOrbitalStore ( ( s ) => s . phase ) ;
5481 const mode = usePerformanceModeStore ( ( s ) => s . mode ) ;
82+ const theme = useSceneTheme ( ) ;
5583
56- useEffect ( ( ) => {
84+ useLayoutEffect ( ( ) => {
85+ // 第一轮:同步执行,确保 frameloop 已挂载
5786 invalidate ( ) ;
58- } , [ phase , mode , invalidate ] ) ;
87+ // 第二轮:setTimeout 兜底,确保 React 事务完成、R3F 已处理 frameloop 变更
88+ const timer = setTimeout ( ( ) => invalidate ( ) , 0 ) ;
89+ return ( ) => clearTimeout ( timer ) ;
90+ } , [ phase , mode , theme , invalidate ] ) ;
5991
6092 return null ;
6193}
@@ -98,7 +130,7 @@ export function SceneProvider({ children, interactive = false }: SceneProviderPr
98130 gl . toneMappingExposure = 1.0 ;
99131 gl . shadowMap . enabled = true ;
100132 gl . shadowMap . type = THREE . PCFSoftShadowMap ;
101- scene . fog = new THREE . FogExp2 ( '#0a0a2e' , 0.03 ) ;
133+ // 注意: fog 和 background 由 ThemeAwareEnvironment 组件管理(主题感知)
102134 // GPU 诊断:打印实际渲染器名称,出现 SwiftShader 即说明落入软件渲染(硬件加速失效)
103135 const ctx = gl . getContext ( ) ;
104136 const dbgExt = ctx . getExtension ( 'WEBGL_debug_renderer_info' ) ;
@@ -111,6 +143,7 @@ export function SceneProvider({ children, interactive = false }: SceneProviderPr
111143 { /* FPS 自动降档仅在概览态测量(entering 飞行帧率不代表设备能力,docked 无帧率可言) */ }
112144 { phase === 'overview' && < PerformanceMonitor /> }
113145 < LoopResumer />
146+ < ThemeAwareEnvironment />
114147 < QualityController />
115148 < MemoryManager />
116149 < ContextRecovery />
0 commit comments