最近再做项目的时候,采用vue + three.js进行开发大型的3d场景项目,该项目中涉及到需要为建筑中的元素添加标签说明一些信息、名称,遂决定使用three.js的CSS2DRenderer和CSS2DObject进行标签的绘制,用的代码如下:
function makeCSS2DObject(position, radius, text) {
const div = document.createElement( 'div' );
div.className = 'scene-label';
div.style.marginTop = '-1em';
const span = document.createElement('span');
span.className = 'scene-label-text'
span.textContent = text;
div.appendChild(span);
const label = new CSS2DObject( div );
label.name = "mark-point"
label.position.set( 0, radius-16, 0);
return label;
}
function makeCSS2DPointObject(position, radius) {
let div = document.createElement( 'div' );
div.className = 'scene-point';
div.style.marginTop = '-2em';
const img = document.createElement('img');
img.src = "assets/img/point.gif";
img.style.width = "30px";
img.style.height = "30px";
div.appendChild(img);
const label = new CSS2DObject( div );
label.position.set( 0, radius-40, 0);
return label;
}
通过将标签加入到场景中,显示出来:
原来以为一切都大功告成的时候,就在跳转到其他3D场景页面的时候,问题来了,居然在其他场景中,css2dobject创建的标签被带了过来,并没有随着页面的切换而消失:
初步排查了原因,原来three.js的 CSS2DRenderer生成的标签直接就是挂在真实的DOM上,并非是Vue的虚拟DOM上,所以在页面切换的时候,这个标签并不会随着切换而消失。而是一直在页面的body上面:
到这里,问题找到了,可以解决问题了,但由CSS2DRenderer生成的标签居然不带任何的类目和id,那怎样在代码中去处理它呢,这里用了一个被逼无奈的简单粗暴的办法:直接去dom的body上找挂载在上面的标签元素,在控制台打印出来:
const div = document.getElementsByTagName('div');
console.log(div)
找到对应的div的位置,由于这个div没有任何类名和id,所以一眼就可以认出它来:
是它,是它,就是它,找到它就可以直接在切换页面的时候干掉它了, 直接在Vue的生命周期钩子函数beforeDestroy里面直接把它从DOM上移除或者将其display设置为none即可:
const div = document.getElementsByTagName('div')[2];
// div.style.display = 'none';
document.body.removeChild(div);
如果下个页面中没有再用到这些标签, 这里建议是直接移除掉;好了,到这里就算大功告成了。
再复盘一下这个问题产生的原因:因为通过Three.js 的CSS2DObject创建的页面元素,是直接挂到真是的DOM上面的,而非虚拟的DOM,所以这个标签的地位与Vue创建的标签再mounted挂载到真实DOM的