注册 | 登录  | 提交微信小程序
公告:
QQ:894633933 提交微信小程序审核、合作。审核方式:https://www.sptime.cn/content/index/id/138.html QQ群:603430935 暗号:小程序
当前位置:首页 > 小程序资讯 >

如何在小程序中绘制图表?

发布时间:2017-02-10 11:11浏览568次

由于微信小程序本身框架的限制,很难集成目前已有的图表工具,显示图表目前有两种方案:

  1. 服务器端渲染图表,输出图片,微信小程序中直接显示渲染好的图片;

  2. 利用微信小程序 API 中提供的 canvas 组件支持,自行绘制图表。

前一种方案已经有非常多类似服务可选,比如 Highcharts 提供了服务端渲染的能力。但这种方式需要后台有一套渲染服务,并且有一定的网络开销。

那么,如何利用 canvas 组件,在小程序中绘制图表呢?下面,我们就来尝试一下。

API

首先,我们在模板文件中使用 <canvas></canvas> 声明一个 canvas 组件,再使用wx.createContext() 获取绘图上下文 context。

接下来,我们调用 wx.drawCanvas() 进行绘制:

wx.drawCanvas({    canvasId: 'firstCanvas',    actions: context.getActions() // 获取绘图动作数组});

开始图表的绘制

绘制折线图

// 获取绘图上下文 contextvar context = wx.createContext();// 设置描边颜色context.setStrokeStyle("#7cb5ec");// 设置线宽context.setLineWidth(4);context.moveTo(50, 70);context.lineTo(150, 150);context.lineTo(250, 30);context.lineTo(350, 120);context.lineTo(450, 150);context.lineTo(550, 95);// 对当前路径进行描边context.stroke();wx.drawCanvas({    canvasId: 'testCanvas',    actions: context.getActions()});

需要注意的是,moveTo() 方法不会记录到路径中。

我们来看看效果图:

charts-1

好像没有想象中难,看上去效果还不错。

绘制每个数据点的标识图案

...context.beginPath();// 设置描边颜色context.setStrokeStyle("#ffffff");// 设置填充颜色context.setFillStyle("#7cb5ec");context.moveTo(50 + 7, 70);// 绘制圆形区域context.arc(50, 70, 8, 0, 2 * Math.PI, false);context.moveTo(150 + 7, 150);context.arc(150, 150, 8, 0, 2 * Math.PI, false);...context.closePath();// 填充路径context.fill();context.stroke();

效果图:

charts-2

为了避免之前绘制的折线路径影响到标识图案的路径,这一部分包裹在了 beginPath()closePath() 之间。

绘制横坐标

我们规定的参数格式是这样的:

opts = {    width: 640,    // 画布区域宽度    height: 400,   // 画布区域高度    categories: ['2016-08', '2016-09', '2016-10', '2016-11', '2016-12', '2017']}

我们根据参数中的 categories 来绘制横坐标。先稍微整理下思路:

  1. 根据 categories 数均分画布宽度;

  2. 计算出横坐标中每个分类的起始点;

  3. 绘制文案(这儿会多一些代码,后面会具体提到)。

var eachSpacing = Math.floor(opts.width / opts.categories.length);var points = [];// 起始点x坐标var startX = 0;// 起始点y坐标var startY = opts.height - 30;// 终点x坐标var endX = opts.width;// 终点y坐标var endY = opts.height;// 计算每个分类的起始点x坐标opts.categories.forEach(function(item, index){    points.push(startX + index * eachSpacing);});points.push(endX);// 绘制横坐标context.beginPath();context.setStrokeStyle("#cccccc");context.setLineWidth(1);// 绘制坐标轴横线context.moveTo(startX, startY);context.lineTo(endX, startY);// 绘制坐标轴各区块竖线points.forEach(function(item, index){    context.moveTo(item, startY);    context.lineTo(item, endY);});context.closePath();context.stroke();context.beginPath();// 设置字体大小context.setFontSize(20);// 设置字体填充颜色context.setFillStyle('#666666');opts.categories.forEach(function(item, index){    context.fillText(item, points[index], startY + 28);});context.closePath();context.stroke();

效果图:

chart-3

效果不错,除了文字没有居中…….

查阅微信小程序官方提供的文档,小程序并没有提供 HTML 5 canvas 中的 mesureText(获取文案宽度)的方法。

下面是我们自己简单的实现,并不是绝对精确,但误差基本可以忽略。

functionmesureText(text){    var text = text.split('');    var width = 0;    text.forEach(function(item){        if (/[a-zA-Z]/.test(item)) {            width += 14;        } elseif (/[0-9]/.test(item)) {            width += 11;        } elseif (/\./.test(item)) {            width += 5.4;        } elseif (/-/.test(item)) {            width += 6.5;        } elseif (/[\u4e00-\u9fa5]/.test(item)) {            width += 20;        }    });    return width;}

这里分别处理了字母、数字、点(.)、横线(-)以及汉字这几个常用字符。

上面的代码稍微修改下:

opts.categories.forEach(function(item, index){    var offset = eachSpacing / 2 - mesureText(item) / 2;    context.fillText(item, points[index] + offset, startY + 28);});

charts-4

大功告成!

如何在折线上绘制出每个数据点的数值文案呢?大家可以自己动手,尝试一下。