使用CSS3DRenderer/CSS2DRenderer給模型上面添加html標簽

?先放一下預覽圖

主要使用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 -= 70imgLabel1.position.x += 90let imgLabel2 = this.addimg(img2, () => {this.showImageInfo('https://www.baidu.com')})obj.add(imgLabel2);imgLabel2.position.y -= 120imgLabel2.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);// 創建 CSS2DRendererthis.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);// 創建 CSS3DRendererthis.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.pswp.cn/news/896291.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/896291.shtml
英文地址,請注明出處:http://en.pswp.cn/news/896291.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

完美隱藏滾動條方案 (2024 最新驗證)

完美隱藏滾動條方案 (2024 最新驗證) css /* 全局隱藏豎直滾動條但保留滾動功能 */ html {overflow: -moz-scrollbars-none; /* Firefox 舊版 */scrollbar-width: none; /* Firefox 64 */-ms-overflow-style: none; /* IE/Edge */overflow-y: overlay; …

Linux 內核配置機制詳細講解

本文是對 Linux 內核配置機制 make menuconfig 的 超詳細分步解析&#xff0c;涵蓋其工作原理、界面操作、配置邏輯及底層實現&#xff1a; 一、內核配置系統概述 Linux 內核的配置系統是一個 基于文本的交互式配置工具鏈&#xff0c;核心目標是通過定義 CONFIG_XXX 宏來控制內…

視頻裂變加群推廣分享引流源碼

源碼介紹 視頻裂變加群推廣分享引流源碼 最近網上很火&#xff0c;很多人都在用&#xff0c;適合引流裂變推廣 測試環境&#xff1a;PHP7.4(PHP版本不限制) 第一次訪問送五次觀看次數&#xff0c;用戶達到觀看次數后需要分享給好友或者群,好友必須點擊推廣鏈接后才會增加觀看次…

python-leetcode-每日溫度

739. 每日溫度 - 力扣&#xff08;LeetCode&#xff09; class Solution:def dailyTemperatures(self, temperatures: List[int]) -> List[int]:n len(temperatures)answer [0] * nstack [] # 存儲索引for i, temp in enumerate(temperatures):while stack and temperat…

文件下載技術的終極選擇:`<a>` 標簽 vs File Saver.js

文件下載技術的終極選擇&#xff1a;<a> 標簽 vs File Saver.js 在 Web 開發中&#xff0c;文件下載看似簡單&#xff0c;實則暗藏玄機。工作種常糾結于 <a> 標簽的原生下載和 File Saver.js 等插件的靈活控制之間。本文將從原理、優缺點、場景對比到實戰技巧&…

deepseek sse流式輸出

鏈接 semi-ui-vue聊天組件 - 可以用這個組件優化界面 sse服務端消息推送 webflux&webclient Hi-Dream-Blog - 參考這個博客&#xff0c;可以在后臺將markdown語法轉為html 文章目錄 鏈接效果代碼pom.xmlDeepSeekControllerWebConfigDeepSeekClientAiChatRequestAiChatM…

Linux時間日期類指令

1、data指令 基本語法&#xff1a; date &#xff1a; 顯示當前時間date %Y : 顯示當前年份date %m &#xff1a; 顯示當前月份date %d &#xff1a; 顯示當前哪一天date “%Y-%m-%d %H:%M:%S" &#xff1a; 顯示年月日時分秒date -s 字符串時間 &#xff1a; 設置系統時…

SQLMesh 系列教程9- 宏變量及內置宏變量

SQLMesh 的宏變量是一個強大的工具&#xff0c;能夠顯著提高 SQL 模型的動態化能力和可維護性。通過合理使用宏變量&#xff0c;可以實現動態時間范圍、多環境配置、參數化查詢等功能&#xff0c;從而簡化數據模型的開發和維護流程。隨著數據團隊的規模擴大和業務復雜度的增加&…

鵬哥c語言數組(初階數組)

前言&#xff1a; 對應c語言視頻54集 內容&#xff1a; 一維數組的創建 數組是一組相同元素的集合&#xff0c; 數組的創建方式 type_t就是數組的元素類型&#xff0c;const_n是一個常量表達式&#xff0c;用來指定數組的大小 c99標準之前的&#xff0c;數組的大小必須是…

爬蟲運行后如何保存數據?

爬蟲運行后&#xff0c;將獲取到的數據保存到本地或數據庫中是常見的需求。Python 提供了多種方式來保存數據&#xff0c;包括保存為文本文件、CSV 文件、JSON 文件&#xff0c;甚至存儲到數據庫中。以下是幾種常見的數據保存方法&#xff0c;以及對應的代碼示例。 1. 保存為文…

計算機視覺:經典數據格式(VOC、YOLO、COCO)解析與轉換(附代碼)

第一章&#xff1a;計算機視覺中圖像的基礎認知 第二章&#xff1a;計算機視覺&#xff1a;卷積神經網絡(CNN)基本概念(一) 第三章&#xff1a;計算機視覺&#xff1a;卷積神經網絡(CNN)基本概念(二) 第四章&#xff1a;搭建一個經典的LeNet5神經網絡(附代碼) 第五章&#xff1…

linux--多進程基礎(2)GDB多進程調試(面試會問)

將其中的命令記住就行。 總結&#xff1a;GDB下默認調試父進程&#xff0c;可以設置調試父進程還是子進程&#xff0c;也可以設置調試模式&#xff0c;調試模式默認是on即一個在調試另一個直接運行&#xff0c;off就是另一個進程掛起&#xff0c;最后可以查看調試進程 一般默認…

Cramér-Rao界:參數估計精度的“理論底線”

Cramr-Rao界&#xff1a;參數估計精度的“理論底線” 在統計學中&#xff0c;當我們用數據估計一個模型的參數時&#xff0c;總希望估計結果盡可能精確。但精度有沒有一個理論上的“底線”呢&#xff1f;答案是有的&#xff0c;這就是Cramr-Rao界&#xff08;Cramr-Rao Lower …

【復習】Redis

數據結構 Redis常見的數據結構 String&#xff1a;緩存對象Hash&#xff1a;緩存對象、購物車List&#xff1a;消息隊列Set&#xff1a;點贊、共同關注ZSet&#xff1a;排序 Zset底層&#xff1f; Zset底層的數據結構是由壓縮鏈表或跳表實現的 如果有序集合的元素 < 12…

Git add --- error: Filename too long

0 Preface/Foreword 1 解決辦法 git config --system core.longpaths true

在 Spring Boot 中使用 `@Autowired` 和 `@Bean` 注解

文章目錄 在 Spring Boot 中使用 Autowired 和 Bean 注解示例背景 1. 定義 Student 類2. 配置類&#xff1a;初始化 Bean3. 測試類&#xff1a;使用 Autowired 注解自動注入 Bean4. Spring Boot 的自動裝配5. 總結 在 Spring Boot 中使用 Autowired 和 Bean 注解 在 Spring Bo…

【AI+智造】DeepSeek價值重構:當采購與物控遇上數字化轉型的化學反應

作者&#xff1a;Odoo技術開發/資深信息化負責人 日期&#xff1a;2025年2月24日 引言&#xff1a;從事企業信息化工作16年&#xff0c;我見證過無數企業從手工臺賬到ERP系統的跨越。但真正讓采購和物控部門脫胎換骨的&#xff0c;是融合了Deepseek AI的Odoo數字化解決方案——…

qt-C++筆記之創建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并關聯視圖和場景的方法

qt-C++筆記之創建和初始化 QGraphicsScene 和 QGraphicsView 并關聯視圖和場景的方法 code review! 參考筆記 1.qt-C++筆記之創建和初始化 QGraphicsScene 和 QGraphicsView 并關聯視圖和場景的方法 2.qt-C++筆記之QGraphicsScene和 QGraphicsView中setScene、通過scene得到vie…

Java Map實現類面試題

Java Map實現類面試題 HashMap Q1: HashMap的實現原理是什么&#xff1f; HashMap基于哈希表實現&#xff0c;使用數組鏈表紅黑樹&#xff08;Java 8&#xff09;的數據結構。 public class HashMapPrincipleExample {// 模擬HashMap的基本結構public class SimpleHashMap&…

Win32/ C++ 簡易對話框封裝框架(多語言, 通知欄菜單, 拖拽文件處理)

Win32 簡易對話框封裝簡易框架示例 1. 菜單操作: 多語言 2. 通知欄圖標菜單 3. 其他操作: 接受拖拽文件等等 CDialogFrame.h #pragma once #include "CWindow/CDialogBase.h" #include "CNSFHeader.h" #include "Win32Utils/CBytesUtils.h" …