说到在支付宝小程序里画图,很多开发者第一时间想到的可能是“原生支持吗?”或者“要不要自己写 Canvas?”其实,随着 ECharts 官方对小程序端的支持日益完善,现在用 echarts-for-wechat(别名 ec-canvas,虽名为微信,但支付宝小程序兼容模式甚至原生模式都能跑通)或者更推荐的 @antv/f2 以及专门适配支付宝的 echarts-component 已经非常成熟。不过,最稳妥、效果最好、且社区资源最丰富的方案,依然是使用 ECharts 官方小程序版(通过 npm 安装或引入源码)。
今天这篇教程,我不跟你讲那些干巴巴的理论,咱们直接上手。我会带你从 0 到 1,在支付宝小程序里搭建一个折线图和饼图,并且重点解决两个最容易让你掉头发的问题:报错和卡顿。
为什么选择 ECharts 而不是别家?
你可能会问,支付宝不有自己的图表库吗?确实有,但 ECharts 的强项在于生态和配置灵活性。你的折线图需要平滑曲线、数据缩放、提示框美化?饼图需要环形、内圆文字、悬停特效?ECharts 都能轻松搞定,而且文档中文超全。对于支付宝小程序,我们主要使用由 Apache ECharts 官方维护的小程序版本,或者通过 npm 安装的 echarts-for-wechat 并进行适配修改。
重要提示:支付宝小程序对 npm 的支持已经非常完善,推荐通过
npm方式引入 ECharts,这样便于更新和维护。
第一步:环境准备与 npm 安装
在开始写代码之前,你得确保你的开发环境是最新的。打开支付宝开发者工具,找到你的小程序项目。
1. 开启 npm 支持
在项目的根目录,也就是 app.json 同级的位置,打开支付宝开发者工具,点击菜单栏的 工具 -> 构建 npm。这一步至关重要,很多人跳过导致引入失败。
2. 安装 echarts
打开支付宝开发者工具的终端,或者你本地的命令行,进入项目目录,执行:
npm install echarts-for-wechat --save
等等,你可能会疑惑,echarts-for-wechat 是给微信用的,支付宝能用吗?答案是:能。 因为微信小程序和支付宝小程序在底层 API 上有较高的相似度,ECharts 官方也为支付宝小程序提供了专门的适配层。更推荐的做法是使用 @antv/wx-chart 或者直接引入 ECharts 官方的小程序源码版。
但为了让你少走弯路,我这里推荐一个经过社区验证、兼容性最好的方案:使用 echarts-for-wechat 并进行少量修改以适配支付宝。或者,更现代的做法是使用 @umijs/f2(蚂蚁金服的图表库),它对支付宝小程序支持原生极佳。
不过,既然标题是 ECharts,我们就死磕 ECharts。我们使用 miniprogram-sm-canvas 或者直接使用 ECharts 官方提供的支付宝小程序示例源码 中的核心文件。
这里,我采用一个更通用、更稳定的方案:引入 ECharts 小程序版的核心文件。你可以从 GitHub 下载 echarts-for-wechat 的源码,或者直接使用 npm 包,然后我们在小程序中通过自定义组件的方式引入。
3. 引入组件
在 app.json 或页面的 .json 配置中,注册组件。假设你将 ECharts 的核心文件放在了 components/echarts 目录下(你需要手动创建这个目录并放入从 GitHub 下载的适配文件,或者使用 npm 包的完整路径)。
推荐路径:直接使用 npm 包,并在页面中引用。
{
"usingComponents": {
"ec-canvas": "../../components/ec-canvas/ec-canvas"
}
}
注意:如果你使用 npm,路径可能需要调整,例如 npm/echarts-for-wechat/components/ec-canvas/ec-canvas。具体取决于你的 npm 包结构。
为了让你更清晰,我直接给出一个无需复杂 npm 配置、直接复制源码即可运行的方案结构,这对于解决报错问题最直观。
第二步:搭建折线图
我们创建一个页面,比如 pages/lineChart/index。
页面结构
pages/lineChart/index.axml(支付宝小程序使用 axml,类似 wxml)
<view class="container">
<view class="title">月度销售趋势折线图</view>
<!-- 这里引入 ec-canvas 组件,注意 id 要唯一 -->
<ec-canvas id="mychart-line" canvas-id="mychart-line" oninit="initLineChart"></ec-canvas>
</view>
样式
pages/lineChart/index.less(或 wxss)
.container {
padding: 20px;
}
.title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
text-align: center;
}
/* 确保图表容器有高度,这是无数报错的根源! */
ec-canvas {
width: 100%;
height: 400px;
}
逻辑层
pages/lineChart/index.js
// 引入 echarts 核心模块
import * as echarts from '../../components/ec-canvas/echarts'; // 请根据实际路径调整
Page({
data: {
ec: {
lazyLoad: true // 延迟加载,优化性能
}
},
// 初始化折线图
initLineChart(canvas, width, height) {
// 初始化图表实例
const chart = echarts.init(canvas, null, {
width: width,
height: height
});
// 将图表实例绑定到 canvas 上,以便后续获取
canvas.chart = chart;
// 配置项
const option = {
title: {
text: '近六个月销售额',
left: 'center',
textStyle: {
fontSize: 14
}
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#333'
}
},
legend: {
data: ['销售额'],
bottom: 0
},
grid: {
left: '3%',
right: '4%',
bottom: '10%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
axisLine: {
lineStyle: {
color: '#ccc'
}
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} 万'
},
splitLine: {
lineStyle: {
type: 'dashed'
}
}
},
series: [{
name: '销售额',
type: 'line',
smooth: true, // 平滑曲线,看起来更美观
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#5470c6'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(84, 112, 198, 0.5)' },
{ offset: 1, color: 'rgba(84, 112, 198, 0.1)' }
])
},
data: [120, 132, 101, 134, 90, 230]
}]
};
// 渲染图表
chart.setOption(option);
return chart;
},
// 组件初始化回调
initLineChart(canvas, width, height) {
this.lineChart = this.initLineChart(canvas, width, height);
},
// 生命周期,页面显示时重绘,防止被其他层遮挡
onShow() {
if (this.lineChart) {
this.lineChart.resize();
}
},
// 页面卸载时销毁实例,释放内存
onUnload() {
if (this.lineChart) {
this.lineChart.dispose();
this.lineChart = null;
}
}
});
第三步:搭建饼图
同样的,我们创建 pages/pieChart/index。
页面结构
pages/pieChart/index.axml
<view class="container">
<view class="title">用户来源分布饼图</view>
<ec-canvas id="mychart-pie" canvas-id="mychart-pie" oninit="initPieChart"></ec-canvas>
</view>
逻辑层
pages/pieChart/index.js
import * as echarts from '../../components/ec-canvas/echarts';
Page({
data: {
ec: {
lazyLoad: true
}
},
initPieChart(canvas, width, height) {
const chart = echarts.init(canvas, null, {
width: width,
height: height
});
canvas.chart = chart;
const option = {
title: {
text: '用户来源分布',
subtext: '纯属虚构',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
radius: ['40%', '70%'], // 环形图
center: ['50%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}\n{d}%',
fontSize: 12,
color: '#666'
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
}
},
labelLine: {
show: true
},
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
{ value: 300, name: '视频广告' }
]
}
]
};
chart.setOption(option);
return chart;
},
initPieChart(canvas, width, height) {
this.pieChart = this.initPieChart(canvas, width, height);
},
onShow() {
if (this.pieChart) {
this.pieChart.resize();
}
},
onUnload() {
if (this.pieChart) {
this.pieChart.dispose();
this.pieChart = null;
}
}
});
第四步:解决报错问题——那些让人抓狂的细节
在支付宝小程序里跑 ECharts,报错通常不是 ECharts 本身的错,而是环境和配置的错。以下是我踩过的坑,整理成了“排雷指南”。
1. 报错:canvas is not defined 或 echarts.init is not a function
原因:引入路径错误,或者 ec-canvas 组件的源码没有正确适配支付宝。
解决方案:
- 检查引入路径:确保
import * as echarts from '...'的路径指向的是包含echarts.init的主文件。如果你使用 npm,路径通常是npm/echarts-for-wechat/dist/echarts.min.js或类似路径。 - 使用官方适配版:强烈建议使用经过支付宝小程序测试的
echarts小程序版。你可以从 ECharts 官网 下载小程序版本,或者使用社区维护的miniprogram-echarts。
2. 报错:canvas 宽高为 0 或图表不显示
原因:这是最常见的问题!ec-canvas 组件需要在页面渲染时才能获取到正确的宽高。如果初始化时容器没有高度,图表就画不出来。
解决方案:
- 在 axml 中固定高度:如上例所示,给
ec-canvas或其父容器设置明确的height。 - 使用
oninit回调:确保在oninit回调中初始化图表,而不是在onLoad或onShow中立即初始化。oninit会在 canvas 尺寸确定后触发。 - 调用
resize():在onShow中调用chart.resize(),确保在页面显示时图表能自适应容器大小。
3. 报错:setData 数据过多导致内存溢出
原因:在 series.data 中传入了大量数据点(比如上千个点),或者在数据更新时频繁调用 setData。
解决方案:
- 分页加载数据:如果数据量大,考虑分页展示,或者使用 ECharts 的
dataZoom组件,让用户可以缩放查看。 - 使用
setOption而非setData:更新图表数据时,调用chart.setOption(option)而不是页面级的this.setData(),避免触发整个页面的数据刷新。 - 减少数据精度:对于折线图,可以适当简化数据点,或者使用 ECharts 的
sampling: 'lttb'属性进行采样优化。
第五步:解决卡顿问题——让图表丝滑流畅
支付宝小程序运行在 JSCore 或 V8 引擎上,性能虽然不错,但复杂的图表动画和频繁的重绘依然可能导致卡顿。以下是优化技巧:
1. 开启硬件加速
在 ec-canvas 的配置中,确保开启 WebGL 或 canvas 的硬件加速。虽然 ECharts 小程序版默认会尝试使用,但你可以手动检查:
const chart = echarts.init(canvas, null, {
width: width,
height: height,
renderer: 'canvas' // 或 'svg',在支付宝小程序中 'canvas' 通常性能更好
});
2. 延迟加载与按需渲染
使用 lazyLoad: true 配置,让图表在真正可见时才进行渲染,避免页面加载时的性能瓶颈。
data: {
ec: {
lazyLoad: true
}
}
3. 简化图表配置
- 关闭不必要的动画:如果数据更新频繁,关闭动画可以提升性能。
series: [{ animation: false // 关闭动画 }] - 减少数据点数量:对于折线图,如果数据点超过 1000 个,考虑降采样。
- 避免复杂渐变和阴影:大面积的
linearGradient和shadowBlur会消耗大量 GPU 资源。
4. 使用 setData 的优化技巧
不要在数据更新时频繁调用 setData。如果必须更新数据,可以使用 chart.setOption() 直接更新图表,而不是通过页面数据驱动。
// 错误做法:通过 setData 更新图表数据
this.setData({
chartOption: newOption
});
// 正确做法:直接调用 chart.setOption
this.lineChart.setOption(newOption);
5. 页面卸载时销毁实例
务必在 onUnload 或 onHide 中调用 chart.dispose(),释放 canvas 资源和内存,避免页面切换后的内存泄漏导致的后续卡顿。
onUnload() {
if (this.lineChart) {
this.lineChart.dispose();
this.lineChart = null;
}
}
完整源码结构参考
为了让你更方便地复制使用,以下是推荐的目录结构:
miniprogram/
├── components/
│ └── ec-canvas/
│ ├── ec-canvas.js
│ ├── ec-canvas.json
│ ├── ec-canvas.axml
│ ├── ec-canvas.less
│ └── echarts.js // ECharts 核心文件
├── pages/
│ ├── lineChart/
│ │ ├── index.axml
│ │ ├── index.js
│ │ ├── index.json
│ │ └── index.less
│ └── pieChart/
│ ├── index.axml
│ ├── index.js
│ ├── index.json
│ └── index.less
├── app.js
├── app.json
└── project.config.json
结语
在支付宝小程序中使用 ECharts 画图,并不是遥不可及的事情。关键在于环境的正确配置、组件的合理引入以及对性能问题的预判和优化。希望这篇教程能帮你省下几个晚上的调试时间,让你在画图时像呼吸一样自然。
记住,遇到问题不要慌,先检查路径、再看日志、最后搜社区。ECharts 的社区非常活跃,很多坑别人已经踩过并给出了答案。
