Skip to content

90. 实战:Three.js Editor(七)

Published:

上节我们实现了物体位置的编辑:

2025-05-14 06.44.26.gif

Three.js Editor 里还支持旋转、放缩的编辑:

2025-05-14 07.10.04.gif

点击左侧的按钮,可以切换不同的编辑模式:

2025-05-14 07.12.58.gif

这节我们来实现这些按钮以及对应的编辑功能。

先来写下这些按钮:

image.png

直接用 antd 的悬浮按钮就行:

image.png

改下 Main/index.jsx

image.png

import { FloatButton } from "antd";
import { ArrowsAltOutlined, DragOutlined, RetweetOutlined } from "@ant-design/icons";
return <div className="Main">
    <div id="threejs-container"></div>
    <FloatButton.Group className="btn-group">
        <FloatButton icon={<DragOutlined />}/>
        <FloatButton icon={<RetweetOutlined />}/>
        <FloatButton icon={<ArrowsAltOutlined />} />
    </FloatButton.Group>
</div>

创建三个按钮。

安装下用到的图标库:

npm install --save @ant-design/icons

改下 App.scss 的样式:

image.png

position: relative;
.btn-group {
  width: 100px;
  height: 200px;
  position:absolute;
  left: 0;
  top:0;
}

设置宽、高、绝对定位。

看下效果:

image.png

接下来就可以实现三种编辑模式了。

image.png

这个就是修改 TransformControls 的 mode

点不同的按钮设置不同的 mode 就好了。

我们封装一个方法返回:

image.png

function setTransformControlsMode(mode) {
    transformControls.setMode(mode);
}

return {
    scene,
    setTransformControlsMode
}

在组件里用 useRef 来保存:

image.png

const transformControlsModeRef = useRef();
transformControlsModeRef.current = setTransformControlsMode;

点击按钮的时候调用下:

image.png

function setMode(mode) {
    transformControlsModeRef.current(mode);
}

return <div className="Main">
    <div id="threejs-container"></div>
    <FloatButton.Group className="btn-group">
        <FloatButton icon={<DragOutlined />} onClick={() => setMode('translate')}/>
        <FloatButton icon={<RetweetOutlined />} onClick={() => setMode('rotate')}/>
        <FloatButton icon={<ArrowsAltOutlined />} onClick={() => setMode('scale')}/>
    </FloatButton.Group>
</div>

试下效果:

2025-05-14 07.47.30.gif

这样,三种编辑模式的切换就完成了。

但 json 里只保存了位置,并没有保存旋转和缩放的数据:

image.png

我们来加一下:

image.png

image.png

分别在 createBox、createCylinder 里加一下 scale 和 rotation 数据:

scale: {
    x: 1,
    y: 1,
    z: 1
},
rotation: {
    x: 0,
    y: 0,
    z: 0
}

在渲染的时候设置下:

image.png

image.png

mesh.scale.copy(scale)
mesh.rotation.x = rotation.x;
mesh.rotation.y = rotation.y;
mesh.rotation.z = rotation.z;

然后在用 TransformControls 编辑的时候,把 position、scale、ratation 都更新到 json。

rotation 需要分别设置 x、y、z

把 updatePosition 方法改造下:

image.png

根据不同的 type 来分别修改 position、scale、rotation

updateMeshInfo(name, info, type) {
    set(state => {
        return {
            data: {
                ...state.data,
                meshArr: state.data.meshArr.map(mesh => {
                    if(mesh.name === name) {
                        if(type === 'position') {
                            mesh.props.position = info;
                        } else if(type === 'scale') {
                            mesh.props.scale = info;
                        } else if(type === 'rotation') {
                            mesh.props.rotation = {
                                x: info.x,
                                y: info.y,
                                z: info.z
                            }
                        }
                    }
                    return mesh;
                })
            }
        }
    })
}

改下用的方法:

image.png

然后编辑的时候根据 mode 修改不同的数据:

image.png

transformControls.addEventListener('change', () => {
    const obj = transformControls.object;
    if(obj) {
        if(transformControls.mode === 'translate') {
            updateMeshInfo(obj.name, obj.position, 'position');
        } else if(transformControls.mode === 'scale') {
            updateMeshInfo(obj.name, obj.scale, 'scale');
        } else if(transformControls.mode === 'rotate'){
            updateMeshInfo(obj.name, obj.rotation, 'rotation');
        }
    }
});

试下效果:

2025-05-14 08.57.44.gif

这样,旋转、缩放的编辑就完成了。

案例代码上传了小册仓库

总结

这节我们实现了旋转、缩放的编辑功能。

原理就是 TransformControls 设置 translate、rotate、scale 三种 mode。

我们加了三个按钮,点击的时候来切换 mode。

并且在编辑的时候根据 mode 来分别把数据同步到 json 中。

这样,位移、旋转、缩放的编辑功能就都完成了。

评论