canvas

canvas

canvas介绍

  • canvas是什么
    • canvas是一个html5的标签,可以使用JavaScript在其中绘制图形的 HTML5 元素, canvas 原意画布, 帆布。在 HTML 页面中用于展示绘图效果。所有画图的过程使用js来实现。
  • canvas可以做什么
    • 绘图(图标,图形的绘制)
    • 数据的可视化(重点)
    • 动画与游戏
    • banner 广告
    • 多媒体、虚拟现实、图形编辑等

初体验

  1. Canvas 的默认大小为300像素×150像素(宽×高,像素的单位是px)。但是,可以使用HTML的高度和宽度属性来自定义Canvas 的尺寸。Canvas是一个画布(图片),不要使用css来设置宽高,否则该画布将会被拉升变形。

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#canvas {
border: 1px solid #000;
width: 600px;
height:400px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
</script>
</body>
</html>

绘制基本形状的api

  • 创建画图工具 canvas.getContext(“2d”)
  • 移动画笔 context.moveTo(x, y)
  • 画一条直线 context.lineTo(x, y)
  • 描边 context.stroke();
  • 填充 context.fill();
  • 设置描边颜色 context.strokeStyle = “red”;
  • 设置填充颜色 context.fillStyle = “green”;
  • 创建一张新的玻璃纸 context.beginPath();
  • 闭合路径context.closePath();

路径

  • 路径就是画布上的线条
  • 路径是有方向的
  • 我们可以这样想象: context在画图时,并不是直接把线画到画布上,而是画在玻璃纸上
  • 当执行stroke时,把玻璃纸向画布上做一次印刷,再执行一次stoke会再印刷一次
  • 为了避免重复印刷,我们的做法是, 再拿一张新的玻璃纸,在新的玻璃纸上绘制
  • 再次执行stroke就会使用新玻璃纸上的图形
  • beginPath, 创建一个新玻璃纸的过程, 叫做, 路径就是玻璃纸上的图形元素;
  • closePath,会自动将lineTo的最后一个点和最近的moveTo点连接起来,闭合路径。
  • stroke()和fill()
    • stroke是描边,(素描)
    • fill是填充,(上色)
    • fill会自动执行一次closePath
  • strokeStyle和fillStyle
    • strokeStyle,描边颜色
    • fillStyle, 填充颜色

非零填充原则

  • 路径围成的区域内部,任意一点拉一条射线,找到所有与其相交的路径。顺时针为1, 逆时针为-1. 将所有的相交的路径的值相加。 如果不等于零则对该区域进行填充

矩形绘制的三种方式

  • 绘制一个矩形的路径 context.rect(x, y, width, height)
    • 需要手动去执行stoke或者是fill()
  • 绘制一个描边的矩形 context.strokeRect(x, y, width, height)
    • 只能描边,不能填充
  • 绘制一个填充颜色的矩形 context.fillRect(x, y, width, height)
    • 只能填充,不能描边
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1. 第一种方式
//绘制出一个矩形的路径, 适应性是最强的
context.rect(100, 100, 200, 150);
context.stroke();
context.fill();
//2. 第二种方式
//绘制一个描边的矩形,只能描边不能填充
context.strokeRect(100, 100, 200, 150);
context.fill();
//3. 第三种方式
//创建一个填充的矩形,只能填充不能描边
context.fillStyle = "yellowgreen";
context.fillRect(100, 100, 200, 150);
context.stroke(); //不会生效

绘制圆形或圆弧

  • 绘制一个圆形 context.arc(x, y, radius, startRadian, endRadian, direction)
    • js中使用的是弧度制。一个完整的圆的弧度是 2*Math.PI
    • 角度转弧度的公式 angle/180 * Math.PI
    • 绘制扇形, 需要lineTo到圆心,再closePath
    • direction默认是false(是否逆时针), 可以不传
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
//1. 绘制一个圆形
//x,y代表圆心的坐标, r(radius)半径,startRadian, endRadian起始弧度和结束的弧度, direction路径的方向(是否逆时针),默认值是false,代表是顺时针,最后一个参数可以不传。
context.arc(x, y, r, startRadian, endRadian, direction)
//注意事项:1. 在canvas中所有和角度相关的,都要转成弧度,2*Math.PI对应的是360度的圆,2.如果起启弧度和结束弧度的差值小于2*Math.PI就是一个圆弧
context.arc(300, 200, 100, 0, 2*Math.PI);
context.stroke();
context.fillStyle = "yellowgreen";
context.fill();
//角度转弧度
radian = angle/360 * 2*Math.PI = angle/180*Math.PI;
//绘制一个弧形
context.arc(300, 200, 100, -0.5*Math.PI, 0);
context.stroke();
context.arc(300, 200, 100, 15/180*Math.PI, 127/180*Math.PI);
context.stroke();
//绘制扇形
context.arc(300, 200, 100, 15/180*Math.PI, 127/180*Math.PI);
context.lineTo(300, 200);
context.closePath();
context.fill();
//context.fill();
context.stroke();

飞镖盘(例子)

1
2
3
4
5
6
7
8
//循环绘制十个圆形的路径
for (var i=0; i<10; i++) {
//i%2==0,如果是偶数就是true,就逆时针,奇数就顺时针
context.arc(300, 200, 15+15*i, 0, 2*Math.PI, i%2==0);
}
context.fillStyle = "red";
context.fill();

折线图

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
//1. 创建模拟数据
var datasArr = [6000, 7500, 9300, 8000, 6800, 9500];
//2. 画坐标系
context.moveTo(30, 30);
context.lineTo(30, canvas.height-30);
context.lineTo(canvas.width-30, canvas.height-30);
context.stroke();
//3. 循环把所有点的x,y算出来
var pointsArr = [];
for (var i = 0; i<datasArr.length; i++) {
//算出每个折点的x的坐标
var x = 30 + 90*i;
//算y坐标
//a. 算出百分比
var percent = datasArr[i]/12000;
//b. 算出折点的高度
var height = (canvas.height-60)*percent;
//c.算出y
var y = canvas.height - 30 - height;
//x,y代表一个点,每个点是一个对象,把对象放在数组中
var point = {x:x, y: y};
pointsArr.push(point);
}
//3. 循环画折线
context.beginPath();
context.moveTo(pointsArr[0].x, pointsArr[0].y);
for (var i = 1; i<pointsArr.length; i++) {
context.lineTo(pointsArr[i].x, pointsArr[i].y);
}
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
//1. 创建模拟数据
var datasArr = [6000, 7500, 9300, 8000, 6800, 9500];
//2. 画坐标系
context.moveTo(30, 30);
context.lineTo(30, canvas.height-30);
context.lineTo(canvas.width-30, canvas.height-30);
context.stroke();
//3. 循环把所有点的x,y算出来
var gap = 45; //是柱子和柱子之间的间隔
var squarew = 40;//柱子的宽度
var pointsArr = [];
for (var i = 0; i<datasArr.length; i++) {
//算出每个折点的x的坐标
var x = 30 + gap + (gap+squarew)*i;
//算y坐标
//a. 算出百分比
var percent = datasArr[i]/12000;
//b. 算出折点的高度
var height = (canvas.height-60)*percent;
//c.算出y
var y = canvas.height - 30 - height;
//x,y代表一个点,每个点是一个对象,把对象放在数组中
var point = {x:x, y: y, w: squarew, h: height};
pointsArr.push(point);
}
//3. 循环画折线
context.beginPath();
context.fillStyle = "yellowgreen";
for (var i = 0; i<pointsArr.length; i++) {
context.fillRect(pointsArr[i].x, pointsArr[i].y, pointsArr[i].w, pointsArr[i].h);
}

饼状图

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
//1.模拟数据
var datasArr = [
{type: "测试", count: 40, color: "red"},
{type: "产品", count: 30, color: "green"},
{type: "设计人员", count: 70, color: "hotpink"},
{type: "程序员", count: 100, color: "blue"},
{type: "鼓励师", count: 80, color: "yellow"}
];
//2. 计算总数
var sum = 0;
datasArr.forEach(function (t) {
sum += t.count;
});
//3. for循环画扇形
var startRadian = 0;
for (var i = 0; i<datasArr.length; i++) {
var percent = datasArr[i].count/sum;
//每一个扇形的弧度的大小
var radian = 2*Math.PI * percent;
//画扇形并填充
context.beginPath();
context.arc(300, 200, 120, startRadian, startRadian+radian);
context.lineTo(300, 200);
context.closePath();
context.fillStyle = datasArr[i].color;
context.fill();
//每画完一个扇形,起始弧度就增加
startRadian += radian;
}