第154天:canvas基础(一)
一、canvas简介
<canvas>
是 HTML5
新增的,一个可以使用脚本(通常为JavaScript
)在其中绘制图像的 HTML
元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。
它最初由苹果内部使用自己MacOS X WebKit
推出,供应用程序使用像仪表盘的构件和 Safari
浏览器使用。 后来,有人通过Gecko
内核的浏览器 (尤其是Mozilla
和Firefox
),Opera
和Chrome
和超文本网络应用技术工作组建议为下一代的网络技术使用该元素。
Canvas
是由HTML
代码配合高度和宽度属性而定义出的可绘制区域。JavaScript
代码可以访问该区域,类似于其他通用的二维API
,通过一套完整的绘图函数来动态生成图形。
Mozilla 程序从 Gecko 1.8 (Firefox 1.5)开始支持 <canvas>
, Internet Explorer 从IE9开始<canvas>
。Chrome和Opera 9+ 也支持<canvas>
。
二、Canvas基本使用
2.1 <canvas>
元素
<canvas id="tutorial" width="300" height="300"></canvas>
<canvas>
看起来和<img>
标签一样,只是 <canvas>
只有两个可选的属性 width、height
属性,而没有 src、alt
属性。
如果不给<canvas>
设置widht、height
属性时,则默认 width
为300、height
为150,单位都是px
。也可以使用css
属性来设置宽高,但是如宽高属性和初始比例不一致,他会出现扭曲。所以,建议永远不要使用css
属性来设置<canvas>
的宽高。
1、替换内容
由于某些较老的浏览器(尤其是IE9之前的IE浏览器)或者浏览器不支持HTML元素<canvas>
,在这些浏览器上你应该总是能展示替代内容。
支持<canvas>
的浏览器会只渲染<canvas>
标签,而忽略其中的替代内容。不支持 <canvas>
的浏览器则 会直接渲染替代内容。
(1)用文本替换:
1 <canvas> 2 你的浏览器不支持canvas,请升级你的浏览器 3 </canvas>
(2)用 <img>
替换:
1 <canvas> 2 <img src="./美女.jpg" alt=""> 3 </canvas>
结束标签</canvas>
不可省
与 <img>
元素不同,<canvas>
元素需要结束标签(</canvas>
)。如果结束标签不存在,则文档的其余部分会被认为是替代内容,将不会显示出来。
2.2 渲染上下文(Thre Rending Context)
<canvas>
会创建一个固定大小的画布,会公开一个或多个 渲染上下文(画笔),使用 渲染上下文来绘制和处理要展示的内容。
我们重点研究 2D渲染上下文。 其他的上下文我们暂不研究,比如, WebGL使用了基于OpenGL ES的3D上下文 (“experimental-webgl”) 。
1 var canvas = document.getElementById('tutorial'); 2 //获得 2d 上下文对象 3 3var ctx = canvas.getContext('2d');
2.3 检测支持性
1 var canvas = document.getElementById('tutorial'); 2 3 if (canvas.getContext){ 4 var ctx = canvas.getContext('2d'); 5 // drawing code here 6 } else { 7 // canvas-unsupported code here 8 }
2.4 代码模板
1 <html> 2 <head> 3 <title>Canvas tutorial</title> 4 <style type="text/css"> 5 canvas { 6 border: 1px solid black; 7 } 8 </style> 9 </head> 10 <canvas id="tutorial" width="300" height="300"></canvas> 11 </body> 12 <script type="text/javascript"> 13 function draw(){ 14 var canvas = document.getElementById('tutorial'); 15 if(!canvas.getContext) return; 16 var ctx = canvas.getContext("2d"); 17 //开始代码 18 19 } 20 draw(); 21 </script> 22 </html>
2.5 一个简单的例子
绘制两个长方形。
<html> <head> <title>Canvas tutorial</title> <style type="text/css"> canvas { border: 1px solid black; } </style> </head> <canvas id="tutorial" width="300" height="300"></canvas> </body> <script type="text/javascript"> function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); 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); } draw(); </script> </html>
三、绘制形状
3.1 栅格(grid)
和坐标空间
如下图所示,canvas
元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas
元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点来定位。所以图中蓝色方形左上角的坐标为距离左边(X轴)x像素,距离上边(Y轴)y像素(坐标为(x,y))。
后面我们会涉及到坐标原点的平移、网格的旋转以及缩放等。
3.2 绘制矩形
<canvas>
只支持一种原生的 图形绘制:矩形。所有其他图形都至少需要生成一种路径(path
)。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。
canvas
t 提供了三种方法绘制矩形:
-
fillRect(x, y, width, height)
绘制一个填充的矩形
-
strockRect(x, y, width, height)
绘制一个矩形的边框
-
clearRect(x, y, widh, height)
清除指定的矩形区域,然后这块区域会变的完全透明。
说明:
这3个方法具有相同的参数。
x, y
:指的是矩形的左上角的坐标。(相对于canvas
的坐标原点)
width, height
:指的是绘制的矩形的宽和高。
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if(!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.fillRect(10, 10, 100, 50); //绘制矩形,填充的默认颜色为黑色 6 ctx.strokeRect(10, 70, 100, 50); //绘制矩形边框 7 8 } 9 draw();
ctx.clearRect(15, 15, 50, 25);
四、绘制路径(path
)
图形的基本元素是路径。
路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。
一个路径,甚至一个子路径,都是闭合的。
使用路径绘制图形需要一些额外的步骤:
- 创建路径起始点
- 调用绘制方法去绘制出路径
- 把路径封闭
- 一旦路径生成,通过描边或填充路径区域来渲染图形。
下面是需要用到的方法:
-
beginPath()
新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径
-
moveTo(x, y)
把画笔移动到指定的坐标
(x, y)
。相当于设置路径的起始点坐标。 -
closePath()
闭合路径之后,图形绘制命令又重新指向到上下文中
-
stroke()
通过线条来绘制图形轮廓
-
fill()
通过填充路径的内容区域生成实心的图形
4.1 绘制线段
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); //新建一条path 6 ctx.moveTo(50, 50); //把画笔移动到指定的坐标 7 ctx.lineTo(200, 50); //绘制一条从当前位置到指定坐标(200, 50)的直线. 8 //闭合路径。会拉一条从当前点到path起始点的直线。如果当前点与起始点重合,则什么都不做 9 ctx.closePath(); 10 ctx.stroke(); //绘制路径。 11 } 12 draw();
4.2 绘制三角形边框
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.moveTo(50, 50); 7 ctx.lineTo(200, 50); 8 ctx.lineTo(200, 200); 9 ctx.closePath(); //虽然我们只绘制了两条线段,但是closePath会closePath,仍然是一个3角形 10 ctx.stroke(); //描边。stroke不会自动closePath() 11 } 12 draw();
4.3 填充三角形
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.moveTo(50, 50); 7 ctx.lineTo(200, 50); 8 ctx.lineTo(200, 200); 9 10 ctx.fill(); //填充闭合区域。如果path没有闭合,则fill()会自动闭合路径。 11 } 12 draw();
4.4 绘制圆弧
有两个方法可以绘制圆弧:
-
arc(x, y, r, startAngle, endAngle, anticlockwise)
:以
(x, y)
为圆心,以r
为半径,从startAngle
弧度开始到endAngle
弧度结束。anticlosewise
是布尔值,true
表示逆时针,false
表示顺时针。(默认是顺时针)注意:
- 这里的度数都是弧度。
0
弧度是指的x
轴正方形
radians=(Math.PI/180)*degrees //角度转换成弧度
- 1
-
arcTo(x1, y1, x2, y2, radius)
:根据给定的控制点和半径画一段圆弧,最后再以直线连接两个控制点。
圆弧案例1:
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.arc(50, 50, 40, 0, Math.PI / 2, false); 7 ctx.stroke(); 8 } 9 draw();
圆弧案例2:
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.arc(50, 50, 40, 0, Math.PI / 2, false); 7 ctx.stroke(); 8 9 ctx.beginPath(); 10 ctx.arc(150, 50, 40, 0, -Math.PI / 2, true); 11 ctx.closePath(); 12 ctx.stroke(); 13 14 ctx.beginPath(); 15 ctx.arc(50, 150, 40, -Math.PI / 2, Math.PI / 2, false); 16 ctx.fill(); 17 18 ctx.beginPath(); 19 ctx.arc(150, 150, 40, 0, Math.PI, false); 20 ctx.fill(); 21 22 } 23 draw();
圆弧案例3:
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.moveTo(50, 50); 7 //参数1、2:控制点1坐标 参数3、4:控制点2坐标 参数4:圆弧半径 8 ctx.arcTo(200, 50, 200, 200, 100); 9 ctx.lineTo(200, 200) 10 ctx.stroke(); 11 12 ctx.beginPath(); 13 ctx.rect(50, 50, 10, 10); 14 ctx.rect(200, 50, 10, 10) 15 ctx.rect(200, 200, 10, 10) 16 ctx.fill() 17 } 18 draw();
arcTo
方法的说明:
这个方法可以这样理解。绘制的弧形是由两条切线所决定。
第 1 条切线:起始点和控制点1决定的直线。
第 2 条切线:控制点1 和控制点2决定的直线。
其实绘制的圆弧就是与这两条直线相切的圆弧。
4.5 绘制贝塞尔曲线
4.5.1 什么是贝塞尔曲线
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。
一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。
贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。
贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算法开发,以稳定数值的方法求出贝兹曲线。
一次贝塞尔曲线(线性贝塞尔曲线)
一次贝塞尔曲线其实是一条直线。
二次贝塞尔曲线
三次贝塞尔曲线
4.5.2 绘制贝塞尔曲线
绘制二次贝塞尔曲线
quadraticCurveTo(cp1x, cp1y, x, y)
:
说明:
参数1和2:控制点坐标
参数3和4:结束点坐标
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.moveTo(10, 200); //起始点 7 var cp1x = 40, cp1y = 100; //控制点 8 var x = 200, y = 200; // 结束点 9 //绘制二次贝塞尔曲线 10 ctx.quadraticCurveTo(cp1x, cp1y, x, y); 11 ctx.stroke(); 12 13 ctx.beginPath(); 14 ctx.rect(10, 200, 10, 10); 15 ctx.rect(cp1x, cp1y, 10, 10); 16 ctx.rect(x, y, 10, 10); 17 ctx.fill(); 18 19 } 20 draw();
绘制三次贝塞尔曲线
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
:
说明:
参数1和2:控制点1的坐标
参数3和4:控制点2的坐标
参数5和6:结束点的坐标
1 function draw(){ 2 var canvas = document.getElementById('tutorial'); 3 if (!canvas.getContext) return; 4 var ctx = canvas.getContext("2d"); 5 ctx.beginPath(); 6 ctx.moveTo(40, 200); //起始点 7 var cp1x = 20, cp1y = 100; //控制点1 8 var cp2x = 100, cp2y = 120; //控制点2 9 var x = 200, y = 200; // 结束点 10 //绘制二次贝塞尔曲线 11 ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); 12 ctx.stroke(); 13 14 ctx.beginPath(); 15 ctx.rect(40, 200, 10, 10); 16 ctx.rect(cp1x, cp1y, 10, 10); 17 ctx.rect(cp2x, cp2y, 10, 10); 18 ctx.rect(x, y, 10, 10); 19 ctx.fill(); 20 21 } 22 draw();