ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ECharts地图线特效实战:北京公交路线动态流光

ECharts地图线特效实战:北京公交路线动态流光 简介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两种地图GeoJSONimages是贴图资源根目录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允许用户拖拽缩放。如果这里不指定rendererECharts 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使用方式WGS84GPS设备注册自定义GeoJSONGCJ02高德地图、腾讯地图geo或转码后用于bmapBD09百度地图bmap扩展实际项目中我一般会写一个数据校验脚本读取原始JSON里的坐标前三个点和百度地图搜索到的真实站名做距离比对偏差超过100米就判定为坐标系不匹配。另一个判断技巧是看线条是否整体平移、扭曲如果线路在路口位置出现几十米的偏移八成是GCJ02没转BD09而不是数据本身写错。3. 公交路线坐标处理从高德/天地图到lines-bus.json3.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]顺序必须与公交行驶方向一致否则绘制出来的线会来回折叠。如果只有站点列表没有轨道轨迹建议不要直接相邻站点之间直线连线而应该把站间实际道路的折点加进去否则公交线路会变成穿过建筑群的直线。字段的含义如下表字段类型说明namestring线路名提示框与图例使用coordsnumber[][]按行驶方向排列的经纬度坐标顺序不能乱colorstring可选不设置时使用系列默认颜色3.2 通过坐标拾取获取线路轨迹资源包里没有写死的使用步骤但实际做数据时我会在天地图坐标拾取器或者高德地图API中拉取公交路线。高德的公交路径规划接口能返回步行和公交分段从中提取shape坐标然后按公交段拼接成完整线路。天地图坐标拾取器适合人工校正少量关键站点但不适合批量获取几千条线路。对于批量任务我建议用高德Web服务API的公交路径规划请求一次返回单条线路上步行和公交的坐标点串。import requests import json def fetch_bus_line(city北京, line1路): 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, paramsparams) 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 srclib/echarts.min.js/script script srclib/bmap.min.js/script div idmap stylewidth: 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-8trailLength尾迹比例0.2-0.4symbolSize光点大小4-8constantSpeed匀速模式true4.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坐标即可。本文还有配套的精品资源点击获取
返回列表