【问题标题】:React + Three.js canvas does not resize correctly when making the browser window smallerReact + Three.js 画布在缩小浏览器窗口时无法正确调整大小
【发布时间】:2020-04-26 15:43:42
【问题描述】:

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

我想要一个具有固定宽度侧边栏的视口。我正在使用一个 flexbox 行容器来执行此操作,该容器围绕一个侧边栏组件(一个设置了最小宽度的简单 div)和响应式 three.js 组件。

将窗口调整得更大可以正常工作,画布会适当地填充浏览器窗口。但是,当将窗口大小调整为较小时,画布不会正确地重新计算与静态宽度侧边栏剩余的可用空间相关的大小,从而导致画布不能完全适合浏览器并引入滚动条。

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

根据 StackOverflow 上许多答案的建议,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);
}

我错过了什么?

【问题讨论】:

  • 为什么要在包含一个项目的 div 上使用 display:flex,即画布?这个 div 不是已经在带有display:flex 的 div 中了吗(我们无法从您的代码中看到。)。如果是这种情况,那么我希望包含画布的 div 具有 flexbox 项 css,例如。 flex-basis, flex-grow etc.,不是display:flex
  • 父 div 确实是 display:flex。在视口 div 上没有 display:flex 时,three.js 画布在编译或整页刷新时不会完全填满容器。将其切换到 flex item css 实际上会使重新调整大小变得更糟。这很古怪。我确定这很微妙,我只是还不知道是什么。

标签: reactjs three.js autoresize


【解决方案1】:

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

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

This Fiddle
我在示例中添加了渲染和动画,因此可以很容易地看到结果。

希望这会有所帮助。

【讨论】:

  • 您的代码在 Fiddle 中运行,但在我的 react 应用程序中仍然无法运行。神奇的子弹最终成为您添加的oveflow: hidden。这让我找到了一个找到这个答案的搜索路径:stackoverflow.com/questions/36247140/…。最终将min-width:0min-height: 0 添加到我的CSS 重置器*{ } 并解决了所有问题。
【解决方案2】:

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

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

【讨论】:

  • 我试过了。不幸的是,这并没有什么不同。结果完全一样。
猜你喜欢
  • 2016-12-18
  • 2022-12-19
  • 2023-04-07
  • 1970-01-01
  • 2012-02-23
  • 2015-04-20
  • 1970-01-01
  • 1970-01-01
  • 2021-04-06
相关资源
最近更新 更多