上节把家具在 2D 视图画了出来。
并且 2D 视图编辑会同步 3D 视图:

3D 视图编辑会同步 2D 视图:

但我们需要分别切换 2D、3D 视图,不能同时看。
而在酷家乐里,3D 视图会在右上角展示 2D 视图:

2D 视图会在右上角展示 3D 视图:

我们也来实现这种交互。
其实也很简单,之前是切换显示隐藏,修改的是 display 样式:

现在改为修改宽高。
我们除了要改 div 的宽高外,还要改 threejs 渲染的 canvas 的宽高:

所以我们要在 threejs 初始化的时候暴露一个改变宽高的方法:

function changeSize(isBig: boolean) {
if(isBig) {
const width = window.innerWidth;
const height = window.innerHeight - 60;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
} else {
const width = 240;
const height = 200;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
就是如果 isBig 是 true 就把画布大小设置为窗口宽高,否则设置 240 * 200
把之前设置 display 的逻辑去掉:


然后调用下 changeSize 方法:

const changeSize3DRef = useRef<(isBig: boolean) => void>(null);
changeSize3DRef.current = changeSize;
调用下:

useEffect(() => {
const changeSize3D = changeSize3DRef.current!;
changeSize3D(false);
}, []);
改了宽高还得改下样式:

#threejs-3d-container {
position: absolute;
z-index: 2;
right: 0;
}
#threejs-2d-container {
position: absolute;
z-index: 1;
right: 0;
}
把 3D 的 z-index 改大点,然后 right 为 0,在右上角。

没啥问题。
canvas 宽高设置好了,div 没有设置宽高,会由内容撑开,所以也是一样的宽高:


所以我们只需要在点击按钮的时候,修改两个 canvas 的宽高就好了。
在 2D 的初始化代码里也加上这个方法:

function changeSize(isBig: boolean) {
if(isBig) {
const width = window.innerWidth;
const height = window.innerHeight - 60;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
} else {
const width = 240;
const height = 200;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
也同样接收一下:


const changeSize2DRef = useRef<(isBig: boolean) => void>(null);
changeSize2DRef.current = changeSize;
之前有一个状态记录了当前视图:

把它提到上面来。
然后把设置 size 的移到最后,因为要等那两个场景初始化完才能拿到这俩方法:

curMode 变化的时候,修改两个视图的 size,并且还要改下 index。
useEffect(() => {
const changeSize3D = changeSize3DRef.current!;
const changeSize2D = changeSize2DRef.current!;
if(curMode === '2d') {
changeSize3D(false);
changeSize2D(true);
} else {
changeSize3D(true);
changeSize2D(false);
}
}, [curMode]);
<div id="threejs-3d-container" style={{ zIndex: curMode ==='2d' ? 2 : 1}}></div>
<div id="threejs-2d-container" style={{ zIndex: curMode ==='3d' ? 2 : 1}}></div>
试下效果:

切换 2D 视图的时候,3D 视图在右上角。
切换 3D 视图的时候,2D 视图在右上角。
但有个问题:

当视图变小之后,家具就没法编辑了。
还有一个是 resize 的时候,小视图突然变大了:

这俩问题都是因为我们之前都是用的大的 width、height:

我们在窗口 resize 的时候,拿到 canvas 大小,如果是 200,那就不变了:

const size = renderer.getSize(new THREE.Vector2());
if(size.y === 200) {
return;
}
另一个视图也是这么改:

再试下:

现在窗口 resize 的时候,就只有大的视图会重新设置 canvas 大小了。
再来解决小视图没法编辑家具位置的问题:


const { x: width, y: height} = renderer.getSize(new THREE.Vector2());
同样的方式,width、height 改成动态取的 canvas 宽高就可以了。
试一下:


现在就可以操作小视图了。
案例代码上传了小册仓库
总结
这节我们实现了大小视图。
之前切换 2D、3D 视图的时候是修改 display 来实现显示隐藏。
现在是切换 canvas 的 width、height 和 z-index。
我们在 threejs 初始化的时候暴露了一个修改 size 的方法给组件里调用,切换视图的时候调用来设置 canvas 的 size。
此外,要注意窗口 resize 的时候,处理鼠标事件的时候都要改为动态获取 width、height 才可以。