一、实现目标
基于一个3D模型,实现在3D模型中某些固定位置做指向性标注的功能。
(配色真丑不过可以突出目标,凑合看看)
(转动各个角度也是指向该位置的)
例如,在一个柜子的3D模型里,需要在指向柜子底部的位置,标注一个柜子底部高度的信息
二、实现思路
1、在3D模型的画布层上再叠加一层页面元素显示标注
2、使用 threejs的CSS2DRenderer执行3D模型的位置到页面元素位置的转换(将页面元素能够渲染到3D模型的执行位置)
3、在threejs的渲染动画animate函数中每一帧重新渲染页面元素,保证在拖动放大等操作后,元素依旧跟随该指向的固定点,标注信息以及文字也一直面向正面。但是,这里要提,在实现过程中发现,使用animate动画每一帧重复渲染,过度消耗性能,因此找到了threejs的变化事件,监听相机的change事件时,重新渲染页面元素标签,同样能够达到在3D模型拖拽变化时,标签指向位置保持正确
三、实现代码
1、加载页面要展示的3d模型文件并渲染完成之后,执行initMeshRender函数,初始CSS2DRenderer对象,并把元素push到页面。
2、初始完成执行addLabel添加点位函数,传入所有标点的数组位置和名称。
3、绘制点位时,先清除掉之前的全部点位,避免页面元素叠加重复消耗性能。
4、初始化代码中定义控制器,监听相机变动,调用addLabel函数重绘点位。
initMeshRender() {
const param = 页面存储数据的对象,container为页面主体元素
//初始化渲染
labelRenderer = new CSS2DRenderer();
param.container = document.getElementById(`container${this.currentKey}`)
labelRenderer.setSize(param.container.clientWidth, param.container.clientHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.id = 'CSS2DRenderer';
param.container.appendChild(labelRenderer.domElement);
this.addLabel();
},
addLabel() {
this.setPosMesh([{pos:{x: 100, y: 50, z: 100}, name: "测试222"}])
},
setPosMesh(posArr) {
function removeTag() {
//移除所有标签
groupPosTag ? groupPosTag.children.forEach(f=>{
groupPosTag.remove(f);
}) : '';
scene.remove(groupPosTag)
let r = document.getElementById('CSS2DRenderer');
if (r) {
r.innerHTML = "";
}
}
let addPos =(obj)=>{
let name = obj.name;
let pos = obj.pos;
//创建测点显示标签
let falanpos = new THREE.Mesh();
groupPosTag.add(falanpos);//mesh添加到场景
const posDiv = document.createElement('div');
posDiv.className = 'wind_falan_posBg';
//背景图自定义标签
let bg = document.createElement('div')
bg.className = 'bg';
posDiv.appendChild(bg);
//标签指向线
let line = document.createElement('div')
line.className = 'line';
posDiv.appendChild(line);
//文字
let text = document.createElement('div')
text.className = 'text';
text.textContent = name;
posDiv.appendChild(text);
const falanposLabel = new CSS2DObject(posDiv);
falanposLabel.position.set(pos.x, pos.y, pos.z);
falanpos.add(falanposLabel);
falanposLabel.layers.set(0);
falanposLabel.name = 'wind_falan_posBg';
groupPosTag.add(falanposLabel);
divArr.push(posDiv)
}
let divArr = [];
removeTag();
posArr.forEach(f=>{
addPos(f)
})
scene.add(groupPosTag);
labelRenderer.render(scene, camera);
divArr.forEach(posDiv=>{
let r = posDiv.getBoundingClientRect(), offset = 0;
if (r) {
offset = r.width / 2;
}
posDiv.style.marginLeft = (offset + 35) + 'px';//x偏移计算
})
},
//初始化控制器
controls = new OrbitControls(camera, param.container)
controls.addEventListener('change', () => {
this.addLabel();//重绘标签,保证标签在转动时点位正确
})