使用CSS3DRenderer/CSS2DRenderer给模型上面添加html标签

news/2025/2/26 9:29:31

 先放一下预览图

主要使用css2dRender和css3dRender,添加图片和标签。

思路:使用css3dRender添加一个图片,然后获取的位置坐标,使用css3dRender添加一个文字标签,也设置这个位置坐标,此外z轴设置一个高度,这样就可以放在图片的正上方。图片添加一个点击事件,点击之后会出现一个弹窗。海岛介绍文本框,使用css2dRender。

下面的通过css3dRender创建的html标签,都是放到了模型上面,这个要注意,不是放在场景scene里面。

使用css3dRender添加图片

        addimg(img, fun) {

            const imgDiv = document.createElement('div');

            imgDiv.style.width = '25px';

            imgDiv.style.height = '25px';

            imgDiv.style.backgroundImage = `url(${img})`;

            imgDiv.style.backgroundSize = 'contain';

            imgDiv.style.backgroundRepeat = 'no-repeat';

            imgDiv.style.cursor = 'pointer'; // 鼠标悬停时显示为手型

            imgDiv.style.zIndex = '1000'

            imgDiv.addEventListener('click', fun)

            const imgLabel = new CSS3DObject(imgDiv);

            imgLabel.position.set(1, 0, 0);

            return imgLabel

        },

使用css3dRender添加文本

        addtext(text) {

            // 创建第二个文本标签

            const labelDiv = document.createElement('div');

            labelDiv.innerHTML = text;

            labelDiv.style.color = 'white';

            labelDiv.style.fontSize = '5px';

            labelDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';

            labelDiv.style.padding = '2px';

            labelDiv.style.borderRadius = '3px';

            const label = new CSS3DObject(labelDiv);

            label.position.set(0, 1, 0); // 相对于模型的局部位置

            return label

        },

 这里有一点需要注意一下,就是添加渲染器css3dRender,他会增加一个html标签,默认会叠加到父元素canvas上面,这样就会导致鼠标缩放,模型无法缩放,所以需要设置pointerEvents属性为none,解决html标签对画布的遮盖。对于具体添加的某个css3dObject对象,我们可以看需求,然后决定是否添加 div.style.pointerEvents = 'none'; 如果添加了,就是说对这个html标签设置任何点击事件,都无法起作用。

 添加一个obj格式的模型,这里之前写过一篇文章,写了如何引入如何在three.js里面导入obj的三维模型_threejs 加载obj模型-CSDN博客

        addmodel() {

            // 加载三维模型

            let obj;

            const mtlLoader = new MTLLoader();

            mtlLoader.load(blockmtl, (materials) => {

                const objLoader = new OBJLoader();

                objLoader.setMaterials(materials);

                objLoader.load(block, (loadedObj) => {

                    obj = loadedObj;

                    obj.rotation.x = -20 * (Math.PI / 180);

                    obj.position.set(-2, 2, 0);

                    obj.scale.set(0.02, 0.02, 0.02);

                    // 创建文字标签

                    const labelDiv = document.createElement('div');

                    // labelDiv.className = 'label';

                    labelDiv.innerHTML = `海岛介绍</br>这是被海水围绕的小块陆地`;

                    labelDiv.style.color = 'white';

                    labelDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';

                    labelDiv.style.padding = '9px';

                    labelDiv.style.borderRadius = '5px';

                    const label = new CSS2DObject(labelDiv);

                    labelDiv.style.pointerEvents = 'none'

                    label.position.set(0, 0, 0); // 相对于模型的局部位置

                    obj.add(label);

                    // 创建图片标签

                    let imgLabel1 = this.addimg(img1, () => {

                        window.location.href = 'https://baidu.com';

                    })

                    obj.add(imgLabel1);

                    imgLabel1.position.y -= 70

                    imgLabel1.position.x += 90

                    let imgLabel2 = this.addimg(img2, () => {

                        this.showImageInfo('https://www.baidu.com')

                    })

                    obj.add(imgLabel2);

                    imgLabel2.position.y -= 120

                    imgLabel2.position.x += 140

                    // 创建文字标签

                    let label1 = this.addtext('电箱')

                    obj.add(label1);

                    label1.position.copy(imgLabel2.position); // 复制图片的位置

                    label1.position.y += 20; // 向上偏移

                    let label2 = this.addtext('路牌')

                    obj.add(label2);

                    label2.position.copy(imgLabel1.position); // 复制图片的位置

                    label2.position.y += 20; // 向上偏移

                    this.scene.add(obj);

                });

            });

        },

<template>
    <div class="box">
        <div id="wbglg" style="margin-top: 100px;">
        </div>
    </div>
</template>

<script>
import * as THREE from 'three';
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import block from '../../public/obj/block/block.obj'
import blockmtl from '../../public/obj/block/block.mtl'
import img1 from '../assets/ganzi.png'
import img2 from '../assets/gaungai.png'

export default {
    name: "TestS",
    components: {},
    mounted() {
        this.init()
    },
    data() {
        return {
            controls: "",
            scene: '',
            renderer: '',
            camera: '',
            css2DRenderer: '',
            css3DRenderer: '',
        }
    },
    methods: {
        showImageInfo(detailUrl) {
            // 创建弹窗
            const popup = document.createElement('div');
            popup.style.position = 'fixed';
            popup.style.top = '60%';
            popup.style.left = '51%';
            popup.style.transform = 'translate(-50%, -50%)';
            popup.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; // 半透明黑色背景
            popup.style.padding = '5px';
            popup.style.borderRadius = '10px'; // 圆角
            popup.style.boxShadow = '0 0 10px rgba(255, 255, 255, 0.2)'; // 白色阴影
            popup.style.zIndex = '1000';
            popup.style.fontSize = '14px';

            popup.style.color = 'white'; // 文字颜色
            popup.style.fontFamily = 'Arial, sans-serif'; // 字体
            popup.style.width = '190px'; // 弹窗宽度
            popup.style.textAlign = 'center'; // 文字居中

            // 创建关闭按钮(叉号)
            const closeButton = document.createElement('div');
            closeButton.innerHTML = '&times;'; // 叉号
            closeButton.style.position = 'absolute';
            closeButton.style.top = '10px';
            closeButton.style.right = '10px';
            closeButton.style.cursor = 'pointer'; // 鼠标悬停时显示为手型
            closeButton.style.fontSize = '24px'; // 叉号大小
            closeButton.style.color = 'white'; // 叉号颜色
            closeButton.style.userSelect = 'none'; // 禁止选中文本

            // 点击关闭按钮时移除弹窗
            closeButton.addEventListener('click', () => {
                document.body.removeChild(popup);
            });

            // 弹窗内容
            popup.innerHTML = `
        <h3>电箱信息</h3>
        <p>状态:正常</p>
        <p>管理人:张三</p>
        <p>联系电话:12323232323</p>
        <p style="color: #1E90FF; cursor: pointer;" onclick="window.open('${detailUrl}', '_blank')">点击查看详情</p>
    `;

            // 将关闭按钮添加到弹窗中
            popup.appendChild(closeButton);

            // 将弹窗添加到页面中
            document.body.appendChild(popup);
        },
        addmodel() {
            // 加载三维模型
            let obj;
            const mtlLoader = new MTLLoader();
            mtlLoader.load(blockmtl, (materials) => {
                const objLoader = new OBJLoader();
                objLoader.setMaterials(materials);
                objLoader.load(block, (loadedObj) => {
                    obj = loadedObj;
                    obj.rotation.x = -20 * (Math.PI / 180);
                    obj.position.set(-2, 2, 0);
                    obj.scale.set(0.02, 0.02, 0.02);

                    // 创建文字标签
                    const labelDiv = document.createElement('div');
                    // labelDiv.className = 'label';
                    labelDiv.innerHTML = `海岛介绍</br>这是被海水围绕的小块陆地`;
                    labelDiv.style.color = 'white';
                    labelDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
                    labelDiv.style.padding = '9px';
                    labelDiv.style.borderRadius = '5px';
                    const label = new CSS2DObject(labelDiv);
                    labelDiv.style.pointerEvents = 'none'
                    label.position.set(0, 0, 0); // 相对于模型的局部位置
                    obj.add(label);

                    // 创建图片标签
                    let imgLabel1 = this.addimg(img1, () => {
                        window.location.href = 'https://baidu.com';
                    })
                    obj.add(imgLabel1);
                    imgLabel1.position.y -= 70
                    imgLabel1.position.x += 90

                    let imgLabel2 = this.addimg(img2, () => {
                        this.showImageInfo('https://www.baidu.com')
                    })
                    obj.add(imgLabel2);
                    imgLabel2.position.y -= 120
                    imgLabel2.position.x += 140

                    // 创建文字标签
                    let label1 = this.addtext('电箱')
                    obj.add(label1);
                    label1.position.copy(imgLabel2.position); // 复制图片的位置
                    label1.position.y += 20; // 向上偏移

                    let label2 = this.addtext('路牌')
                    obj.add(label2);
                    label2.position.copy(imgLabel1.position); // 复制图片的位置
                    label2.position.y += 20; // 向上偏移
                    this.scene.add(obj);
                });
            });
        },
        animate() {
            requestAnimationFrame(this.animate);
            this.controls.update();
            this.renderer.render(this.scene, this.camera);
            this.css2DRenderer.render(this.scene, this.camera); // 更新 CSS2D 标签
            this.css3DRenderer.render(this.scene, this.camera); // 更新 CSS3D 标签
        },
        addtext(text) {
            const labelDiv = document.createElement('div');
            labelDiv.innerHTML = text;
            labelDiv.style.color = 'white';
            labelDiv.style.fontSize = '5px';
            labelDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
            labelDiv.style.padding = '2px';
            labelDiv.style.borderRadius = '3px';
            const label = new CSS3DObject(labelDiv);
            label.position.set(0, 1, 0); // 相对于模型的局部位置
            return label
        },
        addimg(img, fun) {
            const imgDiv = document.createElement('div');
            imgDiv.style.width = '25px';
            imgDiv.style.height = '25px';
            imgDiv.style.backgroundImage = `url(${img})`;
            imgDiv.style.backgroundSize = 'contain';
            imgDiv.style.backgroundRepeat = 'no-repeat';
            imgDiv.style.cursor = 'pointer'; // 鼠标悬停时显示为手型
            imgDiv.style.zIndex = '1000'
            imgDiv.addEventListener('click', fun)
            const imgLabel = new CSS3DObject(imgDiv);
            imgLabel.position.set(1, 0, 0);
            return imgLabel
        },
        init() {
            this.scene = new THREE.Scene();
            this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            this.renderer = new THREE.WebGLRenderer({ alpha: true });
            this.renderer.setSize(window.innerWidth, window.innerHeight);
            document.querySelector("#wbglg").appendChild(this.renderer.domElement);

            // 创建 CSS2DRenderer
            this.css2DRenderer = new CSS2DRenderer();
            this.css2DRenderer.setSize(window.innerWidth, window.innerHeight);
            this.css2DRenderer.domElement.style.position = 'absolute';
            this.css2DRenderer.domElement.style.top = '0px';
            this.css2DRenderer.domElement.style.left = '0px';
            this.css2DRenderer.domElement.style.pointerEvents = 'none'
            document.body.appendChild(this.css2DRenderer.domElement);

            // 创建 CSS3DRenderer
            this.css3DRenderer = new CSS3DRenderer();
            this.css3DRenderer.setSize(window.innerWidth, window.innerHeight);
            this.css3DRenderer.domElement.style.position = 'absolute';
            this.css3DRenderer.domElement.style.top = '0px';
            this.css3DRenderer.domElement.style.left = '0px';
            this.css3DRenderer.domElement.style.pointerEvents = 'none'
            document.body.appendChild(this.css3DRenderer.domElement);

            // 添加模型
            this.addmodel()

            // 光源
            const light = new THREE.DirectionalLight(0xffffff);
            light.position.set(20, 10, 1305);
            this.scene.add(light);

            // 相机位置
            this.camera.position.set(0, 0, 5);

            // 加载场景控制插件
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
            this.controls.enableZoom = true; // 启用缩放
            this.controls.minDistance = 1; // 最小缩放距离
            this.controls.maxDistance = 100; // 最大缩放距离
            this.controls.enableDamping = true; // 启用阻尼效果
            this.controls.dampingFactor = 0.05; // 阻尼系数

            // 渲染循环
            this.animate();
        },
    },
}
</script>
<style scoped></style>


http://www.niftyadmin.cn/n/5868475.html

相关文章

lowagie(itext)老版本手绘PDF,包含页码、水印、图片、复选框、复杂行列合并等。

入口类&#xff1a;exportPdf ​ package xcsy.qms.webapi.service;import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.common.utils.StringUtils; import com.ibm.icu.text.RuleBasedNumberFormat; import com.lowa…

全域旅游景区导览系统:赋能智慧旅游生态,破解行业核心难题

全域旅游景区导览系统&#xff1a;赋能智慧旅游生态&#xff0c;破解行业核心难题 ——整合旅游商城、非遗文化与全域服务的一站式解决方案 一、行业痛点&#xff1a;传统旅游服务模式的局限性 随着旅游业从单一景区游览向“全域旅游”转型&#xff0c;传统服务模式暴露出诸多…

前沿科技:改变未来生活的新趋势

人工智能在物流配送中的应用越来越广泛。它能有效优化配送路线&#xff0c;提高配送效率。很多物流公司已经开始使用这项技术&#xff0c;取得了显著成效。 首先&#xff0c;人工智能可以分析大量数据。它能获取天气、交通、货物信息等多个数据源。这些信息可以帮助系统快速制…

kafka数据拉取和发送

文章目录 一、原生 KafkaConsumer1、pom文件引入kafka2、拉取数据3、发送数据二、在spring boot中使用@KafkaListener1、添加依赖2、application.yml3、消息拉取:consumer4、自定义ListenerContainerFactory5、消息发送:producer6、kafka通过clientId鉴权时的鉴权失败问题一、…

【Python爬虫(69)】解锁游戏数据宝藏:Python爬虫实战攻略

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

01-03基于vs2022的c语言笔记——软件安装,写程序前的准备,初识c语言

目录 前言 1.c语言编程环境的安装 安装vs2022 2.写程序前的准备 ​ 3.初识c语言 3-1第一个程序Hello World 3-2注释的使用 3-3变量 ​3-4-1 字面常量 3-4-2 用const修饰常量 3-4-3用#define来修饰常量 3-5关键字 3-6标识符 前言 本套笔记是基于英雄哪里出来c语言…

Tips :仿真竞争条件 指的是什么?

文章目录 **为什么会出现仿真竞争条件?****典型场景举例****System Verilog 如何解决竞争条件?****1. 使用 `program` 块隔离测试平台****2. 使用 `clocking` 块明确时序关系****3. 非阻塞赋值(`<=`)的合理使用****竞争条件的根本原因****总结****代码结构****1. 设计模…

单目摄像头物体深度计算基础原理

三维空间物体表面点位与其在图像中对应点之间的相互关系&#xff0c;必须建立相机成像的几何模型&#xff0c;这些几何模型参数就是相机参数&#xff0c;而相机参数的求解就是相机标定。 相机的参数矩阵包括内参和外参&#xff1a; 外参&#xff1a;决定现实坐标到摄像机坐标。…