使用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
//具体实现代码
// ===> 步骤 1.
// 创建canvas标签,该标签用来展示图像
var canvas = document.createElement("canvas");
//canvas的宽高不要使用 CSS 来设置,会有拉伸问题,应该直接使用属性设置
canvas.width = 600;
canvas.height = 400;
canvas.style.border = '1px dashed red';
document.body.appendChild(canvas);
//获得绘图工具, 该工具是 CanvasRenderingContext2D 类型的对象.
var context = canvas.getContext('2d');
// 不要硬编码
// 考虑是应该有什么参量?
// 常规意义上是给 上下左右边距
var paddingleft = 50, paddingright = 50, paddingtop = 50, paddingbottom = 50;
//箭头的宽度、高度
var arrowWidth = 10, arrowHeight = 20;
//原点坐标
var ox = paddingleft,
oy = canvas.height - paddingbottom;
// 绘制坐标轴
//设置开始绘图的位置为 Y轴顶点处
//开始沿Y轴负方向、原点、X轴正方向依次绘制直线
context.moveTo( ox, paddingtop );
context.lineTo( ox, oy );
context.lineTo( canvas.width - paddingright, oy );
context.stroke();//描边绘制
// 绘制箭头
//开启新的绘制
context.beginPath();
//绘制Y轴箭头
context.moveTo( ox - arrowWidth / 2, paddingtop + arrowHeight );
context.lineTo( ox, paddingtop );
context.lineTo( ox + arrowWidth / 2, paddingtop + arrowHeight );
context.lineTo( ox, paddingtop + arrowHeight * 2 / 3 );
//填充绘制
context.fill();
//开启新的绘制
context.beginPath();
//绘制X轴箭头
context.moveTo( canvas.width - paddingright - arrowHeight, oy - arrowWidth / 2 );
context.lineTo( canvas.width - paddingright, oy );
context.lineTo( canvas.width - paddingright - arrowHeight, oy + arrowWidth / 2 );
context.lineTo( canvas.width - paddingright - arrowHeight * 2 / 3, oy );
//填充绘制
context.fill();

效果示意图

1
2
<script> code </script>
将上述代码替换 code .在将<script>加入到<body>后.浏览器效果如下图:

坐标系基本绘制

绘制坐标系上的单点( 理解过渡 可忽略)

绘制完坐标系,尝试在其基础上绘制单点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//举个例子,绘制点(100,100)
// 步骤:
// 1.先计算 对应的坐标 在 坐标系 中的实际坐标
// 2.拿到原点坐标
var ox = paddingleft,
oy = canvas.height - paddingbottom;
// 3.实际坐标
var x = ox + 100,
y = oy - 100;
// 4.绘制点
// 注意: 绘制的时候起始点从坐标点向上,向左移动 2 个像素
context.beginPath();
context.moveTo( x - 2, y - 2 );
context.lineTo( x + 2, y - 2 );
context.lineTo( x + 2, y + 2 );
context.lineTo( x - 2, y + 2 );
context.closePath();
//秒变颜色
context.strokeStyle = 'blue';
//描边绘制
context.stroke();

单点绘制效果图:

坐标系单点绘制

多点绘制(数字随机生成)

随机生成函数

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
// ===> 步骤 2. 准备随机函数
var random =( function () {
//传入参数为两个,表示生成的数的个数在 min 和 max 之间取值
function randomWithTwoParam( min , max ) {
return Math.floor( ( max - min ) * Math.random() ) + min;
}
// 传入的参数为 1 个,表示生成的数的个数在 0 -max 之间取值
function randomWithOneParam( max ) {
return randomWithTwoParam( 0, max );
}
function random ( min, max ) {
if ( min === undefined ) {//没有传参
return Math.random();
} else if ( max === undefined ) {//只传了min
return randomWithOneParam( min );
} else {//传了min 和 max 俩参数
return randomWithTwoParam( min, max );
}
}
return random;
})();
//随机生成几个点(范围在传入的值之间,这里传入了 5 和 20,表示个数在5-20之间)
var count = random( 5, 20 );
var points = [];
var x;
while ( points.length < count ) {
//注意 x 不允许重复
x = random( 1000 );//生成一个0-1000的随机数X
if( points.every( pt => pt.x != x) ) {
//没有这个 x
points.push({
x: x,
y: random( 1000 )//生成一个0-1000的随机数Y
});
}
}

console.table( points ); 便会得到下面的图:

多点随机绘制

计算最值求比例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ===> 步骤 3.求 X 和 Y 的最大值
var maxX = Math.max.apply( null, points.map( pt => pt.x ) ),
maxY = Math.max.apply( null, points.map( pt => pt.y ) );
// 对数组按照 x 的大小排序
points.sort( ( a, b ) => a.x - b.x );
// 计算比例
// 思考: 我们的目的是什么?
// 我们的目的是计算在画布中的绘制坐标
// 绘制坐标 : 随机坐标 == 坐标系的宽高 : 最大的xy
// 比例 ( rate ) = 坐标系的宽高 / 最大值
var rateX = ( canvas.width - paddingLeft - paddingRight ) / maxX,
rateY = ( canvas.height - paddingTop - paddingBottom ) / maxY;
1
2
3
console.table( points );
console.log( 'maxX = ' + maxX + ', maxY = ' + maxY );
//在控制台输出得到下面的图

计算比例求最值

计算绘制坐标

1
2
3
4
5
6
7
// ===> 步骤 4.计算要绘制的坐标
var newpoints = points.map( pt => ({
x: pt.x,
y: pt.y,
drawX: pt.x * rateX,
drawY: pt.y * rateY
}) );
1
2
console.table( newpoints );
//输出得到下图所示:

计算绘制坐标

连线绘制点

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
// ===> 步骤 5.连线绘制点
context.beginPath();
context.fillStyle = 'red';
newpoints.forEach(function (newpt) {
//让 随机点 在坐标范围内显示
var x = ox + newpt.drawX,
y = oy - newpt.drawY;
//生成多个 随机点
context.moveTo(x - ptWidth / 2, y - ptWidth / 2);
context.lineTo(x + ptWidth / 2, y - ptWidth / 2);
context.lineTo(x + ptWidth / 2, y + ptWidth / 2);
context.lineTo(x - ptWidth / 2, y + ptWidth / 2);
context.closePath();
});
//填充绘制
context.fill();
// 连线
context.beginPath();
context.strokeStyle = 'red';
//让折线拐角处不出现尖角
context.lineJoin = 'round';
context.lineWidth = 1;
newpoints.forEach(function (newpt) {
var x = ox + newpt.drawX,
y = oy - newpt.drawY;
context.lineTo(x, y);
});
//描边连线
context.stroke();

最终效果图

代码整合: 将上述代码依次整合在一个script标签中,代码结束.

最终效果图

小结

1
简单完成了,随机数生成折线图的功能,可样式不够美观,没有在折线处添加数据,还有很多不足的地方,以后会完善。