小程序-Canvas绘制折线图
自己在一个小程序项目中,希望通过绘制折线图展示不同时间的数值变化趋势,搜索了一番后没找到特别好的第三方库,故打算自己实现一个简单的绘制折线图方法,本文记录自己如何在小程序中通过canvas绘制折线图。
目标
最终目标是达到官方小程序数据助手中折线图的效果:
除了UI目标外,还要便于在小程序其他地方使用或者在其他小程序中使用,所以API要有适当的通用性。
实现
实现的思路是在页面上放置指定大小的canvas,然后调用API传递’canvas id’与配置选项完成绘制,配置选项主要用来指定绘制数据、有关颜色、绘制大小等,绘制的API可以封装到一个单独的模块中便于公用。
接口
暂时设计了4个接口:init、draw、showLine、hideLine,分别用来初始化、绘制、显示和隐藏某条线,具体API描述如下所示:
- init(ctx: string|CanvasContext, options: object): LineChart
init,返回一个LineChart实例,可以调用LineChart原型上的三个方法,其中:
- ctx: 'canvas id’或通过
wx.createCanvasContext
得到的CanvasContext对象- options:
{
width: 320, // canvas的宽度
height: 200, // canvas的高度
labelColor: '#888888', // label的颜色
axisColor: '#d0d0d0', // 轴的颜色
xUnit: '', // x轴label的单位
yUnit: '', // y轴label的单位
xAxis: [1, 2, 3, 4], // x轴label数组
lines: [{ // 需要绘制的线
color: '#1296db', // 线的颜色
points: [10, 29, 18, 20], // 线的y轴值
}],
margin: 20, // 内容与边界的距离
fontSize: 12 // 文字大小
}
- LineChart.prototype.draw()
draw,按初始化给定的ctx和options绘制折线图
- LineChart.prototype.showLine(index: number)
showLine,显示options.lines[index]中的该条线
- LineChart.prototype.hideLine(index: number)
hideLine,隐藏options.lines[index]中的该条线
代码
完整代码移步Github,这里只描述一下自己用canvas绘制折线图的主要思路:
- 绘制x轴与label
绘制x轴相对简单,但所有x轴label都绘制可能导致空间不一够,所以这里要依据label数量与宽度计算能够绘制的个数,然后按等步长的方式从中挑选。同时因为
ctx.fillText
的绘制位置是从左上角计算,为避免溢出,x轴label绘制位置在x轴方向要有适当的偏移
- 绘制y轴label以及水平标度线
y轴label数可以根据cavans高度与希望的标度线间距计算,而每条水平标度线的取值则需要考虑options.lines在y轴的最大值。数量与取值决定后,绘制就相对容易
- 绘制线
绘制轴与label需要收集4个信息:xOffset、yOffset、xStep、yStep,分别表示原点的偏移量(座标)和单位步长,此时线上的点:(index, value)对应到的canvas中座标为:(xOffset + index * xStep, yOffset - value * yStep),最后再使用
ctx.moveTo
结合ctx.lineTo
将所有点连起来即可
- 绘制线与x轴的阴影面积
将需要绘制的区域连接成一个闭合区域再使用
ctx.fill
填充即可,在绘制线的起点前添加其在x轴的投影点作为新起点,以及在绘制线的终点后添加其在x轴的投影点作为新终点,最后使用ctx.closePath
串联起来并fill
- 绘制空心数据点
在每个点的位置使用
ctx.arc
绘制
绘制效果
最后通过LineChart绘制出来的折线图效果如下:
总结
目前的折线图还差一个重要的交互功能:“根据触摸位置,显示临近点的y轴取值”,这个功能晚点补上。