Skip to content

126. 实战:酷家乐装修编辑器(三十)

Published:

上节实现了基本的全景浏览功能:

2025-07-29 18.26.04.gif

2025-07-29 20.33.36.gif

这节我们来优化一下。

首先,酷家乐这个预览,窗外是有风景的:

2025-07-29 20.41.09.gif

其实就是一个全景图。

我们现在窗外是黑乎乎的一片。

找个全景图:

https://github.com/QuarkGluonPlasma/threejs-course-code/blob/b5488ee334c7c6689f7a42c2d23c0fbf1297ac73/hdr-background/public/pic.hdr

image.png

下载下来放到 public 目录:

image.png

代码里加载下:

image.png

const rgbeloader = new RGBELoader();

rgbeloader.load('./pic.hdr', function ( texture ) {
    texture.mapping = THREE.EquirectangularReflectionMapping;
    scene.background = texture;
});

2025-07-29 20.55.51.gif

这样,窗外就有风景了。

现在转的有点慢:

2025-07-30 10.21.37.gif

调一下速度:

image.png

2025-07-30 10.22.56.gif

这样角度转的就快了。

然后我们实现下家具的删除。

store 加一个方法:

image.png

image.png

deleteFurniture(id: string): void;
deleteFurniture(id) {
    set(state => {
        return {
            ...state,
            data: {
                ...state.data,
                furnitures: state.data.furnitures.filter(item => {
                    return item.id !== id
                })
            }
        }
    })
}

然后我们要知道当前选中的家具是哪个,所以还要加一个 curSelectedFurniture 的属性:

image.png

curSelectedFurniture: Furniture | null
setCurSelectedFurniture(furnitureId: string): void;

image.png

curSelectedFurniture: null,
setCurSelectedFurniture(furnitureId) {
    set(state => {
        const found = state.data.furnitures.filter((item)=> {
            return item.id === furnitureId;
        });

        return {
            ...state,
            curSelectedFurniture: found.length ? found[0] : null
        }
    })
}

传入家具 id,找到对应的家具,设置到 curSelectedFurniture

然后点击选中物体的时候,如果是家具,就调用这个方法:

我们把 furniture.id 设置到了 name:

image.png

所以点击的时候就取 name 来设置:

image.png

image.png

setCurSelectedFurniture: Action['setCurSelectedFurniture']

点击的时候调用下:

image.png

setCurSelectedFurniture(obj.target.name);
setCurSelectedFurniture('');

我们在 Properties 组件里把它可视化展示一下:

image.png

JSON.stringify(curSelectedFurniture, null, 4)

试下效果:

2025-07-30 15.38.03.gif

可以看到,点击不同家具,确实选中的家具信息变了。

同样的方式来加一下 2D 场景的选中逻辑:

image.png

image.png

setCurSelectedFurniture: Action['setCurSelectedFurniture']

image.png

setCurSelectedFurniture(obj.target.name);
setCurSelectedFurniture('');

试一下:

2025-07-30 15.42.30.gif

也没问题。

接下来实现下删除:

image.png

useEffect(() => {
    const scene = scene3DRef.current!;
    function handleKeydown(e: KeyboardEvent) {
        if(e.key === 'Backspace') {
            if(curSelectedFurniture) {
                const furniture = scene.getObjectByName(curSelectedFurniture.id);

                if(furniture) {
                    furniture.parent?.remove(furniture);
                    deleteFurniture(furniture.name);
                    setCurSelectedFurniture('');
                }
            }
        }
    }
    window.addEventListener('keydown', handleKeydown);
    return () => {
        window.removeEventListener('keydown', handleKeydown);
    }
}, [curSelectedFurniture]);

监听键盘事件,当按下删除键的时候,找到 curSelectedFurniture 对应的对象删除,并且在 store 里删除。

试一下:

2025-07-30 19.40.58.gif

可以看到,家具确实删除了。

但是 TransformControls 没有 detach。

我们导出一个 detach 方法:

image.png

function detachTransformControls() {
    transformControls.detach();
}

然后组件里接收下:

image.png

image.png

const detachTransformControls3DRef = useRef<() => void>(null);
detachTransformControls3DRef.current = detachTransformControls;

删除的时候调用下:

image.png

detachTransformControls3DRef.current?.();

再试试:

2025-07-30 20.29.38.gif

现在删除之后就 detach 了。

案例代码上传了小册仓库

总结

这节我们给预览的场景里加上了全景图,窗外就有风景了。

然后实现了选中后按 delete 键删除家具。

现在删除家具还有一些问题,下节我们继续完善。

评论