在缩小浏览器 window 时,React + Three.js canvas 无法正确调整大小

React + Three.js canvas does not resize correctly when making the browser window smaller

我正在使用 React 和 three.js 在浏览器中显示一些 3D 模型。只要 three.js 组件是页面上唯一具有可见内容的元素,一切都可以正常工作。 three.js canvas 将调整大小就好了。您可以将它包裹在其他容器中,它仍然可以正常工作。当另一个元素或组件作为同级添加时,问题就开始了。

我想要一个带有固定宽度边栏的视口。我正在用一个 flexbox 行容器来做这个,它包裹着一个侧边栏组件(一个简单的 div 设置了最小宽度),以及响应式 three.js 组件。

将 window 调整得更大一些效果很好,canvas 适当地填充浏览器 window。但是,当将 window 调整得更小时,canvas 不会正确地重新计算它相对于静态宽度侧边栏剩余的可用 space 的大小,从而导致 canvas 不完全适合浏览器并引入滚动条。

canvas 确实缩小了一些,但不足以将其完全保留在浏览器中 window。如果组件位于 three.js 组件上方或下方,则垂直方向会出现相同的问题。如果您在浏览器 window 较小时重新编译应用程序,刷新后的视图将 canvas 适当调整大小。

根据 Whosebug 上许多答案的建议,three.js 反应代码看起来像这样:

export class Viewport extends React.Component {

  componentDidMount() {

    const width = this.mount.clientWidth;
    const height = this.mount.clientHeight;
    window.addEventListener("resize", this.handleWindowResize);

    // setup scene
    this.scene = new THREE.Scene();

    //setup camera
    this.camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );
    this.camera.position.set( 0, 5, 10 );

    // setup rendering
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setClearColor('#666666');
    this.renderer.setSize(width, height, false);
    this.mount.appendChild(this.renderer.domElement);

    // setup geo
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({ color: '#433F81' });
    this.cube = new THREE.Mesh(geometry, material);
    this.scene.add(this.cube);
    ...

  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.handleWindowResize);
    this.mount.removeChild(this.renderer.domElement);
  }

  handleWindowResize = () => {
    const width = this.mount.clientWidth;
    const height = this.mount.clientHeight;

    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();

    this.renderer.setSize(width, height, false);
  };

  render() {
    return (
      <div className="viewport" style={{display: 'flex', width: "100%", height: "100%"}} ref={(mount) => { this.mount = mount }} />
    );
  }
}

样式很简单 css flexbox,元素设置为全宽和全高 (100%),侧边栏设置了最小宽度。

.flex-row-container {
  display: flex;
  flex-flow: row;
  height: 100%;
  width: 100%;
}

.sidebar {
  display: block;
  min-width: 250px;
  background-color: var(--color-mid-gray);
  margin: 3px 4px 3px 4px;
  box-sizing: border-box;
  border: 1px solid var(--color-mid-gray);
  outline: 3px solid var(--color-mid-gray);
}

我错过了什么?

您使用 element.clientWidth 可能会得到不正确的值。您是否尝试过使用 element.getBoundingClientRect()

const rect = this.mount.getBoundingClientRect();
const width = rect.width;
const height = rect.height;

所以在评论之后,我想我已经按照你想要的方式工作了。

基本上,
我删除了视口上的设置 display:flex,因为这是具有 display:flex 的元素(flex-row-container div)的子元素,它不需要它,但确实需要 flex:1 1 auto 设置它在 flexbox 中的显示方式(我在 css 而不是 JSX 中做的)。
然后问题变成视口 div 会扩展,但不会收缩(因为它包含 canvas),因此添加 overflow:hidden 解决了这个问题。
进行这些更改后,this.mount.clientWidth 将获得所需的结果,然后可用于设置三个 canvas 大小。

参见 This Fiddle
我在示例中添加了渲染和动画,以便可以轻松查看结果。

希望这对您有所帮助。