?先放一下預覽圖
主要使用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 = '×'; // 叉號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>