ARTICLE DETAIL

资讯详情

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

微信小程序集成 ECharts 统计图指南:从接入到避坑

微信小程序集成 ECharts 统计图指南:从接入到避坑 前阵子接手一个小程序项目源包里统计模块用的还是网页那套思路把 echarts 的 CDN 直接挂到 web-view 里跑结果真机一打开就白屏报错信息全是 xxx is not defined。排查到最后才明白微信小程序环境里没有 window、没有 documentecharts 在浏览器里正常工作的初始化流程在这里一步都走不通。这篇就把“微信小程序引用 echarts 做统计图”这件事一次性讲透从选型官方 echarts-for-weixin、组件接入到折线图、柱状图、饼图、地图的完整配置再到底层 canvas 层级、包体积、真机渲染这些容易翻车的点。如果你是刚从网页端转小程序或者第一次在小程序里做数据可视化不需要再去找零散的文章直接按这篇的顺序操作就能跑起来。1. 为什么网页那套在微信小程序里跑不通解决方法是什么1.1 小程序环境里没有 DOMecharts 的初始化逻辑直接失效先理解 echarts 在网页里是怎么工作的。我们通常写const chart echarts.init(document.getElementById(main));这背后依赖三样东西DOM一个真实存在的 HTML 节点window全局对象用于读取视口尺寸、监听 resize、注册事件document用于计算节点位置、样式小程序两大架构特点决定了这条路走不通逻辑层和渲染层分离逻辑层跑在独立的 JavaScript 引擎里不是浏览器内核window 和 document 都不存在UI 由自定义组件构成没有 HTML 节点所以“把网页版 echarts 引到小程序”这种念头最好尽早放下。就算你真的能用 web-view 把网页包进去体验也很差白屏时间长、无法和小程序其他页面通信、纠错能力也弱。1.2 echarts-for-weixin 的适配原理针对这个问题echarts 官方给出了 echarts-for-weixin 方案。它的核心是提供了一个 ec-canvas 小程序自定义组件内部干了几件事用小程序的 canvas 组件当渲染层在组件内部模拟了一套 echarts 需要的 DOM 接口把 echarts.init 需要的 width、height、devicePixelRatio 从 canvas 节点里取出来传入从开发者角度看你不需要关心这些模拟细节只需要按它的约定调用即可。1.3 为什么不用 wx-charts 或者 F2方案图表类型交互丰富度维护方定制成本echarts-for-weixin折线、柱状、饼、散点、地图等高tooltip/联动/富文本echarts 官方配置项一套到底可复用网页经验wx-charts折线、柱状、饼等基础图低主要靠点击事件个人/社区轻量但画复杂图形费劲F2移动端常见图表中等专为移动端优化蚂蚁API 和 echarts 不通用学习成本高结论既然需求明确是统计图而且你对 echarts 的熟悉度会从网页端延伸过来选 echarts-for-weixin 是最省力的。2. 把 echarts-for-weixin 装进项目组件目录与页面接入2.1 获取组件包别把整个仓库塞进来从 GitHub 的 echarts-for-weixin 仓库下载后只需要拷贝 ec-canvas 这一个目录到你的项目里。推荐放在 components/ec-canvas 下。目录结构大概是components/ └── ec-canvas/ ├── ec-canvas.js ├── ec-canvas.json ├── ec-canvas.wxml ├── ec-canvas.wxss └── echarts.js # 这是打包好的 echarts 库这里坑很多。一是 echarts.js 体积不小默认是全量包后面我会专门讲怎么瘦身二是不同版本的 echarts-for-weixin 对 echarts 版本支持不同不要拿新版 echarts.min.js 直接覆盖旧目录容易跑出一些奇怪的兼容问题。2.2 页面接入的三步配置第一步页面 JSON 注册组件{ usingComponents: { ec-canvas: /components/ec-canvas/ec-canvas } }第二步WXML 放置容器view classchart-wrap ec-canvas idlineChart canvas-idlineChart ec{{ ec }}/ec-canvas /view第三步页面的 JS 里定义 ec 对象import * as echarts from ../../components/ec-canvas/echarts; function initChart(canvas, width, height, dpr) { const chart echarts.init(canvas, null, { width: width, height: height, devicePixelRatio: dpr }); canvas.setChart(chart); chart.setOption({ // 这里放图表配置 }); return chart; } Page({ data: { ec: { onInit: initChart } }, onReady() { // 页面就绪后可以在这里做后续操作 } });2.3 几个必须注意的初始化时机很多第一次用的人会习惯在 onLoad 里直接写echarts.init然后发现图表死活不渲染。原因在于 ec-canvas 是自定义组件它的画布节点要等组件初始化完成才存在。官方的一整套回调机制就是为此设计的不要在 onLoad 里抢跑。另外一个常见问题是容器高度。chart-wrap 如果没有明确高度canvas 的宽高会算成 0图表自然就不出来。你可以在 WXSS 里写定值比如height: 400rpx;也可以用 flex 布局撑开。总之高度不能依赖内容撑开因为 canvas 内部绘图是绝对定位的。注意一个页面如果有多个 ec-canvas 实例canvas-id 必须各自唯一否则后初始化的图表会覆盖前面的。3. 第一张统计图折线图从数据到渲染3.1 维护折线图的最小 setOption继续用上文的 initChart把 setOption 换成折线图配置chart.setOption({ grid: { left: 36, right: 16, top: 24, bottom: 28, containLabel: true }, xAxis: { type: category, data: [3月1日, 3月2日, 3月3日, 3月4日, 3月5日] }, yAxis: { type: value }, series: [{ name: 营收, type: line, smooth: true, data: [120, 200, 150, 80, 270] }] });这里我特别花了篇幅写 grid。小程序屏幕窄默认的 grid 四周留白偏大图表区域会被压缩。你把 left/right/top/bottom 收紧后可视区域会明显变大。containLabel 是防止 y 轴文字溢出到图表外。3.2 x 轴刻度避碰rotate 与 formatter数据一多x 轴 label 重叠是我们最常碰到的问题。热搜词里“echarts折线图x轴刻度”说的就是这件事。常见的处理方式就两种一是旋转xAxis: { axisLabel: { interval: 0, rotate: 30, fontSize: 10, color: #666 } }interval: 0 表示强制每个刻度都显示否则 echarts 会自动抽稀。二是格式化截断axisLabel: { formatter(value) { return value.length 4 ? value.slice(0, 4) ... : value; } }两种思路可以叠加。如果你的刻度是时间序列还可以考虑把 x 轴换成 time 类型用 axisLabel 的 formatter 控制显示粒度。3.3 请求接口数据之后再绘图真实项目里数据肯定不是写死的。比如你要展示最近 7 天统计数据推荐做法是在 onLoad 里请求接口拿到数据后再初始化图表而不是在拿不到数据时先画一个空图。使用 lazyLoad 模式更合适。先在 data 里声明data: { ec: { lazyLoad: true } }然后请求成功后async fetchData() { const res await request({ url: xxx, method: GET }); const list res.data.list.map(item item.value); this.selectComponent(#lineChart).init((canvas, width, height, dpr) { const chart echarts.init(canvas, null, { width, height, devicePixelRatio: dpr }); canvas.setChart(chart); chart.setOption({ xAxis: { type: category, data: res.data.dates }, series: [{ type: line, data: list }] }); return chart; }); }注意 wx.request 默认不返回 Promise平时我会自己封装一层 Promise或者直接在回调里写。用这种模式的要点是lazyLoad: true 会让 onInit 不执行等组件 ready 后你再主动调 init。这样避免了一次空白渲染也方便 loading 转圈占位。3.4 smooth 与 areaStyle 的视觉细节折线图的 smooth: true 会把线变成平滑曲线但在数据点很少的时候平滑曲线反而会产生比较夸张的“甩尾”看起来不严谨。我一般会先看数据分布再决定要不要开平滑。如果需要面积渐变可以给 series 加areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: rgba(47, 140, 240, 0.3) }, { offset: 1, color: rgba(47, 140, 240, 0.02) } ]) }渐变总面积图在真机上性能稍差如果一页有多个折线图我建议先不开等主流程跑通再追加。4. 柱状图渐变、横向条、标签显示这些需求一次讲完4.1 基础柱状图与间距设置柱状图在统计场景里出现频率最高。基础配置只比折线图多一步series.type 换成 barseries: [{ type: bar, barWidth: 16, itemStyle: { borderRadius: [4, 4, 0, 0], color: #2f8cf0 } }]barWidth 建议显式指定否则在小屏上柱条会被中间空白拉得很窄。borderRadius 让柱顶圆角化视觉上柔和一点。4.2 柱状图设置渐变色的正确姿势很多人在网页版 echarts 里用 LinearGradient 都顺手到了小程序里发现同样写法报错。原因是没有正确导入 image 或 graphic 命名空间。在小程序版里只要保证 import 的是完整 echarts 对象就可以直接这样写import * as echarts from ../../components/ec-canvas/echarts; color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: rgba(46, 132, 255, 0.9) }, { offset: 1, color: rgba(46, 132, 255, 0.2) } ])注意 LinearGradient 构造参数表示渐变方向0,0,0,1 是从上到下0,0,1,0 是从左到右。颜色 stop 的 offset 是从 0 到 1。4.3 横向柱状图与“横向进度条”的实现业务上看业绩完成率、看设备使用率时横向柱状图比纵向柱状图更直观很多产品管它叫“进度条式统计图”。实现方法很简单交换 xAxis 和 yAxis 的 type。xAxis: { type: value, max: 100 }, yAxis: { type: category, data: [设备A, 设备B, 设备C] }, series: [{ type: bar, label: { show: true, position: right, formatter: {c}% }, data: [75, 92, 64] }]这里 max 显式设为 100配合 formatter 显示百分比就得到了一个非常干净的完成率条形图。4.4 让标签显示在每个柱子上面纵向柱状图的经典需求是让数值标签出现在每个柱顶。正常配置是label: { show: true, position: top, color: #333, fontSize: 12 }position 可取值有 top、insideTop、insideBottom 等。当柱子高度不一、数值差距大时建议用 insideTop 避免大柱子的标签溢出画布小柱子的标签则会自动贴近柱顶不易造成视觉混乱。4.5 双柱对比图的小技巧两个系列并排对比时需要让它们同用一套 category 数据并且开启 barGapseries: [{ name: 本月, type: bar, data: [120, 200, 150] }, { name: 上月, type: bar, data: [90, 180, 120] }]默认两根柱子会自动并排。如果你觉得柱子间距不合适可以用 barGap: 0.3 调整。颜色上建议一深一浅并配套 legend否则真机上容易分不清两个系列。5. 饼图与环形图百分比、中间文字、图例细节5.1 饼图基础配置饼图的数据结构比较简单series: [{ type: pie, radius: 60%, data: [ { name: 已完成, value: 68 }, { name: 未完成, value: 32 } ], label: { formatter: {b}: {d}% } }]{d} 表示百分比{b} 表示 name。在移动端饼图比例小默认 label 会拥挤可以把 label 的 fontSize 调小一点或者只在选中时显示。5.2 环形图与“饼图中间的字”环形图就是把 radius 改成区间radius: [40%, 70%]外半径 70%内半径 40%。中间那行字是另一个很常见的需求比如“完成率 86%”。它的实现不是饼图配置而是用 title 组件title: { text: 86%, subtext: 完成率, left: center, top: center }关键是让 title 的位置对准圆心。left: centertop: center直接居中。echarts 会把标题按 canvas 中心对齐。如果你想在中间放多行文字或者更自由的样式可以用 graphic 元素手绘但那属于进阶玩法新手没必要一上来就用。5.3 饼图图例的小程序困局网页版饼图可以有很多个 legend 换行排列但小程序屏幕宽度就那么大legend 数据多了会排成一个拥挤的方块。我的建议是图例项 3 个以内用 legend放在底部图例项 3 个以上关掉 legend直接用 label 把名称和百分比标在图上另外 legend 的 icon 尺寸可以调小legend: { icon: circle, itemWidth: 8, itemHeight: 8, textStyle: { fontSize: 11 } }这样在真机上不至于挤成一团。6. 中国地图与天地图地图类统计图的接入思路6.1 echarts 地图数据需要手动注册如果你要做中国地图统计图需要注意 echarts 4 之后官方不再内置地图 GeoJSON 数据需要自己准备 China 地图数据然后调用echarts.registerMap(china, chinaJson); chart.setOption({ tooltip: { trigger: item }, visualMap: { min: 0, max: 100, left: 10, bottom: 10, text: [高, 低] }, series: [{ type: map, map: china, data: [ { name: 广东, value: 88 }, { name: 浙江, value: 72 } ] }] });registerMap 的时机要放在 setOption 之前而且建议放在 initChart 函数内部避免全局注册污染。6.2 地图数据体积与加载策略完整的中国地图 GeoJSON 压缩后也有几百 KB。微信小程序的主包/分包体积限制很死所以一般把地图 JSON 放到分包里或者等页面真正打开时再通过 wx.request 拉取远程数据拉回来再 registerMap。这里还有一个常用技巧如果你的地图只需要展示省份颜色可以在获取到数据后做一次降级处理比如按需求裁剪掉非业务省份的坐标。裁剪精度不高没关系省下的体积很可观。6.3 天地图底图与 echarts 图层的结合有些项目要求用天地图当底图再在底图上绘制 echarts 统计散点、迁徙线。直接说结论微信小程序里天地图更多走的是原生 map 组件而 echarts 的 canvas 要浮在地图上方需要让两者的坐标系对齐。比较稳的方案是底层用小程序 map 组件加载天地图图源上层用覆盖物或者同层渲染的 canvas 图层叠加 echarts通过地图可视区域变化事件重新计算 echarts 里点的坐标位置这个方案复杂度比较高涉及经纬度换算、视口偏移、地图手势联动。如果业务上只是要“一张带省份颜色的统计图”用前面的 GeoJSON 着色方案就足够不必为天地图徒增工作量。6.4 富文本提示框与引导线案例的参考如果你偏向做地理坐标图、客流分析这类场景可以参考 echarts 社区里的“地理坐标图视觉引导线及富文本提示框”案例。它的核心是 markLine 配合 label 的 rich 字段比如在标记线上方显示箭头和文字。小程序的 echarts-for-weixin 对这块支持相对完整但字体渲染会比网页版精简视觉上要降低预期。7. 那些不查文档根本找不到的坑7.1 canvas 层级遮挡与列表滚动小程序 canvas 是原生组件早期版本会天然盖在普通 view 之上。如果你在页面里放了一个 position: fixed 的悬浮按钮结果按钮被图表盖住多半就是这个原因。随着基础库同层渲染的推进现在大部分机型上 canvas 已经能和普通组件共存但部分低版本微信或者 iOS 老机型上遮挡问题依然会出现。如果统计图所在页面还要做列表滚动加载更多比如上方是图表、下方是数据列表滚动过程中 canvas 会周期性重绘低端机容易卡顿。我的做法是把图表的 setOption 在滚动停止后再触发或者在 onPageScroll 里做节流。稳妥做法页面上需要浮在图表上方的交互元素优先用 cover-view图表区域内部不要叠加普通 view 做 toast 或气泡提示如果真的必须叠加把基础库版本提升到支持同层渲染的 2.4.0 以上并保证真机验证7.2 图表不渲染的排查清单我自己遇到最多的“图表不出现”场景按概率排是这样canvas-id 重复。一个页面有多个 ec-canvascanvas-id 必须各自唯一否则后面初始化的会覆盖前面的初始化时机不对。在 onLoad 里 init组件未 readycanvas 节点还没有宽高容器高度为 0。chart-wrap 没有设置高度canvas 是绝对定位父容器高度无法被撑开数据为空。series.data 传了空数组图表会显示空白不是报错排查顺序我建议先看 wxml 容器有没有高再看 console 有没有报错最后再检查数据。7.3 tooltip 在真机上偏移或不显示网页上 tooltip 用得好好的小程序真机上可能出现提示框跑到画布外面、或者不跟随手指的情况。解决办法优先这两个tooltip: { trigger: axis // 或 item }以及tooltip: { confine: true // 把提示框限制在画布内避免溢出 }如果你用了自定义 formatter 返回 HTML 片段小程序里大概率渲染不出效果。小程序 canvas 的 tooltip 内容是走 echarts 内部的绘图逻辑不支持真正意义上的 HTML复杂的富文本要用代码片段而不是 HTML 标签。7.4 包体积超限与 echarts 按需构建很多人在 uniapp 或原生小程序里引入 echarts 后真机预览直接报错像source size 2612kb exceed max limit 2mb这种。2600 多 KB 说白了就是全量 echarts.js 加其他依赖超了。处理思路分三层第一层把统计页面放进分包。分包只在进入对应页面时才加载主包体积就不会被 echarts 撑爆第二层用 echarts 官方提供的按需构建勾选你实际用到的图表类型下载定制包替换 ec-canvas/echarts.js第三层如果是 uniapp 项目检查 vendor 里是否有多份 echarts 重复打包必要时配置 manualChunks 拆包提示如果只做基础统计图定制构建时尽量别把地图、富文本这些模块一起打进去体积差非常多。我个人的顺序是先用全量包把功能调通上线前再按需构建。只保留折线、柱状、饼图时构建出来的 echarts.js 体积能从 800 KB 降到 400 KB 上下效果非常明显。7.5 开发中的小程序怎么发给别人试用最后说一个和统计图无关、但每个做小程序的人都会遇到的事微信开发者工具里想把手头版本发给同事、朋友试用收集反馈直接点工具栏的“预览”会生成一个二维码扫码后就是体验版。需要注意两点体验版要配置 request 合法域名否则接口请求会被拦截图表数据加载不出来预览二维码是动态的隔一段时间会失效需要再生成适合收集“几天内试用反馈”这种短期场景更长期的做法是上传源码后在后台设为体验版把体验版二维码固定下来让多人持续试用。最后关于 echarts 社区那些零散案例我的使用习惯是先把业务里需要的图表类型定死遇到具体配置项记不清时去社区搜对应关键词比如“echarts 柱状图设置渐变色”“echarts pie 中间的字”“折线图 x 轴刻度”大部分都能直接找到可复用的 setOption。真正能让你省时间的是搞清这些配置在小程序里哪些可用、哪些要绕路。
返回列表