You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Three-Fiber实现容器尺寸变化时动态更新Canvas尺寸

问题:React Three Canvas 在CSS动画容器中尺寸更新延迟,无法实时响应

我正在开发一个应用,将react-three-fiber的Canvas嵌套在带有CSS动画的div中。目前遇到的问题是:Canvas的尺寸只有在过渡或动画完成后才会更新,我希望它能像监听窗口resize事件那样实时响应容器尺寸变化。

Canvas尺寸更新延迟演示

代码示例:

<div id={ s.UserView }>
    <Canvas 
        shadows={ false } 
        orthographic 
        camera={{ position: [ 0, 0, 200 ] }} 
        ref={ SceneRef } 
        dpr={ DPR } 
        gl={{ outputColorSpace: THREE.SRGBColorSpace, toneMapping: THREE.NoToneMapping }}
    >
        <MapCam targetRef={ containerRef } HUDRef={ HUDRef }  sceneFrame={ data.frame } sceneGrids={ data.grids }/>
        <SceneRoot setCursor={ setCursor } mapFilters={ mapFilters } cells={ data.grids } cellsIndex={ cellsIndex }/>
    </Canvas>
    <HUD passRef={ HUDRef }/>
</div>

解决方案

核心思路

react-three-fiber的Canvas默认只监听窗口resize事件,不会追踪容器在CSS动画/过渡中的实时尺寸变化。我们需要手动监听容器尺寸,并强制Canvas同步更新。

方案1:用ResizeObserver实时监听容器尺寸

ResizeObserver可以精准捕捉元素尺寸的每一步变化,包括动画过程中的动态调整。

步骤:

  1. 确保包裹Canvas的div已绑定ref(比如代码里的containerRef)
  2. 组件挂载后创建ResizeObserver实例,监听容器
  3. 尺寸变化时,通过Canvas的ref获取renderer和相机,手动更新尺寸与投影矩阵

代码示例:

import { useEffect, useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import * as THREE from 'three';

function YourComponent() {
    const containerRef = useRef(null);
    const SceneRef = useRef(null);

    useEffect(() => {
        const observer = new ResizeObserver(() => {
            if (!containerRef.current || !SceneRef.current) return;
            
            const { width, height } = containerRef.current.getBoundingClientRect();
            const renderer = SceneRef.current.gl;
            const camera = SceneRef.current.camera;

            // 更新渲染器尺寸
            renderer.setSize(width, height);
            // 针对正交相机更新投影矩阵(你的场景用的是正交相机)
            if (camera.isOrthographicCamera) {
                camera.left = -width / 2;
                camera.right = width / 2;
                camera.top = height / 2;
                camera.bottom = -height / 2;
                camera.updateProjectionMatrix();
            }
        });

        observer.observe(containerRef.current);
        return () => observer.disconnect();
    }, []);

    return (
        <div ref={containerRef} id={s.UserView}>
            <Canvas 
                shadows={false} 
                orthographic 
                camera={{ position: [0, 0, 200] }} 
                ref={SceneRef} 
                dpr={DPR} 
                gl={{ outputColorSpace: THREE.SRGBColorSpace, toneMapping: THREE.NoToneMapping }}
            >
                <MapCam targetRef={containerRef} HUDRef={HUDRef} sceneFrame={data.frame} sceneGrids={data.grids}/>
                <SceneRoot setCursor={setCursor} mapFilters={mapFilters} cells={data.grids} cellsIndex={cellsIndex}/>
            </Canvas>
            <HUD passRef={HUDRef}/>
        </div>
    );
}

方案2:结合useFrame钩子确保实时同步

如果ResizeObserver效果不够理想,可以用useFrame在每一帧检查容器尺寸,确保Canvas完全同步:

import { useFrame } from '@react-three/fiber';

// 在组件内部添加
useFrame(() => {
    if (!containerRef.current || !SceneRef.current) return;
    
    const { width, height } = containerRef.current.getBoundingClientRect();
    const renderer = SceneRef.current.gl;
    const camera = SceneRef.current.camera;

    // 仅在尺寸真的变化时更新,避免不必要的性能消耗
    if (renderer.domElement.width !== width || renderer.domElement.height !== height) {
        renderer.setSize(width, height);
        if (camera.isOrthographicCamera) {
            camera.left = -width / 2;
            camera.right = width / 2;
            camera.top = height / 2;
            camera.bottom = -height / 2;
            camera.updateProjectionMatrix();
        }
    }
});

注意事项

  • 必须确保containerRef正确绑定到包裹Canvas的div上
  • 正交相机和透视相机的投影矩阵更新逻辑不同,根据你的场景选择(你用的是正交相机,重点处理该逻辑即可)
  • 使用useFrame时添加尺寸判断,能减少不必要的重绘,优化性能

内容的提问来源于stack exchange,提问作者Joseph Ghaida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:32:48