在缩小浏览器 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。
我在示例中添加了渲染和动画,以便可以轻松查看结果。
希望这对您有所帮助。
我正在使用 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。
我在示例中添加了渲染和动画,以便可以轻松查看结果。
希望这对您有所帮助。