简介
地图下钻听起来是很厉害的技术,当你明白后会发现,实现它非常的简单。
实现思路
绘制中国地图
- 前面的章节已经绘制过了,这里只需要进行一些小改动。
- 之前的公用方法中
projection()
函数是全局方法,当地图下钻后中心点是要修改的,所以使用参数的方式传入。 - 给
drawExtrudeMesh()
方法生成的立体几何添加,唯一标识.properties
属性。
鼠标拾取
- 使用
.Raycaster()
射线追踪法,监听鼠标事件。
- 创建公用方法归一化坐标。
// 计算 以画布 开始为(0,0)点 的鼠标坐标
function getCanvasRelativePosition(event) {
const rect = canvas.getBoundingClientRect()
return {
x: ((event.clientX - rect.left) * canvas.width) / rect.width,
y: ((event.clientY - rect.top) * canvas.height) / rect.height
}
}
/**
* 获取鼠标在three.js 中归一化坐标
* */
function setPickPosition(event) {
let pickPosition = { x: 0, y: 0 }
// 计算后 以画布 开始为 (0,0)点
const pos = getCanvasRelativePosition(event)
// 数据归一化
pickPosition.x = (pos.x / canvas.width) * 2 - 1
pickPosition.y = (pos.y / canvas.height) * -2 + 1
return pickPosition
}
- 监听鼠标点击事件。
// 监听鼠标
window.addEventListener('click', onRay)
// 全局对象
let lastPick = null
function onRay(event) {
let pickPosition = setPickPosition(event)
const raycaster = new THREE.Raycaster()
raycaster.setFromCamera(pickPosition, camera)
// 计算物体和射线的交点
const intersects = raycaster.intersectObjects([map], true)
// 数组大于0 表示有相交对象
if (intersects.length > 0) {
if (lastPick) {
if (lastPick.object.properties !== intersects[0].object.properties) {
lastPick.object.material.color.set('yellow')
lastPick = null
}
}
if (intersects[0].object.properties) {
intersects[0].object.material.color.set('blue')
}
lastPick = intersects[0]
} else {
if (lastPick) {
// 复原
if (lastPick.object.properties) {
lastPick.object.material.color.set('yellow')
lastPick = null
}
}
}
}
绘制省份场景
- 和绘制地图同样的原理,唯一的区别就是设置重庆市为中心。
- 创建新的场景,加载数据在新场景中绘制地图。
- 省份地图坐标位置间隔小,使用
.scale
放大场景。
// 重庆市为中心
const projection2 = d3.geoMercator().center([106.557691, 29.559296]).translate([0, 0])
// 重庆市地图
loader.load('./file/500000_full.json', (data) => {
const jsondata = JSON.parse(data)
operationData2(jsondata)
})
// 场景2
const scene2 = new THREE.Scene()
const map2 = new THREE.Object3D()
// 解析数据
function operationData2(jsondata) {
// 全国信息
const features = jsondata.features
features.forEach((feature) => {
// 单个省份 对象
const province = new THREE.Object3D()
// 地址
province.properties = feature.properties.name
const coordinates = feature.geometry.coordinates
const color = 'yellow'
// const color = ['重庆市', '上海市'].includes(feature.properties.name) ? 'blue' : 'yellow'
if (feature.geometry.type === 'MultiPolygon') {
// 多个,多边形
coordinates.forEach((coordinate) => {
// coordinate 多边形数据
coordinate.forEach((rows) => {
const mesh = drawExtrudeMesh(rows, color, projection2)
const line = lineDraw(rows, color, projection2)
// 唯一标识
mesh.properties = feature.properties.name
province.add(line)
province.add(mesh)
})
})
}
if (feature.geometry.type === 'Polygon') {
// 多边形
coordinates.forEach((coordinate) => {
const mesh = drawExtrudeMesh(coordinate, color, projection2)
const line = lineDraw(coordinate, color, projection2)
// 唯一标识
mesh.properties = feature.properties.name
province.add(line)
province.add(mesh)
})
}
map2.add(province)
})
// 放大
map2.scale.set(8, 8, 1)
scene2.add(map2)
}
- 这里只绘制了重庆市,其他省市的绘制是一样的。
- 修改渲染函数加载新场景看看效果。
// renderer.render(scene, camera)
renderer.render(scene2, camera)
切换场景
- 基础条件都创建好了,我们只需要修改渲染函数。在函数中判断唯一标识是哪个省市,就切换到展示那个省市。
// 渲染
function render() {
// 根据唯一标识 判断展示省份
if (lastPick && lastPick.object.properties === '重庆市') {
renderer.render(scene2, camera)
} else {
renderer.render(scene, camera)
}
requestAnimationFrame(render)
}