当前位置: 首页 > news >正文

3D场景标注标签信息,three.js CSS 2D渲染器CSS2DRenderer、CSS 3D渲染器CSS3DRenderer(结合react)

如果你想用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);

标签位置的不同设置方式

  1. 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设置一个父对象meshGroupconst 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);
  1. .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);
  1. 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画布有关的鼠标事件都有可能受到影响。

  1. 设置.style.pointerEvents = none,就可以解决HTML元素标签对threejs canvas画布鼠标事件的遮挡。
css2Renderer.domElement.style.pointerEvents = 'none';
  1. 设置.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渲染器CSS3DRenderercss3Renderer = 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 >;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • ESP32-C3在MQTT访问时出现“transport_base: Poll timeout or error”问题的分析(8)
  • TCP 如何保证可靠性?
  • 代码复现改进
  • VUE中出现Cannot find module ‘@/api/xxx.js‘ or its corresponding type declarations
  • 2-72 基于matlab的平稳小波变换进行多聚焦图像融合
  • 特斯拉算法,暴力递归尝试,汉诺塔问题
  • 使用 Python 爬虫进行网站流量分析:Referer 头的利用
  • 设计模式反模式:UML常见误用案例分析
  • DRF——请求的封装与版本管理
  • C语言第17篇
  • react的ul li滚动列表
  • 乾坤微前端框架详细使用大全
  • 机器学习 之 线性回归算法
  • IntelliJ IDEA ideaIU-2024.2.0.2.exe 启动 IDE 失败
  • 阿里云CentOs ClickHouse安装
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • SegmentFault for Android 3.0 发布
  • 2017届校招提前批面试回顾
  • CSS相对定位
  • C学习-枚举(九)
  • eclipse(luna)创建web工程
  • Git 使用集
  • Sass Day-01
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue的全局变量和全局拦截请求器
  • 从伪并行的 Python 多线程说起
  • 好的网址,关于.net 4.0 ,vs 2010
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 如何优雅地使用 Sublime Text
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 栈实现走出迷宫(C++)
  • Spring第一个helloWorld
  • 阿里云ACE认证学习知识点梳理
  • ​Java基础复习笔记 第16章:网络编程
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • # Panda3d 碰撞检测系统介绍
  • (30)数组元素和与数字和的绝对差
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (k8s)Kubernetes 从0到1容器编排之旅
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (二十六)Java 数据结构
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (十六)一篇文章学会Java的常用API
  • (四)React组件、useState、组件样式
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)创业的注意事项
  • ***测试-HTTP方法
  • .ai域名是什么后缀?
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .Net IE10 _doPostBack 未定义
  • .Net mvc总结