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

这个是通过 TransformControls 来实现的。
我们来写一下:
创建 TransformControls:

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:

这里我们把点击的范围精确了一下,只有 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 是冲突的:

试下效果:

这样,添加两个物体之后,就可以用 TransformControls 来移动位置了。
但移动之后,再添加一个物体,所有的物体都会到 0,0,0 的位置。

因为我们并没有把位置更新到 store。
在 change 的时候打印下 attach 到的对象的位置:

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

然后把它更新到 store。
store 里添加一个方法:

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
调用下:

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

位置改变的时候调用下。
updateMeshPosition(obj.name, obj.position);
试下效果:

这样,当位置更新的时候,就会同步到 json 里。
添加新物体的时候,再次渲染也会根据 json 里的位置渲染:

这样,位置的编辑就完成了。
但现在我们把 OrbitControls 注释掉了,为了避免手势的冲突。
OrbitControls 在旋转、缩放场景的时候是很有用的。
如何让 OrbitControls 和 TransformControls 都生效呢?
这样:

const orbitControls = new OrbitControls(camera, renderer.domElement);
transformControls.addEventListener('dragging-changed', function (event) {
orbitControls.enabled = !event.value;
});
我们把 OrbitControls 拿到上面来。
当 TransformControls 拖动的时候,把 OrbitControls 禁用就好了
dragging-changed 是拖动状态的变化,如果 OrbitControls 在拖动,那就禁用 TransformControls,否则就启用。
试一下:

这样,两个 Controls 就都生效了。
案例代码上传了小册仓库
总结
这节我们实现了位置的编辑。
通过 TransformControls 来实现编辑功能。
并且在 store 添加了一个方法,当 TransformControls 在 change 位置的时候,把它更新到 json 里。
但他和 OrbitControls 的交互是冲突的,我们通过 TransformControls 的拖动状态的改变来启用禁用 OrbitControls 就好了。
这样,我们就实现了物体位置的编辑。