threejs 给地图添加气泡效果鼠标移入显示弹窗 学习笔记

threejs yekong

在给threejs 给地图添加气泡效果后,需要再增加一个信息弹窗效果,当鼠标移入气泡后,弹窗详细信息。

threejs 给地图添加气泡效果鼠标移入显示弹窗

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>

最终实现效果

webGL 3D地图可视化实例

喜欢