threejs实现标签标注的三种方式

为了不遮挡模型的点击效果,给标签外层添加pointer-events :none

对比
在这里插入图片描述

CSS2DRenderer

大小是固定的,始终正面朝向我们的相机。
自己写代码把世界坐标xyz,转化为像素px表示屏幕坐标,比较麻烦,不过threejs扩展库CSS2DRenderer.js可以帮助你实现坐标转化,给HTML元素标签定位,下面给大家演示如何实现。

CSS2DObject:css2模型对象
CSS2DRenderer:css2模型渲染器

import {
  CSS2DObject, CSS2DRenderer
} from "three/examples/jsm/renderers/CSS2DRenderer";

//一、创建模型渲染器
    // 创建一个CSS2渲染器CSS2DRenderer
      var labelRenderer = new CSS2DRenderer();
      labelRenderer.setSize(window.innerWidth, window.innerHeight);
      labelRenderer.domElement.style.position = 'absolute';
      // 相对标签原位置位置偏移大小
      labelRenderer.domElement.style.top = '0px';
      labelRenderer.domElement.style.left = '0px';
      // //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
      labelRenderer.domElement.style.pointerEvents = 'none';
      document.body.appendChild(labelRenderer.domElement);
     

//二、在resize方法中,记得也要写渲染器的更新
   this.labelRenderer.setSize(window.innerWidth, window.innerHeight)
//三、animation中
 labelRenderer.render(scene, camera); //渲染HTML标签对象


//四、创建css2模型对象
   const div = document.getElementById('tag');
   div.style.top = '-161px'; //指示线端点和标注点重合
      // HTML元素转化为threejs的CSS2模型对象
   const tag= new CSS2DObject(div);
   tag.position.set(worldPosition.x, worldPosition.y, worldPosition.z - 10)
   tag.element.style.display = 'block'
   tag.element.style.left = e.offsetX - 100 + 'px'
   tag.element.style.top = e.offsetY - 164 + 'px'

CSS3DRenderer

CSS3DObject和CSS3DSprite 都需要先有CSS3DRenderer ,CSS3DRenderer的创建跟CSS2DRenderer类似
初始化操作:

  initD3() {
    // 创建一个CSS3渲染器CSS3DRenderer
    this.D3Render = new CSS3DRenderer();
    this.D3Render.setSize(this.width, this.height);
    // HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
    this.D3Render.domElement.style.position = "absolute";
    this.D3Render.domElement.style.top = "0px";
    //设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
    this.D3Render.domElement.style.pointerEvents = "none";
    const containerDom = document.getElementById(this.id);
    containerDom.appendChild(this.D3Render.domElement);
  }

//在resize
this.D3Render.setSize(innerWidth, innerHeight);

//在animation
this.D3Render.render(this.scene, this.camera);

CSS3DObject

可以通过相机控件OrbitControls旋转、缩放三维场景,CSS3模型对象CSS3DObject跟着旋转、缩放,但是旋转的时候背面是一个对称的效果。
比如下面就是旋转后的效果
在这里插入图片描述

  • 可以禁止展示对应HTMK元素背面展示
<div id="tag" style="backface-visibility: hidden;">标签内容</div>

实际创建:

   const div = document.getElementById('tag');
            // HTML元素转化为threejs的CSS3模型对象
            const tag = new CSS3DObject(div);
            //标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
            const geometry = new THREE.ConeGeometry(25, 80);
            geometry.translate(0, 40, 0);
            const material = new THREE.MeshBasicMaterial({
                color: 0xffffff,
            });
            const mesh = new THREE.Mesh(geometry, material);
            mesh.add(tag);
            // 相对父对象局部坐标原点偏移80,刚好标注在圆锥顶部
            tag.position.y += 80;
            this.viewer.scene.add(mesh)

CSS3DSprite

CSS3精灵模型CSS3DSprite对应的HTML标签,可以跟着场景缩放,位置可以跟着场景旋转,但是自身的姿态角度始终平行于canvas画布,不受旋转影响,就像精灵模型一样Sprite

  const div = document.getElementById('tag');
            // HTML元素转化为threejs的CSS3模型对象
            const tag = new CSS3DSprite(div);
            //标签tag作为mesh子对象,默认标注在模型局部坐标系坐标原点
            const geometry = new THREE.ConeGeometry(25, 80);
            geometry.translate(0, 40, 0);
            const material = new THREE.MeshBasicMaterial({
                color: 0xffffff,
            });
            const mesh = new THREE.Mesh(geometry, material);
            mesh.add(tag);
            // 相对父对象局部坐标原点偏移80,刚好标注在圆锥顶部
            tag.position.y += 80;
            this.viewer.scene.add(mesh)

精灵模型Sprite

精灵模型渲染Sprite的标签,默认可以被其他网格模型遮挡,但是CSS3渲染器渲染的HTML元素标签是叠加在canvas画布上,不会被其它网格模型遮挡。

import * as THREE from 'three';
// 标注位置对应的模型对象obj
function createSprite(obj,state) {
    const texLoader= new THREE.TextureLoader();
    let texture = null;
    if(state == '警告'){
        texture= texLoader.load("./警告.png");
    }else{
        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; //标签底部箭头和空对象标注点重合  
    obj.add(sprite); //tag会标注在空对象obj对应的位置
}

export default createSprite;

### 如何在 Three.js 中实现 3D 标注 要在 Three.js 中实现三维标注,可以利用 `THREE.Sprite` 或者通过创建一个始终面向相机的平面对象来完成。以下是具体方法及其代码示例: #### 使用 THREE.Sprite 实现文字标注 `THREE.Sprite` 是一种特殊的材质类型,它总是朝向摄像机,非常适合用来制作标签或其他需要保持固定方向的对象。 ```javascript // 创建场景、相机和渲染器 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 添加立方体作为测试模型 const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); camera.position.z = 5; // 创建 Sprite 材质用于显示文字 const spriteText = 'Label'; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); context.font = "Bold 40px Arial"; context.fillStyle = "#ffffff"; // 文字颜色 context.fillText(spriteText, 0, 40); // 填充文字到画布上 // 将 Canvas 转换为纹理 const texture = new THREE.CanvasTexture(canvas); // 创建 Sprite 并设置其位置 const spriteMaterial = new THREE.SpriteMaterial({ map: texture }); const sprite = new THREE.Sprite(spriteMaterial); sprite.scale.set(100, 50, 1); // 设置缩放比例 sprite.position.set(0, 0, -2); // 放置在立方体前面 scene.add(sprite); function animate() { requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); } animate(); ``` 上述代码展示了如何使用 `THREE.Sprite` 和 HTML5 `<canvas>` 绘制动态的文字标签[^1]。 --- #### 结合 Billboard 技术使物体始终面向相机 如果希望更加复杂的几何形状或者图像能够始终保持面对相机的方向,则可以通过调整旋转角度的方式实现 billboard 效果。 ```javascript class Billboard extends THREE.Object3D { constructor(text) { super(); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 256; canvas.height = 128; ctx.font = "Bold 60px Arial"; ctx.textAlign = "center"; ctx.fillStyle = "#fff"; ctx.fillText(text, canvas.width / 2, canvas.height / 2); this.texture = new THREE.Texture(canvas); this.texture.needsUpdate = true; const planeGeom = new THREE.PlaneBufferGeometry(1, 0.5); const mat = new THREE.MeshBasicMaterial({ map: this.texture, side: THREE.DoubleSide }); this.mesh = new THREE.Mesh(planeGeom, mat); this.add(this.mesh); } update(cameraPosition) { this.lookAt(cameraPosition); // 让物体始终看向相机 } } ``` 此部分代码定义了一个自定义类 `Billboard`,该类中的对象会自动更新以确保它们一直指向当前活动摄像头的位置。 --- #### 集成至数据可视化平台 当构建完整的 3D 可视化应用时,可能涉及多个标注项以及交互逻辑。此时建议采用模块化的架构设计,并结合用户输入事件(如鼠标点击或拖拽)增强用户体验[^2]。 例如,在地图上的特定地点放置标记并附加描述信息;或者针对复杂的数据集绘制热力图的同时提供数值解释等功能都可以借助此类技术轻松达成目标。 --- #### 处理更多细节与优化性能 对于大规模的应用程序来说,仅仅依靠简单的精灵可能会带来一些局限性。比如加载大量高分辨率图片可能导致内存占用过高甚至崩溃等问题。因此有必要考虑引入其他库辅助处理这些情况下的资源管理任务,像 TextureLoader 加载外部素材文件等手段均有助于缓解压力同时提升画面质量[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值