Skip to content

89. 实战:Three.js Editor(六)

Published:

Three.js Editor 里可以对物体做移动、旋转、缩放:

2025-04-28 19.12.54.gif

这个是通过 TransformControls 来实现的。

我们来写一下:

创建 TransformControls:

image.png

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

function render(time) {
    composer.render();
    // renderer.render(scene, camera);
    transformControls.update(time);
    requestAnimationFrame(render);
}

创建 TransformControls,把它的 helper 添加到 scene。

每次渲染循环调用下 update。

然后点击物体的话就 attach 上这个物体,否则就 detach:

image.png

这里我们把点击的范围精确了一下,只有 Box、Cylinder 开头的物体可以点击,其余的物体比如 GridHelper 之类的不处理点击事件

const objs = scene.children.filter(item => {
    return item.name.startsWith('Box') || item.name.startsWith('Cylinder')
})
const intersections = rayCaster.intersectObjects(objs);

if(intersections.length) {
  const obj = intersections[0].object;
//   obj.material.color.set('green');
    outlinePass.selectedObjects = [obj];
    onSelected(obj);
    transformControls.attach(obj);
} else {
    outlinePass.selectedObjects = [];
    onSelected(null);
    transformControls.detach();
}

把 OrbitControls 禁用掉,因为它的鼠标交互和 TransformControls 是冲突的:

image.png

试下效果:

2025-05-14 06.40.30.gif

这样,添加两个物体之后,就可以用 TransformControls 来移动位置了。

但移动之后,再添加一个物体,所有的物体都会到 0,0,0 的位置。

2025-04-29 00.07.55.gif

因为我们并没有把位置更新到 store。

在 change 的时候打印下 attach 到的对象的位置:

image.png

transformControls.addEventListener('change', () => {
    const obj = transformControls.object;
    if(obj) {
        console.log(obj.name, obj.position);
    }
});

2025-04-29 00.17.23.gif

然后把它更新到 store。

store 里添加一个方法:

image.png

updateMeshPosition(name, position) {
    set(state => {
        return {
            data: {
                ...state.data,
                meshArr: state.data.meshArr.map(mesh => {
                    if(mesh.name === name) {
                        mesh.props.position = position;
                    }
                    return mesh;
                })
            }
        }
    })
}

根据 name 查找目标 mesh,然后更新 props.position

调用下:

image.png

从 store 取出来,传入 init 方法。

image.png

位置改变的时候调用下。

updateMeshPosition(obj.name, obj.position);

试下效果:

2025-05-14 06.44.26.gif

这样,当位置更新的时候,就会同步到 json 里。

添加新物体的时候,再次渲染也会根据 json 里的位置渲染:

2025-05-14 06.44.49.gif

这样,位置的编辑就完成了。

但现在我们把 OrbitControls 注释掉了,为了避免手势的冲突。

OrbitControls 在旋转、缩放场景的时候是很有用的。

如何让 OrbitControls 和 TransformControls 都生效呢?

这样:

image.png

const orbitControls = new OrbitControls(camera, renderer.domElement);
transformControls.addEventListener('dragging-changed', function (event) {
    orbitControls.enabled = !event.value;
});

我们把 OrbitControls 拿到上面来。

当 TransformControls 拖动的时候,把 OrbitControls 禁用就好了

dragging-changed 是拖动状态的变化,如果 OrbitControls 在拖动,那就禁用 TransformControls,否则就启用。

试一下:

2025-05-14 06.51.18.gif

这样,两个 Controls 就都生效了。

案例代码上传了小册仓库

总结

这节我们实现了位置的编辑。

通过 TransformControls 来实现编辑功能。

并且在 store 添加了一个方法,当 TransformControls 在 change 位置的时候,把它更新到 json 里。

但他和 OrbitControls 的交互是冲突的,我们通过 TransformControls 的拖动状态的改变来启用禁用 OrbitControls 就好了。

这样,我们就实现了物体位置的编辑。

评论