canvas小记

使用canvas实现图表

Canvas

canvas是html5新增的元素,通过js绘制图形,编辑图片视频,创建动画

canvas实现图表

  • 折线图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// line.js
// useage:
// line(el, {width: 250, height: 250}, {'A': '100', 'B': '150', 'C': 89, 'D': 200})
/**
* 折线图
* @param {string} id 父视图id
* @param {object} attr canvas属性
* @param {object} data 图标数据
*/
var line = function (id, attr, data) {
// 创建canvas
var el = document.getElementById(id) || document.body
var canvas = document.createElement('canvas')
el.appendChild(canvas)
// 获取上下文
canvas.width = (attr && attr.hasOwnProperty('width')) ? attr.width || 250 : 250
canvas.height = (attr && attr.hasOwnProperty('width')) ? attr.height || 250 : 250
canvas.innerText = 'no support canvas'
var ctx = canvas.getContext('2d')
if (!ctx)
return
// 初始数据
var vals = [100, 150, 89, 200]
var yvals = ['A', 'B', 'C', 'D']
if (data && data.constructor === Object) {
yvals = Object.keys(data)
vals = yvals.map(function(v) { return Number(data[v]) })
}
// 原点
var padding = 30
var origin = {x: padding, y: canvas.height - padding},
width = canvas.width - padding,
height = canvas.height - padding
// x轴
ctx.moveTo(origin.x, origin.y)
ctx.lineTo(width, origin.y)
var startPadding = 20
var space = (width - startPadding) / yvals.length
for (var i = 0; i < yvals.length; i++) {
ctx.moveTo(startPadding + padding + i * space, height)
ctx.lineTo(startPadding + padding + i * space, height - 5)
ctx.fillText(yvals[i], startPadding + padding + i * space, height + 20)
}
// y轴
ctx.moveTo(origin.x, origin.y)
ctx.lineTo(origin.x, padding)
var max = Math.max.apply({}, vals)
var min = 0//Math.min.apply({}, vals)
var count = 5
var space = (height) / count
var step = parseInt((max-min)/(count-1))
for (var i = 0; i < count; i++) {
if (i !== 0) {
ctx.moveTo(padding, height - i * space)
ctx.lineTo(padding + 5, height - i * space)
ctx.fillText(min + step * i, padding - 20, height - i * space)
}
}
// 折线
var scale = space/step
var space = (width - startPadding) / vals.length
for (var i = 0; i < vals.length; i++) {
if (i !== 0) {
ctx.lineTo(space * i + padding + startPadding, height - vals[i] * scale)
ctx.fillText(vals[i], padding + startPadding + space * i, height - vals[i] * scale)
} else {
ctx.moveTo(padding + startPadding, height - vals[i] * scale )
ctx.fillText(vals[i], 30, height - vals[i] * scale)
}
}
ctx.stroke()
ctx.closePath()
}
  • 柱状图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// bar.js
// useage:
// bar(el, {width: 250, height: 250}, {'A': '100', 'B': '150', 'C': 89, 'D': 200})
/**
* 柱状图
* @param {string} id 父视图id
* @param {object} attr canvas属性
* @param {object} data 图标数据
*/
var bar = function (id, attr, data) {
// 创建canvas
var el = document.getElementById(id) || document.body
var canvas = document.createElement('canvas')
el.appendChild(canvas)
// 获取上下文
canvas.width = (attr && attr.hasOwnProperty('width')) ? attr.width || 250 : 250
canvas.height = (attr && attr.hasOwnProperty('width')) ? attr.height || 250 : 250
canvas.innerText = 'no support canvas'
var ctx = canvas.getContext('2d')
if (!ctx)
return
// 初始数据
var vals = [100, 150, 89, 200]
var yvals = ['A', 'B', 'C', 'D']
if (data && data.constructor === Object) {
yvals = Object.keys(data)
vals = yvals.map(function(v) { return data[v] })
}
// origin
var padding = 30
var origin = {x: padding, y: canvas.height - padding},
width = canvas.width - padding,
height = canvas.height - padding
// x
ctx.moveTo(origin.x, origin.y)
ctx.lineTo(width, origin.y)
var startPadding = 20
var space = (width - startPadding) / yvals.length
for (var i = 0; i < yvals.length; i++) {
ctx.moveTo(startPadding + padding + i * space, height)
ctx.lineTo(startPadding + padding + i * space, height - 5)
ctx.fillText(yvals[i], startPadding + padding + i * space, height + 20)
}
// y
ctx.moveTo(origin.x, origin.y)
ctx.lineTo(origin.x, padding)
var max = Math.max.apply({}, vals)
var min = 0//Math.min.apply({}, vals)
var count = 5
var step = parseInt((max-min)/(count-1))
var space = (height) / count
for (var i = 0; i < count; i++) {
if (i !== 0) {
ctx.moveTo(padding, height - i * space)
ctx.lineTo(padding + 5, height - i * space)
ctx.fillText(min + step * i, padding - 20, height - i * space)
}
}
// bar
var scale = space/step
var space = (width - startPadding) / yvals.length
var ww = 20
for (var i = 0; i < vals.length; i++) {
ctx.fillStyle = "rgba(255,0,0,0.6)"
ctx.fillRect(padding + startPadding + space * i - ww/2, height - vals[i] * scale, ww, vals[i]* scale)
ctx.fillText(vals[i], padding + startPadding + space * i - ww/2, height - vals[i] * scale - 10)
}
ctx.stroke()
ctx.closePath()
}
  • 饼状图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// pie.js
// useage:
// bar(el, {width: 250, height: 250}, {vals: [2, 3, 4, 1], colors: ["#ff0", "#f00", "#0ff", "#0f0"]})
/**
* 饼状图
* @param {string} id 父视图id
* @param {object} attr canvas属性
* @param {object} data 图标数据
*/
var Pie = function (id, attr, data) {
// 创建canvas
var el = document.getElementById(id) || document.body
var canvas = document.createElement('canvas')
el.appendChild(canvas)
// 获取上下文
canvas.width = (attr && attr.hasOwnProperty('width')) ? attr.width || 250 : 250
canvas.height = (attr && attr.hasOwnProperty('width')) ? attr.height || 250 : 250
canvas.innerText = 'no support canvas'
var ctx = canvas.getContext('2d')
if (!ctx)
return
// 初始数据
var vals = [2, 3, 4, 1]
var colors = ["#ff0", "#f00", "#0ff", "#0f0"]
if (data && data.hasOwnProperty('vals') && data.vals.constructor === Array)
vals = data.vals
if (data && data.hasOwnProperty('colors') && typeof data.colors.constructor === Array)
colors = data.colors
var total = 0
vals.forEach(function (v) { total += Number(v) })
var scale = vals.map(function (v) { return v/total})
// 绘图
var angle = 0.0, radius = canvas.width/2 * 0.8,
origin = {x: canvas.width / 2, y: canvas.height /2 }
for (var i = 0; i < scale.length; i++) {
ctx.beginPath()
ctx.moveTo(origin.x, origin.y)
angle += scale[i]
ctx.arc(origin.x, origin.y, radius, i === 0 ? 0 : Math.PI * 2 * (angle - scale[i]), Math.PI * 2 * angle, false)
ctx.closePath()
ctx.fillStyle = colors[i]
ctx.fill()
}
// 标字
ctx.beginPath()
ctx.fillStyle = '#000'
var angle = 0
for (var i = 0; i < scale.length; i++) {
var x = radius * Math.cos(Math.PI * 2 * (angle+scale[i]/2))
var y = radius * Math.sin(Math.PI * 2 * (angle+scale[i]/2))
angle += scale[i]
ctx.fillText(vals[i] ,origin.x + x/2, origin.y + y/2)
}
ctx.stroke()
ctx.closePath()
}