通过<canvas>标签可以让我们使用JavaScript脚本绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或渲染。

WebGL也使用<canvas>元素在网页上绘制硬件加速的3D图形。

示例

1
<canvas id="canvas"></canvas>
1
2
3
4
5
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.fillStyle = 'green'; // 填充颜色
ctx.fillRect(10, 10, 100, 100); // 确定位置,画个矩形

基础信息

<canvas>标签只是一个空白的画布,供JavaScript在上面进行绘画等操作。<canvas>标签和其它DOM标签一样拥有标签属性用于定义标签的特性。<canvas>标签常用属性有widthheight,通过它设定画布的大小。如果不指定widthheight则默认宽高是:300x150

绘图步骤:

  1. 获取画布节点
  2. 获取渲染上下文和绘画功能
  3. 开始绘画
1
2
3
4
5
6
7
8
9
 var canvas = document.getElementById('canvasNode'); // 获取画布节点
var ctx = canvas.getContext('2d'); // 获取渲染上下文和绘画功能 可以利用WebGL进行3D绘画

// 进行绘画操作
ctx.fillStyle = "rgb(200,0,0)"; // 填充图形颜色
ctx.fillRect (10, 10, 55, 50);

ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; // 填充图形颜色加透明度
ctx.fillRect (30, 30, 55, 50);

<canvas>画布是个栅格布局,起点(0,0)是左上角,向右是x轴,向下是y轴。所有元素的位置都相对于原点定位。

绘制图形

绘制矩形

canvas只提供了矩形的绘制方法,其它图形绘制需要通过路径完成。

矩形的绘制有三个方法:

  1. fillRect(x, y, width, height) 绘制一个填充的矩形
  2. strokeRect(x, y, width, height) 绘制一个矩形的边框
  3. clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明。
1
2
3
4
5
6
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.fillRect(25, 25, 100, 100); // 绘制了一个边长为100px的黑色正方形
ctx.clearRect(45, 45, 60, 60); // 从正方形的中心开始擦除了一个60*60px的正方形
ctx.strokeRect(50, 50, 50, 50); // 清除区域内生成一个50*50的正方形边框

绘制路径

路径:通过连续的点集合形成的不同颜色和宽度的线段或曲线。将多个路径连接闭合可以形成完整的图形。

canvas除了矩形其它图形都需要通过路径绘制。绘制路径的一般通过一下几步:

  1. 创建路径起始点
  2. 使用画图命令去画出路径
  3. 把路径封闭
  4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。

注意: 图形是通过不同路径组成的,不同的路径可能并不连续,这时你需要通过moveto移动画笔位置。

路径绘制的方法有:

  1. beginPath() 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。

  2. 通过函数指定绘制路径,在调用绘制函数前你也许需要通过moveTo(x,y)移动画笔到合适位置。

  3. closePath() 闭合路径之后图形绘制命令又重新指向到上下文中。

    就是闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。

  4. stroke() 通过线条来绘制图形轮廓。

  5. fill() 通过填充路径的内容区域生成实心的图形。

注意: 当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。

绘制三角形

1
2
3
4
5
6
7
8
9
10
11
12
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75); // 绘制直线
ctx.lineTo(100, 25); // 绘制直线
ctx.fill(); // 自动合并路径,填充合并后的图形
}
}

绘制直线

lineTo(x, y)

绘制一条从当前位置到指定x以及y位置的直线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');

// 填充三角形
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill(); // 自动闭合路径,填充默认的黑色

// 描边三角形
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath(); // 结束路径绘画,会自动合并路径
ctx.stroke(); // 通过黑色描绘出路径形成的图形
}
}

注意: 当一个路径绘制结束,如果需要再绘制路径需要再调用beginPath

绘制圆弧

绘制圆弧的方法有:arc()arcTo()

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)
    • x 圆弧中心(圆心)的 x 轴坐标
    • y 圆弧中心(圆心)的 y 轴坐标
    • radius 圆弧的半径
    • startAngle 圆弧的起始点, x轴方向开始计算,单位以弧度表示
    • endAngle 圆弧的终点, 单位以弧度表示
    • anticlockwise | 可选(Boolean) 如果为 true,逆时针绘制圆弧,反之,顺时针绘制。 默认是顺时针
  • arcTo(x1, y1, x2, y2, radius) 根据控制点和半径绘制圆弧路径,使用当前的描点(前一个moveTo或lineTo等函数的止点)。根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。

注意 arc()函数中表示角的单位是弧度,不是角度。角度与弧度的js表达式:弧度=(Math.PI/180)*角度

Math.PI = 3.1415936 = 180度

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
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');

for(var i=0;i<4;i++){
for(var j=0;j<3;j++){
ctx.beginPath();
var x = 25+j*50; // x 坐标值
var y = 25+i*50; // y 坐标值
var radius = 20; // 圆弧半径
var startAngle = 0; // 开始点
var endAngle = Math.PI+(Math.PI*j)/2; // 结束点
var anticlockwise = i%2==0 ? false : true; // 顺时针或逆时针

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

if (i>1){
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}

绘制曲线

通过贝塞尔曲线用于绘制曲线,canvas提供了二次贝塞尔曲线和三次贝塞尔曲线的绘制方法。

  • quadraticCurveTo(cp1x, cp1y, x, y) 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。

绘制二次贝塞尔曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');

// 二次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
}
}

绘制三次贝塞尔曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');

//三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
}
}

填充样式和颜色

Colors

canvas对颜色的处理有两种:

  1. fillStyle = color 设置图形的填充颜色
  2. strokeStyle = color 设置图形轮廓的颜色。

color 可以是表示 CSS 颜色值的字符串,渐变对象或者图案对象。我们迟些再回头探讨渐变和图案对象。默认情况下,线条和填充颜色都是黑色(CSS 颜色值 #000000

1
2
3
4
5
// 这些 fillStyle 的值均为 '橙色'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";

透明度 Transparency

通过设置 globalAlpha属性或者使用一个半透明颜色作为轮廓或填充的样式。

globalAlpha = transparencyValue 这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。

透明功能也可以使用strokeStylefillStyle 属性完成。

1
2
3
// 指定透明颜色,用于描边和填充样式
ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.fillStyle = "rgba(255,0,0,0.5)";

线型 Line styles

lineWidth = value 设置线条宽度

1
2
3
4
5
6
7
8
9
10
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (var i = 0; i < 10; i++){
ctx.lineWidth = 1+i;
ctx.beginPath();
ctx.moveTo(5+i*14,5);
ctx.lineTo(5+i*14,140);
ctx.stroke();
}
}

lineCap = type 设定线条与线条间接合处的样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineCap = ['butt','round','square'];

// 创建路径
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(140,10);
ctx.moveTo(10,140);
ctx.lineTo(140,140);
ctx.stroke();

// 画线条
ctx.strokeStyle = 'black';
for (var i=0;i<lineCap.length;i++){
ctx.lineWidth = 15;
ctx.lineCap = lineCap[i];
ctx.beginPath();
ctx.moveTo(25+i*50,10);
ctx.lineTo(25+i*50,140);
ctx.stroke();
}
}

miterLimit = value 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。lineJoin 的属性值决定了图形中两线段连接处所显示的样子。它可以是这三种之一:round, bevelmiter。默认是 miter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineJoin = ['round', 'bevel', 'miter'];
ctx.lineWidth = 10;
for (var i = 0; i < lineJoin.length; i++) {
ctx.lineJoin = lineJoin[i];
ctx.beginPath();
ctx.moveTo(-5, 5 + i * 40);
ctx.lineTo(35, 45 + i * 40);
ctx.lineTo(75, 5 + i * 40);
ctx.lineTo(115, 45 + i * 40);
ctx.lineTo(155, 5 + i * 40);
ctx.stroke();
}
}

getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组

setLineDash(segments) 设置当前虚线样式

1
2
3
4
5
6
7
8
9
10
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.setLineDash([5, 15]);
console.log(ctx.getLineDash()); // [5, 15]

ctx.beginPath();
ctx.moveTo(0,100);
ctx.lineTo(400, 100);
ctx.stroke();

lineDashOffset = value 设置虚线样式的起始偏移量

渐变 Gradients

canvas中绘制渐变图形分为四个步骤:

  1. 创建处理渐变的对象,这个对象定义了渐变位置范围(createLinearGradientcreateRadialGradient)。
  2. 设置渐变颜色和渐变速率(addColorStop)。
  3. 将渐变对象赋予fillStylestrokeStyle
  4. 绘制图形
1
2
3
4
5
6
7
8
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var gradient = ctx.createLinearGradient(0,0,200,0);
gradient.addColorStop(0,"green");
gradient.addColorStop(1,"white");
ctx.fillStyle = gradient;
ctx.fillRect(10,10,200,100);

阴影 Shadows

canvas中可以给图形或文字设置阴影。需要使用的方法有:

shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0

  • shadowBlur = float shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。注意:将shadowColor属性设置成不透明,阴影才会被绘制。
  • shadowColor = color shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。
1
2
3
4
5
6
7
8
9
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.shadowColor = "black";
ctx.shadowOffsetX = 10;
ctx.shadowBlur = 10;

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100);

图案样式 Patterns

canvas图形处理可以填充颜色。渐变色,还可以填充图案,借用方法createPattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');

// 创建新 image 对象,用作图案
var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
img.onload = function() {

// 创建图案
var ptrn = ctx.createPattern(img, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 150, 150);

}
}

注意: 与 drawImage 有点不同,你需要确认 image 对象已经装载完毕,否则图案可能效果不对的。

Canvas填充

canvas中填充方法是fillfill可以结合clip设置路径的截取部分填充。例如:

1
2
3
4
5
6
7
8
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// Create clipping region
ctx.arc(100, 100, 75, 0, Math.PI*2, false);
ctx.clip();

ctx.fillRect(0, 0, 100,100);

fill可以设置填充规则:

  1. 非零环绕原则 nonzero 默认
  2. 奇偶原则evenodd

非零环绕原则 nonzero

是用来判断哪些区域属于路径内( 计算结果非0,即为路径内 )。

  • 在路径包围的区域中,随便找一点,向外发射一条射线,
  • 和所有围绕它的边相交,
  • 然后开启一个计数器,从0计数,
  • 如果这个射线遇到顺时针围绕,那么+1,
  • 如果遇到逆时针围绕,那么-1,
  • 如果最终值非0,则这块区域在路径内。

奇偶原则 evenodd

  • 在路径包围的区域中,随便找一点,向外发射一条射线,
  • 和所有围绕它的边相交,
  • 查看相交线的个数,如果为奇数,就填充,如果是偶数,就不填充。(0是偶数)
1
2
3
4
5
6
7
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
ctx.beginPath();
ctx.arc(50, 50, 30, 0, Math.PI*2, true);
ctx.arc(50, 50, 15, 0, Math.PI*2, true);
ctx.fill("evenodd"); // 奇偶原则
}

上图吐槽部分是两个圆间隙部分,因为只有这部分的点向四周放射直线和路径交集次数是奇数。