threejs实现地图,并在鼠标移入地图后显示标签信息,这和echarts的tooltip很相似。
鼠标移入地图显示标签信息实例
标签代码
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
// 创建一个HTML标签
function tag() {
// 创建div元素(作为标签)
var div = document.createElement('div');
div.style.visibility = 'hidden';
div.innerHTML = 'GDP:'+0;
div.style.padding = '5px 10px';
div.style.color = '#fff';
div.style.fontSize = '16px';
div.style.position = 'absolute';
div.style.backgroundColor = 'rgba(25,25,25,0.5)';
div.style.borderRadius = '5px';
//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';
// // 避免renderer.domElement影响HTMl标签定位,设置top为0px
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.style.left = '0px';
// //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
labelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(labelRenderer.domElement);
export {tag,labelRenderer}
加载地图
loader.load(modelUrl + 'json/gdp.json', function (data2) {
var gdpObj = {};//每个省份的名字作为属性,属性值是国家对应GDP
// GDP最高对应红色,GDP最低对应白色
var color1 = new THREE.Color(0xffffff);
var color2 = new THREE.Color(0xff0000);
var gdpMax = 110000//设置一个基准值,以最高的广东gdp为准
data2.arr.forEach(function (obj) {
var gdp = obj.value;//当前省份GDP
gdpObj[obj.name] = gdp;//每个省份的名字作为属性,属性值是国家对应GDP
})
// 加载./china.json,结构和world.json 一样,省份对应国家
loader.load(modelUrl + 'json/china.json', function (data) {
// 访问所有省份边界坐标数据:data.features
data.features.forEach(function (area) {
// "Polygon":省份area有一个封闭轮廓
//"MultiPolygon":省份area有多个封闭轮廓
if (area.geometry.type === "Polygon") {
// 把"Polygon"和"MultiPolygon"的geometry.coordinates数据结构处理为一致
area.geometry.coordinates = [area.geometry.coordinates];
}
// 解析所有封闭轮廓边界坐标area.geometry.coordinates
lineGroup.add(metesBounds(area.geometry.coordinates));//省份边界轮廓插入组对象mapGroup
var mesh = shapeMesh(area.geometry.coordinates);
mesh.name = area.properties.name;//设置mesh对应的省份名字
meshGroup.add(mesh);//省份轮廓Mesh插入组对象mapGroup
// 颜色插值计算
var color = color1.clone().lerp(color2.clone(), gdpObj[mesh.name] / gdpMax);
// console.log(gdp / gdpMax)
mesh.material.color.copy(color);
var gdp = gdpObj[mesh.name];
mesh.gdp = gdp;//mesh自定义一个gdp属性,用于标签设置
mesh.经纬度 = area.properties.center;//用于控制标签位置
mesh.color = color;//记录下自身的颜色,以便选中改变mesh颜色的时候,不选中状态再改变回来
});
})
})
射线拾取获取地图信息
射线拾取获取地图数据
/**
* 射线投射器`Raycaster`的射线拾取选中网格模型对象函数choose()
* 选中的网格模型变色
*/
var label = tag();
scene.add(label);//标签插入场景中
console.log(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(meshGroup.children);
// console.log("射线器返回的对象", intersects);
// console.log("射线投射器返回的对象 点point", intersects[0].point);
// console.log("射线投射器的对象 几何体",intersects[0].object.geometry.vertices)
// intersects.length大于0说明,说明选中了模型
if (intersects.length > 0) {
chooseMesh = intersects[0].object;
chooseMesh.material.color.set(0x00ffff);//选中改变颜色
label.position.set(chooseMesh.经纬度[0], chooseMesh.经纬度[1], 0);
label.element.innerHTML = chooseMesh.name + 'GDP:' + chooseMesh.gdp + '亿元';
label.element.style.visibility = 'visible';
} else {
chooseMesh = null;
}
}
// addEventListener('click', choose); // 监听窗口鼠标单击事件
addEventListener('mousemove', choose); // 监听窗口鼠标滑动事件
渲染
将label标签渲染进去
// 渲染函数
function render() {
labelRenderer.render(scene, camera);
renderer.render(scene, camera); //执行渲染操作
requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();