真正稳定、可控的方案是绕过 ECharts 封装,用 uni.createCanvasContext 手动绘制——它不依赖 DOM,跨端一致性高,且能精准控制每个点、线、文字的位置和样式。

直接用 mpvue-echarts 在多数场景下会白屏或错位,尤其在 App 端和部分小程序;真正稳定、可控的方案是绕过 ECharts 封装,用 uni.createCanvasContext 手动绘制——它不依赖 DOM,跨端一致性高,且能精准控制每个点、线、文字的位置和样式。
为什么 mpvue-echarts 在 uni-app 里容易出问题
核心矛盾在于:ECharts 原生基于浏览器 Canvas API,而 uni-app 的小程序端(微信/支付宝)和 App 端使用的 Canvas 实现是隔离的、非标准的。mpvue-echarts 虽做了桥接,但以下情况仍高频报错:
-
canvas.getContext('2d')返回null(常见于未正确获取 canvas node 或未等onReady) - 雷达图 indicator 标签文字被截断或偏移(
name配置在小程序端常失效) - App 端渲染卡顿甚至白屏(因 ECharts 运行时尝试操作不存在的 DOM 节点)
-
shape: 'circle'在 H5 正常,小程序却显示为多边形(底层 polyfill 不一致)
uni.createCanvasContext 手绘雷达图的关键步骤
手绘不是重造轮子,而是把“画布初始化→算坐标→描边→填色→写文字”拆解成可控逻辑。重点注意三处:
- 必须在
onReady中调用uni.createSelectorQuery获取 canvas node,不能只靠canvas-id直接创建上下文 - 需手动适配像素比:
const dpr = uni.getSystemInfoSync().pixelRatio,然后设置canvas.width = width * dpr并调用ctx.scale(dpr, dpr) - 文字绘制必须用
ctx.fillText()+ctx.setTextAlign()+ctx.setTextBaseline()组合控制对齐,不能依赖 ECharts 的axisLabel - 雷达区域填充要用
ctx.beginPath() → ctx.moveTo() → ctx.lineTo() × n → ctx.closePath() → ctx.fill(),避免用clip导致小程序不兼容
Vue 3 + canvas 封装组件时的坑点
如果你用 Vue 3(setup + ref),要注意 canvas 上下文不能跨生命周期复用:
- 每次数据更新后,必须重新执行完整绘制流程(不能只改
fillStyle后fill()) -
setTimeout(() => ctx.draw(), 100)是必需的——H5 端若不延迟,draw()会静默失败 - 避免在
watch中频繁触发draw(),建议加防抖(如nextTick+setTimeout套一层) - 若需导出海报,别用
canvas.toDataURL()(App 端不支持),改用uni.canvasToTempFilePath并传入完整canvasnode 对象
最易被忽略的是:所有坐标计算必须基于 canvas 物理像素(width * dpr),而不是 rpx 或 CSS 像素;一旦混用,H5 和小程序的图形就会错位一倍。这点在封装组件时,最好把 mCenter、mRadius 等变量全部定义在 onReady 回调内,而非 data 或 ref 中。


















