如果你想用HTML元素作为标签标注三维场景中模型信息,需要考虑定位的问题。比如一个模型,在代码中你可以知道它的局部坐标或世界坐标xyz,但是你并不知道渲染后在canvas画布上位置,距离web页面顶部top和左侧的像素px值。自己写代码把世界坐标xyz,转化为像素px表示屏幕坐标,比较麻烦,不过threejs扩展库CSS2DRenderer.js
可以帮助你实现坐标转化,给HTML元素标签定位,下面给大家演示如何实现。
CSS2DRenderer
CSS2DRenderer是Three.js的扩展库,用于在 WebGL 场景中渲染 HTML 元素。它允许开发者将DOM元素(如<div>
、<span>
等)直接集成到Three.js的3D场景中,从而实现2D元素与3D物体的交互和结合。总的来说,CSS2DRenderer为Three.js提供了强大的功能,使得在3D环境中集成2D界面元素成为可能,这对于创建具有丰富视觉效果和交互性的3D应用非常有用。
CSS2DRenderer的主要特点是它支持将HTML元素作为3D对象进行处理,这些HTML元素可以通过CSS进行样式化,并且可以在Three.js的渲染循环中进行更新和动画处理。
使用CSS2DRenderer的基本步骤
引入扩展库CSS2DRenderer.js
threejs扩展库CSS2DRenderer.js提供了两个类CSS2渲染器CSS2DRenderer
、CSS2模型对象CSS2DObject
。
// 引入CSS2渲染器CSS2DRenderer和CSS2模型对象CSS2DObject
import { CSS2DObject, CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
HTML元素创建标签
<div id="tag">标签内容</div>
CSS2模型对象CSS2DObject
将HTML元素(如<div>
标签)添加到场景中,这些元素将被视为CSS2DObject实例,通过CSS2DObject
类把一个HTML元素转化为一个类似threejs网格模型的对象,可以像操作其他Three.js对象一样对它们进行变换(如位置、旋转等)或者添加到场景中。
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
通过.position
属性设置标签模型对象的xyz坐标。
tag.position.set(50,0,50);
把HTML元素对应的CSS2模型对象添加到其它模型对象或三维场景中。
// 添加到场景
scene.add(tag);
// 添加到组
const group = new THREE.Group();
group.add(tag);
// 添加几何体上
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshBasicMaterial({
color: 0xfaf33a,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.add(tag);
标签位置的不同设置方式
- CSS2模型标签对象位置和要标注的mesh放在同一个位置,这样HTML标签就可以标注mesh。
const group = new THREE.Group();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0xfaf33a,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
tag.position.set(0, 0, 0);
group.add(mesh, tag);
如果需要的mesh有多个父对象,且都有自己的位置属性.position
,设置mesh标签对象位置CSS2DObject.position
的时候,就需要考虑mesh父对象的位置对mesh的影响。
let group = new THREE.Group();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0xfaf33a,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
// mesh设置一个父对象meshGroup
const meshGroup = new THREE.Group();
meshGroup.add(mesh);
// mesh位置受到父对象局部坐标.positionn影响
meshGroup.position.x = -1;
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
tag.position.set(0, 0, 0);
group.add(meshGroup, tag);
上面可以看出,不考虑mesh父元素.position
对mesh的影响,设置标签位置标签偏离。此时标签的位置设置应根据Mesh父元素的位置设置。
// tag.position.set(0, 0, 0);
tag.position.set(-1, 0, 0);
.getWorldPosition()
方法计算世界坐标。
mesh.position.set(0, 0, 0);
// mesh设置一个父对象meshGroup
const meshGroup = new THREE.Group();
meshGroup.add(mesh);
// mesh位置受到父对象局部坐标.positionn影响
meshGroup.position.x = -1;
const tag = new CSS2DObject(div);
const worldPosition = new THREE.Vector3();
// 获取mesh的世界坐标(meshGroup.position和mesh.position累加结果)
mesh.getWorldPosition(worldPosition);
// mesh世界坐标复制给tag
tag.position.copy(worldPosition);
const group = new THREE.Group();
// 最后meshGroup和tag放在同一个父对象中即可
group.add(meshGroup, tag);
- CSS2模型对象作为mesh子对象。无论mesh有多少个父对象,CSS2模型对象作为mesh子对象,可以直接继承mesh的世界坐标,相比通过
.getWorldPosition()
方法获取世界坐标,再设置标签模型位置CSS2DObject.position
更方便。
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
// 标签tag作为mesh子对象,默认受到父对象位置影响
mesh.add(tag);
一个模型对象,不管是一个mesh,还是多个mesh组成的模型,本身是有尺寸的,如果你把标签模型对象CSS2DObject
作为该模型对象的子元素,标签默认是标注在模型的局部坐标系坐标原点。
loader.load("小人.glb", function (gltf) {
const obj = gltf.scene.getObjectByName('Bai');
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
// 标签tag作为obj子对象
obj.add(tag);
})
CSS2渲染器CSS2DRenderer
CSS2渲染器CSS2DRenderer
和常用的WebGL渲染器WebGLRenderer
一样都是渲染器,只是渲染模型对象不同,WebGLRenderer
主要是渲染threejs自身的网格、点、线等模型,CSS2DRenderer
用来渲染HTML元素标签对应的CSS2模型对象CSS2DObject
。
// 创建一个CSS2渲染器CSS2DRenderer
const css2Renderer = new CSS2DRenderer();
CSS2Renderer.render()
CSS2渲染器CSS2DRenderer
和WebGL渲染器WebGLRenderer
虽然不同,但是有些属性和方法是相似的,比如.domElement
、.setSize()
、.render()
。
// 用法和webgl渲染器渲染方法类似
css2Renderer.render(scene, camera);
CSS2Renderer.setSize()
设置CSS2Renderer.render()
渲染输出标签的尺寸范围,一般和threejs canvas画布宽高度一致即可。
const width = window.innerWidth; // canvas画布宽度
const height = window.innerHeight; // canvas画布高度
renderer.setSize(width, height); // 设置渲染区域尺寸
css2Renderer.setSize(width, height);
CSS2Renderer.domElement
CSS2Renderer.render()
渲染会输出标签对应的HTML元素,也就是css2Renderer.domElement
,你可以插入到web网页中任何你想放的位置。
document.body.appendChild(css2Renderer.domElement);
// 放在指定元素中
document.getElementById('box').appendChild(css2Renderer.domElement);
threejs执行css2Renderer.render()
之后,你打开浏览器控制台元素选项,找到你创建的HTML标签<div id="tag">标签内容</div>
,你可以发现<div id="tag"></div>
外面多了一层div父元素,有两个DOM(一个存放三维模型,一个存放CSS2D对象),CSS2Renderer.domElement
是一个HTML元素,对应的就是<div id="tag"></div>
外面的父元素。
<!-- `<div id="tag"></div>`外面多了一层div父元素 -->
<div style="overflow: hidden; width: 1087px; height: 1069px;">
<div id="tag">标签内容</div>
</div>
还可以发现,你创建的HTML标签<div id="tag"></div>
不在原来的位置了,其实是被CSS2Renderer
改变了位置。css2Renderer.render()
渲染HTML元素对应的CSS2模型对象,本质上就是根据CSS2模型对象的xyz世界坐标,计算HTML标签元素在canvas画布上的屏幕像素坐标位置。
CSS2Renderer.domElement
重新定位
CSS2Renderer.domElement重新定位,将外面div父元素重新定位,叠加到canvas画布上,与canvas画布重合即可,可以看到HTML标签的标注效果。注意这里css2Renderer尺寸一定要和渲染器canvas的尺寸保持一致,不然位置不好对应。
// HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
css2Renderer.domElement.style.position = 'absolute';
css2Renderer.domElement.style.top = '0px';
<!-- `<div id="tag"></div>`外面多了一层div父元素 -->
<div style="overflow: hidden; width: 1087px; height: 1069px; position: absolute; top: 0px;">
<div id="tag">标签内容</div>
</div>
HTML标签遮挡Canvas画布事件
HTML元素标签<div id="tag"></div>
外面div父元素遮挡了Canvas画布鼠标事件,会造成相机控件OrbitControls
的旋转、缩放等操作无效,也有可能会影响你的射线拾取,等等任何与canvas画布有关的鼠标事件都有可能受到影响。
- 设置
.style.pointerEvents = none
,就可以解决HTML元素标签对threejs canvas画布鼠标事件的遮挡。
css2Renderer.domElement.style.pointerEvents = 'none';
- 设置
.style.zIndex
,改变css2Renderer.domElement层级,这种方式如果标签层级在下,threejs canvas画布在上,标签被canvas画布遮挡,看不到标签
renderer.domElement.style.zIndex = -1;
css2Renderer.domElement.style.zIndex = 1;
相机控件操作失效还可通过设置OrbitControls
监听的HTML元素为css2Renderer.domElement解决。
controls = new OrbitControls(camera, css2Renderer.domElement);
Canvas尺寸变化(HTML标签)
canvas画布完全填充浏览器文档区域,如果窗口尺寸变化了,通过renderer.setSize()
设置canvas画布尺寸,HTML标签相关的CSS渲染器代码也要同步设置css2Renderer.setSize()
。执行css2Renderer.setSize()
设置CSS2渲染器输出的HTML标签.domElement
的尺寸,保持和canvas画布尺寸一样。
// 画布跟随窗口变化
window.onresize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
// cnavas画布宽高度重新设置
renderer.setSize(width,height);
// HTML标签css2Renderer.domElement尺寸重新设置
css2Renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
};
HTML标签渲染前隐藏
在CSS2渲染器渲染HTML标签,重新定位标签之前,threejs执行代码和加载gltf模型也是需要时间的,这时候标签对应的HTML、CSS代码会显示在web页上面。可以先把标签隐藏display: none;
,等gltf模型加载完成,HTML元素转化CSS2模型对象以后,再取消HTML隐藏状态,CSS2渲染器默认会把标签设置为display: block;
,这样就不用自己代码恢复HTML标签元素的隐藏状态了。
<!-- CSS2渲染器渲染器之前,隐藏标签 -->
<div id="tag" style="display: none;"><div>
鼠标选中模型添加标签
当发生鼠标事件,如果射线拾取到模型对象,就把标签做为选中模型的子对象,或作为选中模型对应标注点空对象的子对象。
let modelObj = null;
addEventListener('click', function (event) {
// ...射线拾取的代码
// 射线交叉计算拾取模型
const intersects = raycaster.intersectObjects(scene);
if (intersects.length > 0) {
// tag会标注在intersects[0].object.parent模型的局部坐标系原点位置
intersects[0].object.ancestors.add(tag);
modelObj = intersects[0].object.parent; // 被选中模型
}else{
//把原来选中模型对应的标签隐藏
if(modelObj){
modelObj.remove(tag); // 从场景移除
}
}
})
单击关闭HTML标签
前面我们了解到,如果你的项目要求三维场景中添加标签时,不能影响canvas画布的事件,必须设置css2Renderer.domElement.style.pointerEvents = 'none'
,此刻你单击按钮去关闭HTML元素标签,会发现无效,可以考虑把标签的子元素关闭按钮,单独设置.style.pointerEvents = 'auto'
或者style="pointer-events: auto;"
,从而解决点击无效的问题。
<div id='tag' style={{ background: '#FFFFFF', display: 'none' }}>
<span>标签内容</span>
<span className='close' style={{ pointerEvents: 'auto' }} onClick={closeEventInfo}>×</span>
</div>
// 关闭事件
function closeEventInfo() {
// 获取标签对象
let tagObj = scene.getObjectByName('tag');
if (tagObj) {
let tagParent = tagObj.parent;
// 将标签从父元素中移除
tagParent.remove(tagObj);
}
}
CSS3DRenderer渲染HTML标签
CSS3渲染器CSS3DRenderer
和CSS2渲染器CSS2DRenderer
整体使用流程基本相同,只是在HTML标签渲染效果方面不同,比如CSS3渲染的标签会跟着场景相机同步缩放,而CSS2渲染的标签默认保持自身像素值。
// 引入CSS3渲染器CSS3DRenderer和CSS3模型对象CSS3DObject
import { CSS3DObject, CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
// 创建一个CSS3渲染器CSS3DRenderer
const css3Renderer = new CSS3DRenderer();
css3Renderer.setSize(width, height);
// HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
css3Renderer.domElement.style.position = 'absolute';
css3Renderer.domElement.style.top = '0px';
//设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
css3Renderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(css3Renderer.domElement);
// 渲染循环
function render() {
css3Renderer.render(scene, camera);
requestAnimationFrame(render);
}
window.onresize = function () {
// HTML标签css3Renderer.domElement尺寸重新设置
css3Renderer.setSize(width,height);
};
通过CSS3DObject
类,可以把一个HTML元素转化为一个CSS3模型对象,就像threejs的网格模型一样,可以添加到场景中,可以设置位置,可以作为其它模型对象的子对象。
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS3模型对象
const tag = new CSS3DObject(div);
// 标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
mesh.add(tag);
// 相对父对象局部坐标原点偏移100
tag.position.y += 100;
// 如果标签覆盖区域过大,可以适当缩小,缩放标签尺寸
tag.scale.set(0.5, 0.5, 1);
CSS3模型对象CSS3DObject
渲染结果,就像一个矩形平面网格模型一样。你通过相机控件OrbitControls
旋转、缩放三维场景,CSS3模型对象CSS3DObject
跟着旋转、缩放。旋转过程中HTML元素标签的正反面都可以看到,但是旋转到背面是一个对称的效果。
禁止CSS3DObject
标签对应HTMl元素背面显示
可是禁止展示对应HTML元素的背面,直观感受就是当相机控件OrbitControls
旋转到CSS3模型对象CSS3DObject
的背面时,CSS3模型对象CSS3DObject
不可见。
<div id="tag" style="backface-visibility: hidden;">标签内容</div>
批量创建标签
// 需要批量标注的标签数据arr
const arr = ['设备A','设备B','停车场'];
for (let i = 0; i < arr.length; i++) {
// 注意是多个标签,需要克隆复制一份
const div = document.getElementById('tag').cloneNode();
div.innerHTML = arr[i]; // 标签数据填写
// HTML元素转化为threejs的CSS3对象
const tag = new CSS3DObject(div);
div.style.pointerEvents = 'none'; // 避免标签遮挡canvas鼠标事件
// obj是建模软件中创建的一个空对象
const obj = gltf.scene.getObjectByName(arr[i]);
// tag会标注在空对象obj对应的位置
obj.add(tag);
tag.scale.set(0.1, 0.1, 1); // 适当缩放模型标签
tag.position.y = 40/2 * 0.1;// 标签底部和空对象标注点重合:偏移高度像素值一半*缩放比例
}
CSS3精灵模型CSS3DSprite
CSS3对象模型CSS3DObject
渲染效果类似矩形平面网格模型Mesh
。CSS3精灵模型CSS3DSprite
渲染效果精灵模型对象Sprite
。CSS3精灵模型CSS3DSprite
对应的HTML标签,可以跟着场景缩放,位置可以跟着场景旋转,但是自身的姿态角度始终平行于canvas画布,不受旋转影响。
// 引入CSS3精灵模型对象CSS3DSprite
import { CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js';
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS3模型对象
const tag = new CSS3DSprite(div);
// 标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
const geometry = THREE.BoxGeometry(100, 100, 100);
geometry.translate(0, 50, 0);
const material = new THREE.MeshBasicMaterial({
color: 0xfaf33a,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.add(tag);
// 相对父对象局部坐标原点偏移100,标签高16,标签偏移108刚好标签的底部在几何体的顶部
tag.position.y += 108;
CSS2DRenderer和CSS3DRenderer的区别
从上面可以看出CSS3DRenderer和CSS2DRenderer的主要区别在于它们面向摄像机的方向、场景缩放时的表现、是否被模型遮挡,以及如何处理DOM事件。
- CSS2DRenderer:主要用于在Three.js场景中渲染2D元素。它允许开发者在3D空间中放置和操作2D对象,这些对象的大小和位置不会随着摄像机的视角的变化而改变,保持固定大小,始终面向屏幕。CSS2DRenderer基于Web技术,使用HTML元素和CSS样式来创建2D对象,这些对象可以与3D对象进行交互,如点击事件等。
- CSS3DRenderer:用于在Three.js场景中渲染具有深度信息的3D对象。它允许开发者创建和操作真正的3D对象,这些对象的大小和位置会随着摄像机的视角的变化而改变,提供更加立体和逼真的效果。CSS3DRenderer可以处理更复杂的3D模型和动画,适用于需要深度感知和立体效果的场景。
总的来说,CSS2DRenderer适合添加简单的2D元素到3D场景中,如文字标签和图像,而CSS3DRenderer则更适合处理复杂的3D模型和动画。选择使用哪种Renderer取决于项目的具体需求和预期的视觉效果,如果元素的大小需要随摄像机的位置和角度变化,或者希望元素能够响应摄像机的缩放,那么CSS3DRenderer是合适的选择。如果元素的大小需要保持固定,或者希望元素能够像传统2D图形一样不受摄像机影响,那么CSS2DRenderer是更合适的选择。
完整示例代码
射线拾取方法参考
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 引入CSS2渲染器CSS2DRenderer和CSS2模型对象CSS2DObject
import { CSS2DObject, CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
// 引入CSS3渲染器CSS3DRenderer和CSS3模型对象CSS3DObject
import { CSS3DObject, CSS3DSprite, CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
import { getCanvasIntersects } from '@/common/three/index.js'; // three自定义公共方法射线穿透
let scene, camera, renderer, css2Renderer, css3Renderer, controls, tag;
export default function Css2DOr3DRenderer() {
const box = useRef(); // canvas盒子
// 加载模型
function setGltfModel() {
// 导入GlTF模型
let gltfLoader = new GLTFLoader();
gltfLoader.load('模型.glb', (gltf) => {
// 添加标签
// create2DTag(gltf.scene);
create3DTag(gltf.scene);
scene.add(gltf.scene);
});
}
// 添加2D标签
function create2DTag(modelObj) {
// 添加标签
if (!tag) {
const tagDom = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
tag = new CSS2DObject(tagDom);
tag.name = 'tag';
tag.position.y = 100;
}
// 避免重复添加
if (modelObj.getObjectByName('tag')) {
return;
} else {
modelObj.add(tag);
}
}
// 添加3D标签
function create3DTag(modelObj) {
// 添加标签
if (!tag) {
const tagDom = document.getElementById('tag');
// 避免标签遮挡canvas鼠标事件
tagDom.style.pointerEvents = 'none';
// HTML元素转化为threejs的CSS3模型对象
tag = new CSS3DSprite(tagDom);
// tag = new CSS3DObject(tagDom);
tag.name = 'tag';
tag.position.y += 10;
tag.scale.set(0.5, 0.5, 1); // 缩放标签尺寸
}
// 避免重复添加
if (modelObj.getObjectByName('tag')) {
return;
} else {
modelObj.add(tag);
}
}
// 关闭标签事件
function closeEventInfo() {
// 获取标签对象
let tagObj = scene.getObjectByName('tag');
if (tagObj) {
let tagParent = tagObj.parent;
// 将标签从父元素中移除
tagParent.remove(tagObj);
}
}
// 渲染动画
function renderFn() {
requestAnimationFrame(renderFn);
controls.update();
// 用相机渲染一个场景
renderer && renderer.render(scene, camera);
// 渲染HTML标签对应的CSS2DObject模型对象
// css2Renderer && css2Renderer.render(scene, camera);
// 渲染HTML标签对应的CSS3DObject模型对象
css3Renderer && css3Renderer.render(scene, camera);
}
// 监听窗体变化、自适应窗体事件
function onWindowResize() {
let width = box.current.offsetWidth;
let height = box.current.offsetHeight;
camera.aspect = width / height;
// 更新相机投影矩阵,在相机任何参数被改变以后必须被调用
camera.updateProjectionMatrix();
renderer.setSize(width, height);
// css2Renderer.setSize(width, height); // 设置css2D渲染区域尺寸
css3Renderer.setSize(width, height); // 设置css3D渲染区域尺寸
}
// 监听事件 窗体监听、点击事件监听
useEffect(() => {
// 监听窗体变化
window.addEventListener('resize', onWindowResize, false);
// 监听点击事件
box.current.addEventListener('click', (event) => {
let selectObj = getCanvasIntersects(event, box.current, camera, scene);
if (selectObj[0]) {
let modelObj = selectObj[0].object.parent;
create2DTag(modelObj);
// create3DTag(modelObj);
} else {
closeEventInfo();
}
}, false);
}, []);
// 初始化环境、灯光、相机、渲染器
useEffect(() => {
scene = new THREE.Scene();
// 添加光源
const ambitlight = new THREE.AmbientLight(0x404040);
scene.add(ambitlight);
const sunlight = new THREE.DirectionalLight(0xffffff);
sunlight.position.set(-20, 1, 1); scene.add(sunlight);
// 加载模型
setGltfModel();
let axisHelper = new THREE.AxesHelper(100);
scene.add(axisHelper); // 坐标辅助线加入到场景中
// 获取宽高设置相机和渲染区域大小
let width = box.current.offsetWidth;
let height = box.current.offsetHeight;
let k = width / height;
// 投影相机
camera = new THREE.PerspectiveCamera(45, k, 1, 100000);
camera.position.set(32, 122, 580);
camera.lookAt(scene.position);
// 创建一个webGL对象
renderer = new THREE.WebGLRenderer({
//增加下面两个属性,可以抗锯齿
antialias: true,
alpha: true
});
renderer.setSize(width, height); // 设置渲染区域尺寸
renderer.setClearColor(0x333333, 1); // 设置颜色透明度
renderer.outputEncoding = THREE.sRGBEncoding; // 解决纹理贴图颜色偏差
box.current.appendChild(renderer.domElement);
// 创建一个CSS2渲染器CSS2DRenderer
// css2Renderer = new CSS2DRenderer();
// css2Renderer.setSize(width, height);
// // HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
// css2Renderer.domElement.style.position = 'absolute';
// css2Renderer.domElement.style.top = '0px';
// css2Renderer.domElement.style.pointerEvents = 'none'; // 避免标签遮挡canvas鼠标事件
// box.current.appendChild(css2Renderer.domElement);
// 创建一个CSS3渲染器CSS3DRenderer
css3Renderer = new CSS3DRenderer();
css3Renderer.setSize(width, height);
// // HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
css3Renderer.domElement.style.position = 'absolute';
css3Renderer.domElement.style.top = '0px';
css3Renderer.domElement.style.pointerEvents = 'none'; // 避免标签遮挡canvas鼠标事件
box.current.appendChild(css3Renderer.domElement);
// 监听鼠标事件
controls = new OrbitControls(camera, renderer.domElement);
// 渲染
renderFn();
}, []);
useEffect(() => {
return () => {
// 清除数据
scene = null;
camera = null;
renderer = null;
css2Renderer = null;
css3Renderer = null;
controls = null;
tag = null;
}
}, []);
return <div className='ui_container_box'>
<div style={{ position: 'relative', width: '100%', height: '100%' }} ref={box}></div>
<div id='tag' style={{ background: '#FFFFFF', display: 'none' }}>
<span>标签内容</span>
<span className='close' style={{ pointerEvents: 'auto' }} onClick={closeEventInfo}>×</span>
</div>
</div >;
}