# 优雅的 WebGL 上下文稳定性检测方案 ## 问题:固定延迟的缺陷 - 100ms可能不够,某些设备可能需要更长时间 - 浪费时间,某些设备可能很快就稳定了 - 不够精确,无法确保真正的稳定性 ## 优雅解决方案 ### 方案1:WebGL 上下文状态检测 + 轮询 ```typescript const waitForWebGLStable = async (canvas: HTMLCanvasElement, timeout = 5000): Promise => { const startTime = Date.now(); return new Promise((resolve) => { const checkStability = () => { const gl = canvas.getContext('webgl') || canvas.getContext('webgl2'); if (!gl) { if (Date.now() - startTime > timeout) { resolve(false); return; } setTimeout(checkStability, 16); // ~60fps return; } // 检查 WebGL 状态 const isStable = gl.getError() === gl.NO_ERROR && gl.getParameter(gl.RENDERER) && gl.getParameter(gl.VERSION); if (isStable) { resolve(true); } else if (Date.now() - startTime > timeout) { resolve(false); } else { setTimeout(checkStability, 16); } }; checkStability(); }); }; ``` ### 方案2:Cornerstone 渲染引擎就绪检测 ```typescript const waitForViewportReady = async (viewport: any, timeout = 5000): Promise => { const startTime = Date.now(); return new Promise((resolve) => { const checkReady = () => { try { // 检查 viewport 是否完全初始化 const canvas = viewport.canvas; const element = viewport.element; if (!canvas || !element) { if (Date.now() - startTime > timeout) { resolve(false); return; } setTimeout(checkReady, 16); return; } // 检查渲染状态 const renderingState = viewport.getRenderingEngine?.()?.hasBeenDestroyed; if (renderingState === false) { resolve(true); } else if (Date.now() - startTime > timeout) { resolve(false); } else { setTimeout(checkReady, 16); } } catch (error) { if (Date.now() - startTime > timeout) { resolve(false); } else { setTimeout(checkReady, 16); } } }; checkReady(); }); }; ``` ### 方案3:组合方案 - 事件驱动 + 状态检测 ```typescript const [isViewportReady, setIsViewportReady] = useState(false); // 在创建 viewport 后检测就绪状态 useEffect(() => { if (!stackViewport) return; const checkViewportReady = async () => { try { // 方法1:等待canvas元素存在 const canvas = viewportRef.current?.querySelector('canvas'); if (!canvas) { setTimeout(checkViewportReady, 16); return; } // 方法2:等待WebGL上下文稳定 const isWebGLStable = await waitForWebGLStable(canvas); if (!isWebGLStable) { console.warn('[DcmCell] WebGL上下文未稳定'); return; } // 方法3:等待viewport就绪 const isViewportStable = await waitForViewportReady(stackViewport); if (!isViewportStable) { console.warn('[DcmCell] Viewport未就绪'); return; } console.log('[DcmCell] Viewport完全就绪'); setIsViewportReady(true); } catch (error) { console.error('[DcmCell] 检测viewport就绪状态失败:', error); } }; checkViewportReady(); }, [stackViewport]); // 只有当viewport就绪后才加载图像 useEffect(() => { if (!isViewportReady || !stackViewport) return; const loadImageFromProps = async () => { // 避免重复加载相同图像 if (imageId === currentImageId) return; if (imageId && imageId.trim() !== '') { console.log(`[DcmCell] Viewport就绪,开始加载图像: ${imageId}`); const success = await loadImage(imageId, 'props'); if (success) { setCurrentImageId(imageId); } } else { // 清空逻辑... } }; loadImageFromProps(); }, [imageId, isViewportReady, stackViewport]); ``` ### 方案4:简化版 - requestAnimationFrame ```typescript const waitForStableFrame = (): Promise => { return new Promise(resolve => { // 等待2-3帧确保渲染稳定 requestAnimationFrame(() => { requestAnimationFrame(() => { requestAnimationFrame(resolve); }); }); }); }; useEffect(() => { const loadImageFromProps = async () => { if (!stackViewport) return; // 等待几个渲染帧确保稳定 await waitForStableFrame(); // 避免重复加载相同图像 if (imageId === currentImageId) return; // 继续正常的加载逻辑... }; loadImageFromProps(); }, [imageId, stackViewport]); ``` ## 推荐实施顺序 1. 首先尝试方案4(最简单,通常有效) 2. 如果仍有问题,升级到方案3(完整的状态检测) 3. 方案1和2作为底层工具函数支持