简介:ECharts地图配合线特效展示北京公交路线的完整示例,面向数据可视化开发者和前端工程师,适合需要在地图上呈现路径轨迹、站点连接或流动动画效果的项目场景。压缩包共8个文件,含1个HTML页面、4个JavaScript脚本和3个JSON数据文件,整体仅1.11MB,结构简洁,其中JS负责图表配置与百度地图API集成,JSON存放中国及北京相关地理数据与公交线路坐标,HTML用于页面承载与效果预览。目前已有60人学习下载,资源提供了可直接运行的ECharts地图公交路线实例,通过代码可掌握地图初始化、线路数据绑定、线特效参数设置等关键实现,例如线条颜色渐变、宽度变化与流动动画效果,能够有效提升路线的视觉辨识度和信息传达效率,方便在此基础上扩展更多城市或自定义路径可视化。
1. 拆开资源包:ECharts地图线特效项目里有什么
拿到“ECharts地图-北京公交路线(线特效).rar”,解压后是典型的ECharts项目骨架:lib目录放着echarts 5.5.0及相关扩展,bmap是百度地图扩展,geo目录包含china.json和usa.json两种地图GeoJSON,images是贴图资源,根目录index.html负责初始化,数据文件lines-bus.json承载全部公交线路坐标。这是做数据可视化路径图时最常用的组合:地图底图加线路坐标,再用lines系列动画形成流动光效。很多开发者卡在“有坐标但画不出流动感”,这套资源把北京公交路线的数据、渲染方案和特效参数都备好了。适合想快速复现公交线路动画,或者想把自己的门店、物流路径做成动态效果的工程师。核心不在压缩包本身,而在如何理解lines-bus.json的数据结构和线特效参数组合。
2. ECharts地图渲染选型:bmap、geo与SVG渲染的取舍
2.1 为什么这个包选择了bmap而不是纯geo
打开index.html会看到它同时引入echarts与bmap.js。bmap是ECharts结合百度地图的扩展坐标系,能在百度地图瓦片之上绘制公交线路,底图自带行政边界、路网和兴趣点,无需自己维护GeoJSON边界。相比之下,geo坐标系需要显式注册china.json等地图数据,适合展示省界或区域填色,而公交路线的特点在于线状走向和城市级底图,用bmap可以获得街道级背景和缩放漫游体验。如果只是画一个静态的北京轮廓,geo就够;但要体现公交线路与道路的贴合关系,bmap是更直接的选择。还有一点,bmap的缩放级别跟百度地图的级别定义一致,而geo使用经纬度范围控制缩放,两者在交互手感上差别明显。
2.2 渲染器:SVG与Canvas如何影响线特效
ECharts 5.5.0默认使用Canvas渲染,但该资源包中建议将renderer显式设为svg。原因是公交路线数据量通常在几千到几万条坐标,Canvas在大量矢量绘制时的重绘成本高,SVG则能利用浏览器的DOM树进行局部更新,且线特效动画在SVG下调试更直观。不过规模再大时又要切回Canvas,因为它不受DOM节点上限的约束。我的做法是先用svg渲染开发,当公交线路超过200条且每条坐标超过500个点时,再通过开启渐进渲染回到Canvas模式,两者不是二选一,而是按数据规模切换。
// 使用SVG渲染,便于调试线特效的路径变化 var chart = echarts.init(document.getElementById('map'), null, { renderer: 'svg' }); chart.setOption({ bmap: { center: [116.404, 39.915], zoom: 10, roam: true // 允许拖拽缩放 } });上述代码将ECharts实例初始化为SVG渲染器,然后通过bmap节点配置百度地图的中心点和缩放级别。roam设置为true允许用户拖拽缩放。如果这里不指定renderer,ECharts 5.5.0会默认选择Canvas,两者对线特效的影响体现在动画帧数上,SVG在调试时还能直接查看DOM中的路径节点,方便定位坐标异常。需要注意的是,bmap配置只在首次setOption时生效,后续更新系列数据不会重置地图视图。
2.3 bmap与geo的坐标系差异及数据适配
bmap坐标系与ECharts默认的GeoJSON坐标系不同,bmap下坐标是百度经纬度BD09,而geo下使用GCJ02或WGS84的GeoJSON边界。因此同一个lines-bus.json不能同时用于bmap和geo,必须先确认坐标来源。如果从高德地图拾取坐标,那是GCJ02,需要先转成BD09再交给bmap。常见做法是用gcoord库做转换,而不是手工加减偏移,不然线路会整体偏出数百米。下表列出了三个坐标系的适用场景:
| 坐标系 | 常见来源 | ECharts使用方式 |
|---|---|---|
| WGS84 | GPS设备 | 注册自定义GeoJSON |
| GCJ02 | 高德地图、腾讯地图 | geo或转码后用于bmap |
| BD09 | 百度地图 | bmap扩展 |
实际项目中,我一般会写一个数据校验脚本,读取原始JSON里的坐标前三个点,和百度地图搜索到的真实站名做距离比对,偏差超过100米就判定为坐标系不匹配。另一个判断技巧是看线条是否整体平移、扭曲:如果线路在路口位置出现几十米的偏移,八成是GCJ02没转BD09,而不是数据本身写错。
3. 公交路线坐标处理:从高德/天地图到lines-bus.json
3.1 lines-bus.json的内部结构
解包后最值得研究的文件是lines-bus.json。它的格式不是普通经纬度数组,而是ECharts lines系列的标准结构:一个JSON数组,每个元素代表一条公交线路,包含coords字段,值为二维数组,后一个坐标是前一个坐标的下一个途经点;配合polyline: true,整条线路就是多个坐标点串联成的折线。下面是一段典型的线路数据:
[ { "name": "1路", "coords": [ [116.354, 39.921], [116.381, 39.915], [116.441, 39.914] ] }, { "name": "2路", "coords": [ [116.302, 39.953], [116.337, 39.942], [116.402, 39.945] ] } ]name在提示框中显示线路名;coords的每个子数组都是[lng, lat],顺序必须与公交行驶方向一致,否则绘制出来的线会来回折叠。如果只有站点列表,没有轨道轨迹,建议不要直接相邻站点之间直线连线,而应该把站间实际道路的折点加进去,否则公交线路会变成穿过建筑群的直线。字段的含义如下表:
| 字段 | 类型 | 说明 |
|---|---|---|
| name | string | 线路名,提示框与图例使用 |
| coords | number[][] | 按行驶方向排列的经纬度坐标,顺序不能乱 |
| color | string | 可选,不设置时使用系列默认颜色 |
3.2 通过坐标拾取获取线路轨迹
资源包里没有写死的使用步骤,但实际做数据时,我会在天地图坐标拾取器或者高德地图API中拉取公交路线。高德的公交路径规划接口能返回步行和公交分段,从中提取shape坐标,然后按公交段拼接成完整线路。天地图坐标拾取器适合人工校正少量关键站点,但不适合批量获取几千条线路。对于批量任务,我建议用高德Web服务API的公交路径规划,请求一次返回单条线路上步行和公交的坐标点串。
import requests import json def fetch_bus_line(city='北京', line='1路'): url = 'https://restapi.amap.com/v3/direction/transit/integrated' params = { 'origin': '116.354,39.921', 'destination': '116.441,39.914', 'city': city, 'strategy': '0', 'key': 'your_key' } resp = requests.get(url, params=params) data = resp.json() segments = [] # 遍历公交方案,提取公交段的polyline字段 for transit in data.get('route', {}).get('transits', []): for step in transit.get('segments', []): bus = step.get('bus', {}) if bus: segments.append({'name': bus.get('name'), 'polyline': bus.get('polyline')}) return segments这个脚本请求高德公交路径规划接口,把返回的polyline字段拆出来。注意高德返回的polyline是用分号连接的经纬度对,使用前要转成数组。返回顺序不一定和实际行驶方向一致,需要用首尾站名做方向矫正。另外,高德的公交分段里可能混入步行段,需要判断bus节点是否为空,否则会把步行的折线也当成公交线路画进地图,视觉上出现断断续续的短线。
3.3 数据压缩与抽稀
北京公交线路坐标点非常多,全量放进lines-bus.json会导致index.html体积过大,首次加载和动画都变慢。常见做法有两点:一是按照地图缩放级别抽稀,当zoom小于10时只保留每10个点中的一个;二是删除相邻重复点。抽稀不能只按距离阈值,还要保留拐弯处的点,否则线路会失去道路形状。我一般用Douglas-Peucker算法的简化实现,设置容差为0.001度,在保留北京街道走向的同时减少约60%的点位。
function simplify(coords, tolerance) { if (coords.length < 3) return coords; var dmax = 0, index = 0; for (var i = 1; i < coords.length - 1; i++) { var d = distanceToSegment(coords[i], coords[0], coords[coords.length - 1]); if (d > dmax) { index = i; dmax = d; } } if (dmax > tolerance) { var left = coords.slice(0, index + 1); var right = coords.slice(index); return simplify(left, tolerance).concat(simplify(right, tolerance).slice(1)); } return [coords[0], coords[coords.length - 1]]; }这里的distanceToSegment函数计算点到线段的垂直距离,这是经典的Douglas-Peucker算法实现,不展开完整代码。tolerance参数控制抽稀力度,0.001在公交路线上大约对应100米,既能保持线路形状又能把点位减半。如果发现线路在某段变成了直线,说明容差太大,需要调小到0.0005。抽稀后记得重新生成lines-bus.json,并在代码中打印每条线路的点数,前后对比确认没有删掉关键拐点。
4. 线特效配置实战:在index.html里实现流光公交线路
4.1 最小可运行配置
打开资源包中的index.html,在引入echarts.min.js和bmap.js之后,核心代码是初始化图表并配制series。最简配置是把数据直接交给lines系列,并指定coordinateSystem: 'bmap'。这一步跑通后再加特效,否则容易把问题混在一起。如果页面空白,优先检查bmap.js是否在echarts之后加载,或者看Network里json是否被浏览器拦截。
<script src="lib/echarts.min.js"></script> <script src="lib/bmap.min.js"></script> <div id="map" style="width: 100%; height: 600px;"></div> <script> // fetch加载大JSON,避免阻塞地图初始化 var chart = echarts.init(document.getElementById('map'), null, { renderer: 'svg' }); fetch('json/lines-bus.json') .then(res => res.json()) .then(data => { chart.setOption({ bmap: { center: [116.404, 39.915], zoom: 10, roam: true }, series: [{ type: 'lines', coordinateSystem: 'bmap', polyline: true, data: data, lineStyle: { color: '#1e90ff', width: 2, opacity: 0.6 } }] }); }); </script>上述代码用fetch请求本地的lines-bus.json数据。之所以用fetch而不是直接JSON.parse,是因为线路文件较大,异步加载不阻塞地图首屏。注意bmap.center和zoom只在首次setOption时生效,如果数据加载完成后想移动到城区,需要单独用百度地图实例的centerAndZoom方法,ECharts的setOption不会每次重置底图。
4.2 线特效:effect与trailLength实现流动光点
纯线条只能表示静态路径,体现“流动”感需要在lines系列中加入effect配置。ECharts中lines系列支持effect,开启后沿线会有光点或彩色轨迹流动。资源包里的线特效正是通过effect来模拟公交车运行方向。延时和尾迹长度的组合决定了视觉节奏:
series: [{ type: 'lines', coordinateSystem: 'bmap', polyline: true, data: data, effect: { show: true, period: 4, trailLength: 0.3, symbol: 'arrow', symbolSize: 6, color: '#ffd700' }, lineStyle: { color: 'rgba(30,144,255,0.4)', width: 1.5, opacity: 0.5 } }]period表示一个光点从线路起点到终点需要的秒数,设置为4适合北京市范围内快速流动;trailLength是尾迹长度,0到1之间,0.3代表留下30%的尾迹,数值越大拖影越长。如果用symbol: arrow还需要配合constantSpeed: true保持速度恒定,否则线路越长光点速度越快。这里有一个容易踩的坑:当trailLength设置过大超过0.8且线路较长时,动画会显得像整条线在渐变,丢失光点移动感。调试时先用symbol: 'circle'看光点,确认流动方向后再切回arrow。
| 参数 | 作用 | 推荐值 |
|---|---|---|
| period | 单程动画时间/秒 | 3-8 |
| trailLength | 尾迹比例 | 0.2-0.4 |
| symbolSize | 光点大小 | 4-8 |
| constantSpeed | 匀速模式 | true |
4.3 数据量过大时的渐进渲染
如果一次把上千条公交线路全部开启特效,页面很容易卡死。ECharts 5.5.0为此提供了progressive参数。它在渲染大量线时把数据分块提交给渲染器,避免主线程长时间阻塞。建议在lines系列里设置progressive: 2000,表示每帧最多新增2000个数据点,如果数据点总数超过阈值会自动开启渐进模式。
series: [{ type: 'lines', coordinateSystem: 'bmap', polyline: true, large: true, progressive: 2000, progressiveThreshold: 3000, data: data }]large: true会把lines系列的几何体合并成单个Polyline进行绘制,减少了draw call,但代价是线条样式无法单独定制,所有线路只能使用同一种颜色。progressiveThreshold是触发渐进模式的临界值,3000个数据点左右比较合适,低于这个数全量渲染,高于则分块。开启后如果动画卡顿,同时调低progressive到1000,或改成只给部分线路开特效。
5. 验证与扩展:让公交路线图更快、能接入vue3
验证线特效是否达到预期,不止肉眼看动画。到浏览器开发者工具中,先看Network里lines-bus.json加载耗时,如果超过500毫秒,可以压缩JSON或拆成按zoom分区加载。然后输入echarts.getInstanceByDom(document.getElementById('map'))获取实例,调用chart.getOption().series[0].data确认数据有没有被ECharts正确读取。Debug时还可以临时关掉effect,确认生效的是特效参数,而不是坐标数据损坏。如果线条没有出现,检查坐标系是否匹配,常见错误是把BD09坐标塞进geo地图,结果线画在海里。
关于渲染性能的边界,我这里给出一个测试过的经验:SVG渲染在200条公交线路、每条100个点左右,并能保持60 FPS。超过这个规模建议把large: true打开,并把渲染切回Canvas,代价是丢失部分细节。另外,animation参数可以把初始动画缩短到300毫秒以下,避免页面加载时线路绘制动画拖慢交互:
chart.setOption({ animationDuration: 300, // 缩短初始动画,避免线路绘制拖慢交互 animationEasing: 'linear' });一个常见扩展是把这套资源的核心逻辑封装成vue3 echarts组件。做法是安装echarts和echarts-bmap包,在onMounted里初始化图表,在watch中监听路线数据变化并调用chart.setOption。注意组件卸载时调用chart.dispose释放地图实例。封装后,原本的index.html可以直接当作演示页保留,数据从接口读取时只替换fetch的URL。
最后的进阶玩法是把静态的bus路线变成动态路径回放。思路是在setOption之后,用setInterval每秒钟更新一次series[0].data,从lines-bus.json中按顺序取线路。同时配合chart.dispatchAction中的highlight指定某一条线路,在地图上强调当前运行线路。这种方法常用于物流配送或巡更轨迹的可视化,只需把数据源换成自己的GPS坐标即可。
本文还有配套的精品资源,点击获取