上节实现了拖拽家具列表中的家具到房子的任意位置:

有两个问题:
- 因为我们去掉了房子位置的修改,现在相机的焦点不是房子中心
- 现在是写死的家具,并不是拖拽不同家具就添加对应的家具
我们先来解决第一个问题。
如何让相机的焦点在房子中间呢?
其实有两种方案:
第一种就是用 Box3 包围盒拿到房子大小,然后修改房子位置到 0,0,0 就好了,相机焦点默认在 0,0,0
另一方方案是房子位置不变,修改相机的焦点为房子的中心位置。
第一种方式之前用过,现在我们用修改相机焦点的方式再来实现这个功能。
要注意的是,相机的焦点 camera.lookAt 要和 OrbitControls 的 target 同时修改才行。
所以我们这里导出 OrbitControls:

组件里接收下:


const controls3DRef = useRef<OrbitControls>(null);
controls3DRef.current = controls;
然后在之前修改房子位置的地方改成修改相机焦点位置:

camera3DRef.current?.lookAt(center.x, 0, center.z);
controls3DRef.current?.target.set(center.x, 0, center.z);
试一下:
之前是这样:

现在是这样:

现在相机焦点又移到房子中央了,但这次不是在 0,0,0 的位置。
添加家具的功能正常:

然后来解决拖拽不同家具的对应处理。
这个其实就是在 useDrag 里加一个上下文数据就行:

const [, drag]= useDrag({
type: '家具',
item: {
modelUrl: props.title === '床' ? './bed.glb' : './dining-table.glb'
},
});
item 就是这个上下文数据。
然后在 useDrop 的时候,就直接用这个 url 来加载模型:

item: { modelUrl: string }
modelUrl: item.modelUrl
还要处理下 scale 的问题:

modelScale: item.modelUrl.includes('bed.glb') ? 800 : 1,
试一下:
清空两个户型的默认家具:

然后切换户型:


这样,拖拽家具到 3D 场景中就完成了。
不过 2D 视图没有同步更新。
和之前同样的问题:

加上 else 的处理,注意 2D 视图位置是反的。
else {
const gltfLoader = new GLTFLoader();
const furnitures = houseObj.getObjectByName('furnitures')!;
gltfLoader.load(furniture.modelUrl, (gltf) => {
furnitures.add(gltf.scene);
gltf.scene.scale.setScalar(furniture.modelScale || 1);
gltf.scene.position.set(
-furniture.position.x,
-furniture.position.y,
-furniture.position.z
);
gltf.scene.rotation.x = furniture.rotation.x;
gltf.scene.rotation.y = furniture.rotation.y;
gltf.scene.rotation.z = furniture.rotation.z;
gltf.scene.traverse(obj => {
(obj as any).target = gltf.scene;
});
gltf.scene.name = furniture.id
});
}
顺便把坐标轴都去一下:



看下效果:


这样,拖拽家具到 3D 场景的功能就完成了。
案例代码上传了小册仓库
总结
这节我们修复了两个问题。
第一个是相机焦点设置到房子中心的问题,这个可以移动房子位置,也可以移动相机焦点,我们用的第二种方案,设置 camera.lookAt 和 controls.target
第二个是拖拽不同家具的时候,添加对应家具的功能,这个是在 useDrag 的时候通过 item 传递数据,然后 useDrop 的时候取出 item 加载对应模型就好了。
当然,目前我们只实现了拖拽家具到 3D 视图,2D 视图也是可以实现拖拽的,原理一样。