我们只要不是要去用 canvas 做游戏,能做到 让 数据 可视图形化 即可,下面是 canvas 绘制折线统计图的练习
<canvas width="600" height="400" id="cas" style='border: 1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' ); // 获取绘图对象
/*---- 绘制坐标系 ----*/
// 坐标系在 canvas 中的 padding
var padding = 30;
// 坐标系原点
var x0 = padding, // 在正方向 padding
y0 = cas.height - padding; // 左下角 高度-padding
// 箭头高宽
var arrowHeight = 20, // 箭头高度
arrowWidth = 10; // 箭头宽度
// 坐标系最大 x,坐标系最大 y
var maxX = cas.width - padding * 2 - arrowHeight,
maxY = cas.height - padding * 2 - arrowHeight;
// 绘制 坐标系 两个轴
ctx.moveTo( x0, y0 ); // 从 原点开始
ctx.lineTo( x0 + maxX + arrowHeight, y0 ); // 绘制 x 轴
ctx.moveTo( x0, y0 ); // 从 原点开始
ctx.lineTo( x0, y0 - maxY - arrowHeight ); // 绘制 y 轴 由于 方向是反的,所以要加 负号
ctx.stroke(); // 连点成线,绘制路径
// 绘制 坐标系 箭头
ctx.beginPath();
// 绘制 x 轴 箭头
ctx.moveTo( x0 + maxX + arrowHeight, y0 ); // x 轴右顶点开始
ctx.lineTo( x0 + maxX, y0 - arrowWidth / 2 ); // 箭头上半边线, 由于是反的,上半边是减
ctx.lineTo( x0 + maxX + arrowHeight / 2, y0 ); // 回 一半
ctx.lineTo( x0 + maxX, y0 + arrowWidth / 2 ); // 箭头下半边线顶点
ctx.closePath(); // 闭合路径 绘制出下半边线
// 绘制 y 轴 箭头
ctx.moveTo( x0, padding ); // y 轴 上顶点开始
ctx.lineTo( x0 - arrowWidth / 2, padding + arrowHeight ); // 箭头左半边
ctx.lineTo( x0, padding + arrowHeight / 2 ); // 回一半
ctx.lineTo( x0 + arrowWidth / 2, padding + arrowHeight ); // 箭头右半边 顶点
ctx.closePath(); // 闭合路径 绘制出右半边线
ctx.fill(); // 填充路径
/*----- 绘制坐标系点 ------*/
var data1 = [ [ 10, 20 ], [ 15, 13 ], [ 17, 30 ], [ 30, 10 ], [ 20, 15 ] ];
var data3 = []; // 用于存储计算之后的坐标
// 排序
// 由于 要进行 连线,所以要先进行排序, 这里冒泡一下
var flag = true; // 定义 flag 看 有没有 排完
for ( var i = 0; i < data1.length - 1; i++ ) { // 排 length - 1 趟
for( var j = 0; j < data1.length - 1 - i; j++ ) { // 每 排完一趟,每趟 就少排一次
if ( data1[ j ][ 0 ] > data1[ j + 1][ 0 ] ) { // 如果 大于,那就换
var t = data1[ j ];
data1[ j ] = data1[ j + 1 ];
data1[ j + 1 ] = t;
flag = false; // 交换了 说明没排好
}
}
if ( flag ) break; // 一趟下来,没进行交换,说明已经排好,退出
flag = true; // 否则没排完,置为 ture
}
// 计算出最大值,和比例
var dataMaxX = Math.max.apply( null, data1.map( function( v ) { return v[ 0 ]; } ) );
var dataMaxY = Math.max.apply( null, data1.map( function( v ) { return v[ 1 ]; } ) );
// 使用循环绘制点
ctx.beginPath();
ctx.fillStyle = 'blue'; // 设置画笔颜色
for( var i = 0; i < data1.length; i++ ) {
// 每个是 点边长 为 8 的矩形
var tmpX = data1[ i ][ 0 ]; // 基准点 x 坐标
var tmpY = data1[ i ][ 1 ]; // 基准点 y 坐标
// 按比例 缩放
tmpX = tmpX * maxX / dataMaxX;
tmpY = tmpY * maxY / dataMaxY;
// 转换坐标基准,以 x0 y0 为基准
tmpX = tmpX + x0;
tmpY = y0 - tmpY;
data3.push( [ tmpX, tmpY ] ); // 将计算好的坐标点存放到数组中
// 绘制点
ctx.moveTo( tmpX - 4, tmpY - 4 );
ctx.lineTo( tmpX + 4, tmpY - 4 );
ctx.lineTo( tmpX + 4, tmpY + 4 );
ctx.lineTo( tmpX - 4, tmpY + 4 );
ctx.closePath();
}
ctx.fill(); // 绘制出四个点
/*---- 连线 -----*/
ctx.beginPath(); // 开启路径
ctx.strokeStyle = 'blue'; // 换个颜色
data3.forEach( function( v, i ) {
ctx [ [ 'moveTo', 'lineTo' ][ i==0?0:1 ] ]( v[ 0 ], v[ 1 ] );
});
ctx.stroke(); // 绘制
</script>
最终绘制出来的结果: