十五、场景标注标签信息
CSS2DRenderer(HTML标签)
通过CSS2DRenderer.js扩展库可以把HTML元素作为标签标注三维场景。
- HTML元素创建标签
- 通过CSS2模型对象
CSS2DObject,把一个HTML元素转化为一个类似threejs网格模型的对象 - 引入CSS2渲染器
CSS2DRenderer- 通过
CSS2Renderer.render()渲染HTML标签 CSS2Renderer.setSize()- 渲染结果
CSS2Renderer.domElement
- 通过
- CSS2Renderer.domElement重新定位 threejs执行
css2Renderer.render()之后,打开浏览器控制台元素选项,可以发现<div id="tag"></div>外面多了一层div父元素,CSS2Renderer.domElement对应的就是<div id="tag"></div>外面的父元素。CSS2Renderer.domElement重新定位,叠加到canvas画布上,与canvas画布重合即可 - 解决HTML标签遮挡Canvas画布事件 HTML元素标签
<div id="tag"></div>外面div父元素遮挡了Canvas画布鼠标事件,会造成相机控件OrbitControls的旋转、缩放等操作无效 1、设置.style.pointerEvents = none2、设置z-index顺序
renderer.domElement.style.zIndex = -1;
css2Renderer.domElement.style.zIndex = 1;设置CSS2模型对象CSS2DObject代码
// 引入CSS2模型对象CSS2DObject
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
// 设置标签位置
tag.position.set(50,0,50);
scene.add(tag);设置CSS2渲染器CSS2DRenderer代码
// 引入CSS2渲染器CSS2DRenderer
import { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';
// 创建一个CSS2渲染器CSS2DRenderer
const css2Renderer = new CSS2DRenderer();
css2Renderer.setSize(width, height);
document.body.appendChild(css2Renderer.domElement);
// HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
// 注意canvas网布布局方式不同,CSS相关代码写法也可能不同,不过你只要能让标签父元素叠加到canvas画布上上面且重合就行
css2Renderer.domElement.style.position = 'absolute';
css2Renderer.domElement.style.top = '0px';
//设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
css2Renderer.domElement.style.pointerEvents = 'none';
// 渲染循环
function render() {
css2Renderer.render(scene, camera);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();css2Renderer.render()渲染本质:css2Renderer.render()渲染HTML元素对应的CSS2模型对象,本质上就是根据CSS2模型对象的xyz世界坐标,计算HTML标签元素在canvas画布上的屏幕像素坐标位置。
标签位置的不同设置方式
如果需要的mesh有多个父对象,且都有自己的位置属性.position,设置mesh标签对象位置CSS2DObject.position的时候,就需要考虑mesh父对象的位置对mesh的影响。
.getWorldPosition()方法计算世界坐标
通过.getWorldPosition()方法可以获取一个模型的世界坐标。
mesh.position.set(50,0,50);
// mesh设置一个父对象meshGroup
const meshGroup = new THREE.Group();
meshGroup.add(mesh);
// mesh位置受到父对象局部坐标.positionn影响
meshGroup.position.x = -100;
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更方便。
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
//标签tag作为mesh子对象,默认受到父对象位置影响
mesh.add(tag);标注模型几何体的某个顶点
标签模型对象作为需要标注mesh的子对象,然后获取mesh几何体某个顶点的坐标,作为标签模型对象局部坐标.position。
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
//标签tag作为mesh子对象,默认受到父对象位置影响
mesh.add(tag);
const pos = geometry.attributes.position;
// 获取几何体顶点1的xyz坐标,设置标签局部坐标.position属性
tag.position.set(pos.getX(0),pos.getY(0),pos.getZ(0));标签位置(标注工厂模型)
CSS2模型对象标注工厂设备
标签tag作为obj子对象,默认标注在工厂设备obj的局部坐标系坐标原点
loader.load("../工厂.glb", function (gltf) {
const tag = new CSS2DObject(div);
// const obj = gltf.scene.getObjectByName('设备A');
const obj = gltf.scene.getObjectByName('设备B');
//标签tag作为obj子对象,默认标注在工厂设备obj的局部坐标系坐标原点
obj.add(tag);
})建模软件创建空对象(控制标签位置)
除了上面调整局部坐标系方式,还有一种更灵活,更方便的标注方式,就是在你的三维建模软件中,任何你想标注的位置,创建一个空对象(空的模型对象,没有任何模型顶点数据,只是一个空对象)。
loader.load("../工厂.glb", function (gltf) {
const tag = new CSS2DObject(div);
// obj是建模软件中创建的一个空对象
const obj = gltf.scene.getObjectByName('设备B标注');
// const obj = gltf.scene.getObjectByName('停车场标注');
//tag会标注在空对象obj对应的位置
obj.add(tag);
})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>CSS3DRenderer渲染HTML标签
CSS3渲染器CSS3DRenderer和CSS2渲染器CSS2DRenderer整体使用流程基本相同,只是在HTML标签渲染效果方面不同,比如CSS3渲染的标签会跟着场景相机同步缩放,而CSS2渲染的标签默认保持自身像素值。
CSS2DRenderer渲染HTML元素标签,默认情况下,HTML元素会保持本身尺寸的像素值,除非你通过代码缩放。
设置CSS3渲染器代码
和CSS2渲染器代码一样设置,只需要把CSS2换成CSS3即可。
// 引入CSS3渲染器CSS3DRenderer
import {CSS3DRenderer} from 'three/addons/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);
};设置CSS3对象模型CSS3DObject代码
也和CSS2对象模型一样
// 引入CSS3模型对象CSS3DObject
import { CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS3模型对象
const tag = new CSS3DObject(div);
//标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
mesh.add(tag);
// 相对父对象局部坐标原点偏移80,刚好标注在圆锥
tag.position.y += 80;禁止CSS3DObject标签对应HTMl元素背面显示
<div id="tag" style="backface-visibility: hidden;">标签内容</div>CSS3精灵模型CSS3DSprite
CSS3精灵模型CSS3DSprite对应的HTML标签,可以跟着场景缩放,位置可以跟着场景旋转,但是自身的姿态角度始终平行于canvas画布,不受旋转影响,就像精灵模型一样Sprite
CSS3精灵模型CSS3DSprite尺寸、位置、缩放等渲染规律和CSS3对象模型CSS3DObject基本一致。
// 引入CSS3精灵模型对象CSS3DSprite
import { CSS3DSprite } from 'three/addons/renderers/CSS3DRenderer.js';
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS3精灵模型`CSS3DSprite`
const tag = new CSS3DSprite(div);
//标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
mesh.add(tag);
// 相对父对象局部坐标原点偏移80,刚好标注在圆锥
tag.position.y += 80;标签局部遮挡鼠标事件
标签<div id="tag"></div>在CSS3渲染器渲染的时候,默认会被设置为pointer-events: auto;,这时候虽然css3Renderer.domElement不遮挡canvas画布的鼠标事件,但是<div id="tag"></div>遮挡canvas画布的鼠标事件。
这时候你可以通过代码强制改变CSS3渲染器给标签设置的.style.pointerEvents = 'auto',设置为.style.pointerEvents = 'none',这时候注意一点,修改.style.pointerEvents,要在实例化new CSS3DObject(div)之后,因为执行new CSS3DObject(div)的时候,会把HTML标签设置为.style.pointerEvents = 'auto'。
const div = document.getElementById('tag');
// HTML元素转化为threejs的CSS3模型对象
const tag = new CSS3DObject(div);
// new CSS3DObject(div);之后设置style.pointerEvents
div.style.pointerEvents = 'none';精灵模型Sprite作为标签
纹理贴图精灵模型
// 引入gltf模型加载库GLTFLoader.js
import {
GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group();
loader.load("../工厂.glb", function (gltf) {
model.add(gltf.scene);
const texLoader= new THREE.TextureLoader();
const texture = texLoader.load("./警告.png");
const spriteMaterial = new THREE.SpriteMaterial({
map: texture,
});
const sprite = new THREE.Sprite(spriteMaterial);
// 控制精灵大小
sprite.scale.set(5, 5, 1);
sprite.position.y = 5 / 2; //标签底部箭头和空对象标注点重合
const obj = gltf.scene.getObjectByName('设备A标注');// obj是建模软件中创建的一个空对象
obj.add(sprite);//tag会标注在空对象obj对应的位置
})canvas画布作为CanvasTexture的参数创建一个纹理对象,本质上你可以理解为CanvasTexture把canvas画布当做图片,读取参数canvas画布上的像素值,创建纹理贴图Texture。
loader.load("../工厂.glb", function (gltf) {
model.add(gltf.scene);
const canvas = createCanvas('设备A');//创建一个canvas画布
// canvas画布作为CanvasTexture的参数创建一个纹理对象
// 本质上你可以理解为CanvasTexture读取参数canvas画布上的像素值
const texture = new THREE.CanvasTexture(canvas);
const spriteMaterial = new THREE.SpriteMaterial({
map: texture,
});
const sprite = new THREE.Sprite(spriteMaterial);
})精灵模型Sprite和CSS3精灵模型CSS3DSprite标签差异
精灵模型渲染Sprite的标签,默认可以被其他网格模型遮挡,但是CSS3渲染器渲染的HTML元素标签是叠加在canvas画布上,不会被其它网格模型遮挡。
