分析
threejs官方提供了一个较为完善的threejs场景编辑的demo,并且支持导出导入,前进后退,场景缓存等功能,所以我们需要做的只有把他引入自己的页面。但是同时业务中可能涉及一些特殊的功能或者一些可配置的内容,我们需要在官方editor的沙盒中完成这些操作。所以最终方案就是通过iframe引入editor编辑器,然后通过contentMessage传送消息给沙盒,同时也可以反向监听沙盒里面的instance。
实施
Editor引入到项目中
首先你需要在public文件夹中放入完整的editor沙盒同时把对应的依赖也放进去(这里推荐使用分布式的,因为public包过大会影响打包效率)。
然后在项目中引入。
//nextjs演示 vue可以将ref替换为useRef vue2可以替换为ref
'use client'
import Head from 'next/head'
import React, { useEffect, useRef } from 'react'
import * as THREE from 'three'
import nextinit from './nextinit'
const ThreeEditor = () => {
const inner = useRef()
useEffect(() => {
nextinit(inner.current)//初始化工具 添加事件
}, [])
return (
<>
<div className='w-full h-full bg-[#cfc]'>
<iframe
ref={inner}
className=' w-full h-full'
src="/three/editor/index.html" // 请替换成你实际的静态页面文件名
></iframe>
</div>
</>
)
}
export default ThreeEditor
注册事件系统
在editor的包文件中创建一个事件处理类并添加到index中
//...........略
const eventHandler = new EventHandler(editor)
</script>
</body>
</html>
事件处理系统和一些演示的方法
import { BoxGeometry, Mesh, MeshNormalMaterial } from "three";
export default class EventHandler {
editor
constructor(editor) {
this.editor = editor
window.addEventListener('message', (event) => {
// 确保消息来自父页面
if (event.source !== parent) return;
const message = event.data;
this.eventIn(message)
});
}
eventIn = (message) => {
console.log(message);
if (message.type === 'addTestBox') {
const scene = editor.scene;
const boxgeo = new BoxGeometry(5, 5, 5);
const material = new MeshNormalMaterial();
const mesh = new Mesh(boxgeo, material);
scene.add(mesh);
}
if (message.type === 'cleanScene') {
this.cleanScene()
}
}
cleanScene = () => {
let { scene, removeObject } = this.editor
scene.children.forEach(obj3D => {
if (obj3D.type == 'Mesh' || obj3D.type == 'Group')
console.log(obj3D)
});
}
}
在项目中使用事件
这里我通过刚才的nextinit方法进行处理
useEffect(() => {
nextinit(inner.current)
}, [])
// import { BoxGeometry, Mesh, MeshNormalMaterial } from "three"
const nextinit = (Frame) => {
console.log(Frame.contentWindow);
const sendMessageToIframe = (message) => {
Frame.contentWindow.postMessage(message, '*');
};
setTimeout(() => {
// const editor = Frame.contentWindow.editor
sendMessageToIframe({ type: 'cleanScene' });
}, 1000)
setTimeout(() => {
// const editor = Frame.contentWindow.editor
sendMessageToIframe({ type: 'addTestBox' });
}, 3000)
}
export default nextinit
效果展示
删除之前的几何体后加入一个box
注意事项
iframe内外的webgl通过不互通,从外部只能传送字符串一类的数据,不能传送任何instance进去,不然会报错内存错误,对应的buffer丢失。