Three.js + D3.js 实战:手把手教你打造可交互3D中国地图(附完整代码)

Three.js + D3.js 实战:从零构建高交互性3D中国地图可视化系统

最近在做一个数据大屏项目,客户要求将全国的业务数据以三维地图的形式直观呈现,并且要能点击、能下钻、有动画。市面上现成的图表库要么效果太“平”,要么定制化程度不够。折腾了一圈,最终决定用 Three.js 和 D3.js 自己动手搭一套。这个组合听起来有点硬核,但实际用下来,你会发现它给的可控性和表现力是其他方案难以比拟的。

这篇文章,我就把自己从踩坑到实现的全过程梳理出来,重点不是给你一段能直接拷贝的代码,而是帮你理解背后的原理和设计思路。无论你是想做一个酷炫的数据看板,还是想在产品里加入地理维度的3D分析,相信这些实战经验都能给你带来启发。我们会从最基础的数据获取和坐标转换讲起,一步步实现地图渲染、交互高亮、视角聚焦,甚至飞线和光柱这些高级特效。准备好了吗?我们开始。

1. 项目基石:数据、坐标与三维场景搭建

任何地图可视化的起点都是数据。对于中国地图,我们需要一份包含各省份边界坐标的 GeoJSON 数据。这类数据可以从一些公开的地理信息平台获取。拿到数据后,第一个要解决的问题是:如何将经纬度坐标转换成 Three.js 能理解的平面直角坐标?

1.1 GeoJSON 数据与墨卡托投影

GeoJSON 数据中的 coordinates 字段存储的是经纬度数组,例如 [116.405285, 39.904989] 代表北京。Three.js 的三维空间使用的是笛卡尔坐标系(x, y, z),我们需要一个转换函数。这就是 D3.js 的 d3-geo 模块大显身手的地方,它提供了成熟的墨卡托投影方法。

import * as d3 from 'd3';

// 创建墨卡托投影转换函数
const projection = d3.geoMercator()
  .center([104.0, 37.5]) // 以中国地理中心为投影中心
  .scale(120)            // 缩放系数,决定地图大小
  .translate([0, 0]);    // 平移,通常先设为[0,0],后续调整

// 使用:将经纬度 [116.405, 39.905] 转换为 Three.js 中的 (x, y)
const [x, y] = projection([116.405285, 39.904989]);
// 注意:转换后的y轴方向与Three.js默认可能相反,有时需要取负值 (-y)

这里有几个参数需要根据你的画布大小和需求调整:

  • center: 投影的中心点经纬度,设为中国的中心,能让地图在视觉上更居中。
  • scale: 这是最关键的一个参数。值太小,地图会缩在屏幕一角;值太大,地图会超出画布。需要反复调试。
  • translate: 通常与 scale 配合使用,用于将投影后的地图平移到场景中心。

提示:调试阶段,可以创建一个简单的点(如 THREE.SphereGeometry)放在投影后的坐标上,快速验证你的 projection 函数是否正确,以及 scale 是否合适。

1.2 初始化 Three.js 基础环境

在转换坐标之前,我们需要先搭建好 Three.js 的“舞台”。这包括场景、相机、渲染器和控制器。

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

// 1. 场景 - 所有3D对象的容器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1a2a); // 设置一个深蓝色背景

// 2. 相机 - 观察场景的视角
const camera = new THREE.PerspectiveCamera(
  60, // 视野角度 (FOV)
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近裁剪面
  2000 // 远裁剪面
);
camera.position.set(0, -100, 150); // 初始相机位置,俯视角度

// 3. 渲染器 - 将3D场景绘制到HTML Canvas上
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio); // 适配高清屏
document.body.appendChild(renderer.domElement);

// 4. 轨道控制器 - 允许用户用鼠标拖拽、缩放、旋转地图
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼,产生平滑的交互效果
controls.dampingFactor = 0.05;

// 5. 光源 - 没有光,3D物体将是全黑的
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); // 环境光,均匀照亮
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // 方向光,产生阴影和立体感
directionalLight.position.set(100, 100, 50);
scene.add(directionalLight);

// 6. 动画循环
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // 必须在动画循环中更新控制器
  renderer.render(scene, camera);
}
animate();

// 7. 响应窗口大小变化
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.inner
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值