使用canvas绘制一个折线图

前几天处理一个需求,需要根据四项数值来绘制一个折线图,具体样子比较简单,为此实在没有必要引入第三方的图表库,干脆还是自己写一个吧。

效果图

首先,从结构上,四个图标使用页面来做,画布只负责上部分的折线图,四个坐标将图标和图表对齐即可。然后是逻辑上,先初始化canvas画布。

1
2
3
4
5
6
7
8
var
chartdom = $("#pointschart"),
thecanvas = chartdom.find("canvas"),
ctx = thecanvas[0].getContext("2d");
thecanvas.attr({
width: parseInt(thecanvas.width()) + "px",
height: parseInt(thecanvas.height()) + "px"
});

这里假设四个数值是通过ajax请求的,在回调中调用绘制折线和圆点的方法即可。

1
2
3
4
5
6
7
8
9
10
...
...
$.ajax({
url: "/test",
success: function(data){
//data.chartpoints = [5, 7, 9, 2];
drawTheRectangle(data.chartpoints);
drawTheCircle(data.chartpoints);
}
});

然后是四个纵坐标的值,一开始感觉是间隔完全相等的,但最后一个圆点总是显示不全,可能跟图表太小,半个像素的显示问题有关,所以还是直接将四个坐标定死。然后是定位的xy值的问题,由于效果图是750宽度,canvas在实际像素定义的时候应该加一个缩放比,不然在各个宽度下,显示会不同,而且实际坐标值也很难计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
var
...
...
maxwidth = 750,
clientWidth = document.documentElement.clientWidth > maxwidth ? maxwidth: document.documentElement.clientWidth,
fixzoom = (maxwidth / clientWidth).toFixed(1),
cirX = [6, 72, 138, 200],
...
...
thecanvas.attr({
width: parseInt(thecanvas.width() * fixzoom) + "px",
height: parseInt(thecanvas.height() * fixzoom) + "px"
});

之后就是绘制折线图,先依次使用lineTo方法链接四个点,在画出右边、右下圆角、下边,左下圆角就可以了。填充的颜色这里是用的是createLinearGradient方法创建的渐变色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var
...
...
linear = ctx.createLinearGradient(0, 60, 208, 60);
linear.addColorStop(0,'#2b80ff');
linear.addColorStop(1,'#41d4ff');
...
...
function drawTheRectangle(chartpoints){
ctx.save();
ctx.beginPath();
for(var i in chartpoints){
var j = chartpoints[i];
ctx.lineTo(cirX[i], 120 - (bottomspace + chartpoints[i] * 8));
}
ctx.lineTo(cirX[3], 120 - bottomspace);
ctx.arc(cirX[3] - bottomspace, 120 - bottomspace, bottomspace, 0, Math.PI * 1 / 2);
ctx.lineTo(cirX[0] + bottomspace, 120);
ctx.arc(cirX[0] + bottomspace, 120 - bottomspace, bottomspace, Math.PI * 1 / 2, Math.PI);
ctx.closePath();
ctx.fillStyle = linear;
ctx.fill();
ctx.restore();
}

最后绘制四个圆点,就可以了
1
2
3
4
5
6
7
8
9
10
11
function drawTheCircle(chartpoints){
ctx.save();
ctx.fillStyle = "white";
for(var i in chartpoints){
var j = chartpoints[i];
ctx.beginPath();
ctx.arc(cirX[i], 120 - (bottomspace + chartpoints[i] * 8), 5.5, 0, Math.PI * 2, true);
ctx.fill();
}
ctx.restore();
}