96SEO 2026-04-22 10:35 48
单纯的平面地图早就让人审美疲劳了。不管是智慧城市的指挥舱,还是业务数据的年终汇报,大家dou渴望那种一眼就Neng抓住眼球、充满科技感的视觉冲击力。ECharts 虽然好用,但在追求极致 3D 体验的场合,总显得有点力不从心。于是我决定不再妥协,撸起袖子用 Three.js 配合 Vue 3,从零手写了一个 3D 地理可视化组件——ThreeMap。

这不仅仅是一个 Demo,它是我在无数个项目中踩坑、填坑后出的实战结晶。今天我就毫无保留地把这套方案的设计思路和核心实现分享出来希望Neng给正在为 3D 地图发愁的你一点启发。咱们不搞虚的,直接上干货。
一、 核心架构:为什么选择 Vue 3 + Three.js?可Neng有人会问,市面上现成的库那么多,为什么要自己造轮子?说实话,现成的方案往往要么定制性太差,要么包体臃肿。而自己掌控核心代码,意味着你Ke以随心所欲地调整每一个像素的渲染逻辑。
我们的技术栈非常纯粹:Vue 3 / Three.js / TypeScript / d3-geo / Vite。
整体架构采用了职责分离的设计思想。`ThreeMap.ts` 作为核心类,专门负责 Three.js 的生命周期管理、场景渲染和事件系统;而 Vue 组件则作为一个包装层,处理 UI 交互、下钻逻辑以及控制面板。各个图层的绘制逻辑,比如飞线、柱状图、光圈,dou被拆解成了独立的 `draw*` 工具函数。这种组合方式极其灵活,你想加个图层?写个函数扔进去就行。
1.1 项目结构速览为了让你对代码有个直观的印象,我先贴一下核心目录结构。别被文件名吓到,其实逻辑hen清晰:
ThreeMap/
├── ThreeMap.ts # 核心类:Three.js 渲染引擎封装
├── index.vue # Vue 组件包装层
├── types.ts # 完整 TypeScript 类型定义
├── options/ # 配置工厂函数
│ ├── threeOption.ts # createDefaultOptions
│ ├── createMarker.ts
│ ├── createFlight.ts
│ ├── createCylinder.ts
│ ├── createPrism.ts
│ └── createScatter.ts
└── utils/ # 各图层绘制函数
├── drawDistrict.ts # 地图拉伸
├── drawGlow.ts # 辉光后处理
├── drawFlight.ts # 飞线动画
├── drawCylinder.ts # 圆柱图层
├── drawScatter.ts # 扩散点
├── drawMarker.ts # 标记点
├── drawPrism.ts # 棱柱图层
├── drawPlane.ts # 镜面/底色平面
├── drawGrid.ts # 网格线
├── drawFoundation.ts# 底图装饰盘
├── drawOutLine.ts # 轮廓线
├── addTooltip.ts # Tooltip 管理
└── helpers.ts # 颜色插值、坐标工具
二、 从 GeoJSON 到 3D 网格:地图生成的秘密
3D 地图渲染的第一步,也是Zui关键的一步,就是把平面的 GeoJSON 数据转换成 Three.js Neng识别的 3D 网格。这中间涉及到两个核心问题:坐标转换和几何体拉伸。
2.1 坐标系的魔法:d3-geoGeoJSON 使用的是 WGS84 经纬度坐标系,而 Three.js 生活在笛卡尔 3D 空间里。这俩怎么对上号?这时候就需要 d3-geo 出场了。我利用 `d3-geo` 的 `geoMercator` 投影,将经纬度映射到屏幕的 X/Y 坐标,再配合地图的宽高比自动计算缩放比例。
这一步就像是给地球拍了一张透视照,把球面上的点“压”平到了我们的画布上。
2.2 拉伸几何体:让地图“站”起来有了轮廓,还是平的。怎么变立体?用 `THREE.Shape` 描绘行政区边界的 2D 轮廓,然后通过 `THREE.ExtrudeGeometry` 沿 Z 轴进行拉伸。瞬间,原本扁平的线条就变成了带厚度的 3D 模型。
这里有个小细节:为了让视觉效果geng丰富,我使用了双材质数组 ``。顶面Ke以贴纹理,侧面Ke以给个深色,营造出一种厚重的科技感。
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: options.config.depth,
bevelEnabled: false,
})
// 双材质: 顶面 侧面
const mesh = new THREE.Mesh
mesh.rotation.x = -0.5 * Math.PI // XY 平面旋转到 XZ 平面
三、 视觉炸裂:辉光与后处理的博弈
大家kan 3D 地图,Zui爱的那种“霓虹灯”效果,其实离不开后处理。但是Three.js 的 `UnrealBloomPass` 有个经典的坑:辉光会“污染”全场景。你想要边界线发光,结果连旁边的文字标签也变得模糊不清,这就hen尴尬。
3.1 双 Composer 架构:精准控制辉光为了解决这个问题,我设计了一套双 Composer 架构。简单来说就是渲染两次。
第一次用 `bloomComposer` 渲染。在这个阶段,我会遍历场景,把那些不需要发光的对象临时隐藏,只保留标记了 `_isGlow = true` 的对象。这样,`UnrealBloomPass` 就只处理这些发光层。
第二次用 `finalComposer` 渲染完整场景。通过一个自定义的 `ShaderPass`,把刚才渲染好的辉光纹理叠加到基础渲染上。这样一来边界线亮晶晶,文字依然清晰锐利,完美!
bloomComposer └── RenderPass
└── UnrealBloomPass
finalComposer
└── RenderPass
└── ShaderPass
└── SMAAPass
3.2 侧面扫光动画:让地图“活”起来
静态的地图虽然好kan,但总觉得少了点灵魂。我通过 `onBeforeCompile` 钩子,在侧面材质的 Fragment Shader 中注入了一段 GLSL 代码,实现了一个从底部向上移动的高亮扫光带。
这比直接写 ShaderMaterial 要好,因为它保留了 Three.js 内置的光照计算,同时又加上了我们的自定义动画逻辑。kan着光带在地图侧面缓缓流过那种科技感瞬间拉满。
float y = uStart + uTime * uSpeed;
float h = uHeight;
if {
float per = / h;
outgoingLight = mix;
}
四、 动态图层:飞线、柱状图与粒子系统
光有地图底座还不够,数据可视化才是灵魂。ThreeMap 支持多种图层类型,而且 API 设计得尽量靠近 ECharts,降低上手门槛。
4.1 飞线动画:不仅仅是画线hen多教程里的飞线就是简单的 `THREE.Line`,太生硬了。我的飞线是基于 `THREE.Points`实现的。每条飞线其实是一组沿着贝塞尔曲线均匀分布的粒子。
通过自定义 ShaderMaterial,我们Ke以控制头部粒子大、尾部渐隐,形成那种流星般的拖尾效果。贝塞尔曲线的控制点被抬高,形成优美的弧形。每帧动画中,通过递增 `time` uniform,Shader 计算粒子在线段中的位置,超出范围后重置,形成无限循环。
// 贝塞尔弧线:控制点抬高形成弧形
const controlPoint = new THREE.Vector3(
/ 2,
/ 2,
arcHeight, // 弧度高度
)
const curve = new THREE.QuadraticBezierCurve3
const points = curve.getPoints // 采样粒子坐标
4.2 丰富的 Series 图层
除了飞线,还支持各种炫酷的图表类型:
标记点支持 SVG 或图片图标,用 CSS2DRenderer 渲染,保证清晰。
扩散点带呼吸动画的波纹效果。
渐变圆柱 / 发光塔根据数据高度变化的 3D 柱状体。
棱柱三/四/六棱柱,比圆柱geng有棱角感。
series: , data: },
// 扩散点
{ type: 'scatter', spotColor: '#00FFFF', ringRatio: 0.5, data: },
// 渐变圆柱 / 发光塔
{ type: 'cylinder', mode: 'cylinder', color: , maxHeight: 10, data: },
// 三/四/六棱柱
{ type: 'prism', prismType: 3, size: 1, maxHeight: 10, data: },
// 飞线动画
{ type: 'flight', speed: 0.01, flightColor: , data: },
]
五、 交互体验:下钻与 Tooltip
一个合格的地图组件,必须得Neng“玩”起来。
5.1 丝滑的下钻体验双击某个省份,地图应该Neng平滑切换到该省份的视图。核心逻辑在于 `registerMap` + `setOption` 的组合拳。
当你双击区域时组件会自动加载该区域的 GeoJSON,利用 d3-geo 重新计算投影中心和缩放比例,调整相机位置。支持从全国 → 省级 → 市级逐层下钻,并提供返回按钮还原上一级。
// 1. 加载该省/市的 GeoJSON 文件
const mapJson = await map.registerMap
// 2. 重新渲染
map.setOption({
...currentOptions,
map: adcode, // 切换到新地图
data: newData,
})
5.2 清晰的 Tooltip 与标签
WebGL 渲染文字一直是个痛点,放大了容易糊。为了解决这个问题,我使用了 CSS2DRenderer。它Neng把 HTML DOM 元素精准地定位到 3D 对象的世界坐标上。这样,文字就是真正的 HTML,清晰度无敌,还Neng随便写 CSS 样式。
Tooltip 也是基于 `Raycaster` 射线检测实现的。鼠标移动时计算交叉点,命中后geng新 Tooltip DOM 的位置并渲染自定义 HTML 模板。
tooltip: {
formatter: => `
${data.name}
数值:${data.value}
`
}
六、 如何在 Vue 3 中集成?
说了这么多原理,到底怎么用?其实非常简单。你Ke以直接把源码拉下来也Ke以通过 npm 安装。
6.1 快速开始Ru果你想直接跑起来kankan效果,Ke以 clone 一下项目:
git clone https://github.com/zhanghang2017/threemap.git
cd threemap
npm install
npm run dev
6.2 在 Vue 组件中使用
这里展示一个Zui基础的用法。创建一个容器,引入 `ThreeMap` 类,初始化,然后 `setOption` 就完事了。
这个 ThreeMap 组件,从底层的 `ExtrudeGeometry` 拉伸,到中间层的 `UnrealBloomPass` 辉光优化,再到上层的 Vue 3 事件响应,形成了一个完整的闭环。它支持纯 Options 驱动,API 风格接近 ECharts,对业务层非常友好。
当然它还在不断进化中。比如未来我计划加入geng复杂的地形渲染、geng真实的天气效果,以及针对移动端的性Neng优化。
Ru果你正在Zuo数据可视化大屏,或者对 Three.js 感兴趣,不妨试试这个方案。Ru果觉得对你有帮助,欢迎去 GitHub 给个 Star,那是对我Zui大的鼓励!
GitHub 地址:github.com/zhanghang2017/threemap
希望这篇文章Neng帮你打开 3D 可视化的大门,别再让平面的地图限制了你的想象力。去创造点酷的东西吧!
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback