在给threejs 给地图添加气泡效果后,需要再增加一个信息弹窗效果,当鼠标移入气泡后,弹窗详细信息。
threejs 给地图添加气泡效果鼠标移入显示弹窗实例
气泡数据渲染
loader.load(modelUrl + 'json/cirMeshData.json', function (data) {
// GDP最高对应红色,GDP最低对应白色
var color1 = new THREE.Color(0x00ffcc);
var color2 = new THREE.Color(0xff6666);
var pmArr = [];//所有pm2.5数据集合
data.arr.forEach(function (obj) {
var pm25 = obj.value;//访问数据PM2.5值
pmArr.push(pm25);
})
pmArr.sort(compareNum);
// console.log('排序后pm2.5',pmArr);
var Max = pmArr[pmArr.length - 1]//PM2.5最大值作为基准值
data.arr.forEach(function (obj) {
var pm25 = obj.value
// 颜色插值计算
var color = color1.clone().lerp(color2.clone(), pm25 / Max);
// 气泡大小和pm2.5正相关,同时注意根据相机渲染范围来设置合适大小
var mesh = cirMesh(obj.coordinate[0], obj.coordinate[1], pm25 / Max * 1.2, color.getHex())
cirGroup.add(mesh);
mesh.name = obj.name;
mesh.pm25 = pm25;//mesh自定义一个gdp属性,用于标签设置
mesh.经纬度 = obj.coordinate;//用于控制标签位置
mesh.color = color;//记录下自身的颜色,以便选中改变mesh颜色的时候,不选中状态再改变回来
})
})
射线拾取气泡显示弹窗
增加100毫秒的延迟,在vue项目中无法马上获取到element增加100毫秒延迟便可以获取到。
/**
* 射线投射器`Raycaster`的射线拾取选中网格模型对象函数choose()
* 选中的网格模型变为半透明效果
*/
setTimeout(() => {
var pm = document.getElementById('pm');
var city = document.getElementById('city');
var div = document.getElementById('tag');
var label = tag(div);
scene.add(label);//标签插入场景中
var chooseMesh = null;//标记鼠标拾取到的mesh
function choose(event) {
if (chooseMesh) {
// 把上次选中的mesh设置为原来的颜色
chooseMesh.material.color.copy(chooseMesh.color);
} else {
label.element.style.visibility = 'hidden';//没有选中mesh,隐藏标签
}
var Sx = event.clientX; //鼠标单击位置横坐标
var Sy = event.clientY; //鼠标单击位置纵坐标
//屏幕坐标转WebGL标准设备坐标
var x = (Sx / window.innerWidth) * 2 - 1; //WebGL标准设备横坐标
var y = -(Sy / window.innerHeight) * 2 + 1; //WebGL标准设备纵坐标
//创建一个射线投射器`Raycaster`
var raycaster = new THREE.Raycaster();
//通过鼠标单击位置标准设备坐标和相机参数计算射线投射器`Raycaster`的射线属性.ray
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
//返回.intersectObjects()参数中射线选中的网格模型对象
// 未选中对象返回空数组[],选中一个数组1个元素,选中两个数组两个元素
var intersects = raycaster.intersectObjects(cirGroup.children);
// console.log("射线器返回的对象", intersects);
// console.log("射线投射器返回的对象 点point", intersects[0].point);
// intersects.length大于0说明,说明选中了模型
if (intersects.length > 0) {
chooseMesh = intersects[0].object;
chooseMesh.material.color.set(0x00ffff);//选中改变颜色
label.position.set(chooseMesh.经纬度[0], chooseMesh.经纬度[1], 0);
pm.innerHTML = chooseMesh?.pm25 ? chooseMesh.pm25 : 0;
city.innerHTML = chooseMesh.name;
label.element.style.visibility = 'visible';
} else {
chooseMesh = null;
}
}
// addEventListener('click', choose); // 监听窗口鼠标单击事件
addEventListener('mousemove', choose); // 监听窗口鼠标滑动事件
}, 100)
tag标签渲染
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
// 创建一个HTML标签
function tag(div) {
//div元素包装为CSS2模型对象CSS2DObject
var label = new CSS2DObject(div);
// div.style.pointerEvents = 'none';//避免HTML标签遮挡三维场景的鼠标事件
// 设置HTML元素标签在three.js世界坐标中位置
// label.position.set(x, y, z);
return label;//返回CSS2模型标签
}
// 创建一个CSS2渲染器CSS2DRenderer
var labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
//标签向右、向下偏移,以免遮挡选中的气泡
labelRenderer.domElement.style.top = '30px';
labelRenderer.domElement.style.left = '70px';
// //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
labelRenderer.domElement.style.pointerEvents = 'none';
export {tag,labelRenderer}
vue代码
<template>
<div class="canvasGLTFBody">
<div ref='canvasGLTF' class="canvasGLTF" id="canvasGLTF">
</div>
<div id="LoadingInfo" class="LoadingInfo"></div>
<div id="tag"
style="visibility:hidden;position: fixed;pointer-events:none;color:#ffffff;background:rgba(0,0,0,0.2);padding:6px 16px;border-radius:5px">
<div>
<span>城市:</span>
<span id="city" style="color:#00ffff">郑州</span>
</div>
<div style="height:1px;background:rgb(190,190,190);margin-top:4px;margin-bottom:4px;"></div>
<div>
<span>PM2.5:</span>
<span id="pm" style="color:#00ffff">19</span>
</div>
</div>
</div>
</template>
<script>
import {renderer} from './index.js'
import {labelRenderer} from './tag.js'
export default {
name: 'GLTFCanvas',
data() {
return {
list: []
}
},
created: function () {
},
mounted: function () {
var that = this
that.$refs.canvasGLTF.appendChild(labelRenderer.domElement)
that.$refs.canvasGLTF.appendChild(renderer.domElement)
},
methods: {}
}
</script>
<style lang="scss" scoped>
.canvasGLTFBody {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
}
.canvasGLTF {
position: relative;
width: 100%;
height: 100%;
}
.LoadingInfo {
width: 100%;
height: 100%;
position: absolute;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
color: #fff;
z-index: 1000000;
top: 0;
left: 0;
font-size: 20px;
display: none;
}
</style>