Skip to content

114. 实战:酷家乐装修编辑器(十八)

Published:

上节做了家具导入:

2025-06-28 22.11.24.gif

这节我们来做编辑。

其实和之前的 Three.js Editor 一样:

2025-05-17 23.48.34.gif

只不过不支持放缩,家具大小是固定的。

酷家乐的家具编辑也是这样:

2025-06-29 11.21.20.gif

只不过它是用自己写的控件,我们直接用 TransformControls 也是一样。

image.png

引入 TransformControls,把它 的 helper 添加到场景中。

然后 TransformControls 拖动的时候,禁用 OrbitControls,不然两者操作冲突。

const transformControls = new TransformControls(camera, renderer.domElement);
const transformHelper = transformControls.getHelper();
scene.add(transformHelper);

transformControls.addEventListener('dragging-changed', function (event) {
    controls.enabled = !event.value;
});

然后点击的时候,把 TransformControls 给 attach 到目标对象上。

我们给所有的家具单独一个 group:

image.png

const furnitures = new THREE.Group();
furnitures.name = 'furnitures';
data.furnitures.forEach(furniture => {
    const gltfLoader = new GLTFLoader();
    gltfLoader.load(furniture.modelUrl, (gltf) => {
        furnitures.add(gltf.scene);
        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;
    });
})
house.add(furnitures);

这样点击的时候方便查找。

然后给模型的所有子对象标记 target,点击的时候可以找到正确的对象:

image.png

gltf.scene.traverse(obj => {
    (obj as any).target = gltf.scene;
})

然后来处理下点击:

image.png

const furnitures = scene.getObjectByName('furnitures')!;
const intersections2 = rayCaster.intersectObjects(furnitures.children);

if(intersections2.length) {
    const obj = intersections2[0].object as any;
    if(obj.target) {
        transformControls.attach(obj.target);
    }
} else {
    transformControls.detach();
}

找到所有家具,点击的时候和这些对象做检测。

如果点中了家具对象,就把 TransformControls 给 attach 上,否则 detach

试下效果:

2025-06-29 13.12.48.gif

除了位移,还要做旋转:

2025-06-29 13.43.29.gif

酷家乐的这个控件是自己写的,可以一起做平移、旋转。

而我们用 TransformControls 需要切换 mode:

image.png

transformControls.mode = 'rotate';
transformControls.showX = false;
transformControls.showZ = false;

切换到 rotate 的 mode,然后禁用 X、Z 方向的旋转。

看下效果:

2025-06-29 13.46.30.gif

这样就只能做围绕 y 轴的旋转了。

我们导出个 changeMode 方法:

image.png

function changeMode(isTranslate: boolean) {
    if(isTranslate) {
        transformControls.mode = 'translate';
        transformControls.showX = true;
        transformControls.showZ = true;
    } else {
        transformControls.mode = 'rotate';
        transformControls.showX = false;
        transformControls.showZ = false;
    }
}

组件里接收下:

image.png

const changeModeRef = useRef<(isTranslate: boolean) => void>(null);
changeModeRef.current = changeMode;

把切换按钮先放在之前 2D、3D 的按钮旁边:

image.png

<Button
    onClick={() => changeModeRef.current?.(true)}
    >平移</Button>
<Button
    onClick={() => changeModeRef.current?.(false)}
    >旋转</Button>

2025-06-29 14.00.21.gif

这样,家具的平移、旋转就完成了。

案例代码上传了小册仓库

总结

这节我们实现了家具的平移、旋转操作。

用 TransformControls 来做平移、旋转,点击家具的时候,把它 attach 到目标家具,没点中家具就 detach。

这里为了方便查找,我们把所有家具单独放在一个 group里,并给这个 group 一个 name。点击的时候,射线和这个 group 里的家具做相交判断。

并且我们加了两个按钮来切换 translate、rotate 两种操作模式。

平移、旋转实现了,下节我们继续完善编辑功能。

评论