<canvas> 是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形。例如:画图,合成照片,创建动画甚至实时视频处理与渲染。
兼容性方面,除了一些骨灰级浏览器IE6、IE7、IE8等,大部分现代浏览器都能支持。
一、属性与方法
1)属性
<canvas> 看起来和 <img> 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上,<canvas> 标签只有两个属性—— width和height。
<canvas id="strick" width="150" height="150"></canvas>
还有些默认的属性,id、style等
2)方法
1. getContext(in DOMString contextId)
canvas起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。这个方法是用来获得渲染上下文和它的绘画功能。
contextType中可选的参数有“2d”、“webgl”、“webgl2”、“bitmaprenderer”。
如果是“2d”,就会返回 CanvasRenderingContext2D 对象。如果是“webgl”,就会返回 WebGLRenderingContext 对象。
contextAttributes属性会根据 “2d” 或 “webgl” 会需要不同的参数。
var canvas = document.getElementById('strick'); var ctx = canvas.getContext('2d');
2. toDataURL(in optional DOMString type, in any ...args)
返回一个data: URL,将canvas中的图片编码成字符串形式,有多种格式选择,type参数的默认值为image/png。
曾做过一个图片合成的功能,就使用到了这个功能。这里要注意一个“画布污染”。
就是嵌入的图片是跨域的,那么就不能使用这个方法。在《预览、旋转、合成》做过简单的分析。
3. toBlob(in Function callback, in optional DOMString type, in any ...args)
返回一个Blob(binary large object)对象。Blob代表了一段二进制数据,就是一个包含只读原始数据的类文件对象。
在《移动端图片操作(一)——上传》曾做过简单的介绍。
二、绘制2D图形
这里绘制的是2D图形,会用到 CanvasRenderingContext2D 对象中的属性或方法。
在MDN上面有个基础教程《Canvas教程》,覆盖面蛮全的。
1)坐标空间
画布的起点为左上角,这个起始点通过方法 translate 可以自定义,例如做旋转缩放等操作。
上图所示,canvas的坐标轴与普通的坐标轴是相反的。
所以顺时针是正值,逆时针是负值。
2)绘制形状
矩形是canvas支持的唯一一种原生的图形绘制。要画其他形状,就需要通过绘制路径实现。绘制矩形提供了3个方法。
路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。有操纵路径和绘制路径(包含贝塞尔曲线)的方法。
操作过程大致为4步,先创建路径起始点,再画出路径,然后闭合路径,最后填充。
1. 简单图形
下图中是些简单的图形,两个三角形中有一个是旋转了画布的,逆时针画的半圆,三个圆圈与一个半圆组成的笑脸,用贝塞尔曲线画的对话气泡,可以在线调试下面的效果。
2. 复杂图形
在CSS中,边框、字体等都能设置宽度、大小、颜色等,高级点的还有阴影、渐变、rgba等。
在canvas中也有相应的操作,绘制文本、线型、文本样式、填充和描边样式、渐变和图案、阴影。
有网友画了头灰太狼,非常逼真,可以在线调试,通过查看源码,里面用到的就是beginPath、moveTo、quadraticCurveTo等路径相关的方法或属性。
3)使用图片
canvas可以对图片进行合成、缩放、裁剪、旋转、变形等操作。
a. 简单的合成
现在有些网站会让你DIY做张海报,然后分享到朋友圈。前段时间做了个简易的海报,仅仅是将图片合成在一起,没有做涂鸦等操作,详细的介绍可以查看《移动端图片操作系列》
b. 高级点的合成
高级点的制作海报,能够输入自定义的文字,用到了上面所说的绘制形状的一些概念,在看源码的时候,发现引用了脚本“hidpi-canvas-polyfill”,解决canvas 在高清屏下绘制图片变模糊。
除了基础的操作,还有高级的像素操作,获取图片中某一像素的RGBA,然后修改其中的R、G、B或A的值,来修改颜色或透明度。
例如鼠标移动获取RGBA值,将彩色照变成黑白照,打马赛克等。
第一张图片是获取像素值,第二张图是变灰。
除了能操纵图片,canvas还能操纵视频,也就是<video>标签。
4)变换矩阵
CSS3中的transform有个矩阵的概念,旋转、平移、扭曲、缩放等都可以用矩阵来实现。关于CSS3的动画可以参考《CSS3中的动画效果记录》
canvas中有一个rotate()方法,实现旋转,但其实旋转的是canvas画布,并不是旋转画出来的那个图形。
如果用transform()方法的话,就可以实现旋转图形。这边有个对比实例,可以在线调试。
2D渲染的上下文矩阵如下,可以忽略最后一行:
2D渲染的坐标计算如下,关于计算过程可以研究下线性代数,简单点说就是a和x、y、1分别相乘。
rotate与skew的矩阵计算会涉及到三角函数中的正弦、余弦还有正切。
//scale()对应的矩阵 下面是CSS3中的写法,对应的方法是CanvasRenderingContext2D.transform matrix(sx,0,0,sy,0,0);/*sx和sy分别对应X轴和Y轴的缩放比率*/ //rotate()对应的矩阵 //在JS中θ对应的是弧度转换公式为 弧度= 2 * PI / 360 * 角度 matrix(cosθ,sinθ,-sinθ,cosθ,0,0); //skew()对应的矩阵 //θy对应的是Y轴的弧度 θx对应的是X轴的弧度 matrix(1,tan(θy),tan(θx),1,0,0)
矩阵计算还会涉及到很多其他的数学知识,例如一次函数,我基本都已经忘记了,囧,都得重新查看了。
有个在线编辑matrix的网站,可以在线制作。
5)相关的计算公式
a. 三角函数基础公式
JavaScript中有两个反正切函数,Math.atan(ratio)与Math.atan2(y, x)。
第一个方法返回一个 -pi/2 到 pi/2 弧度之间的数值,第二个方法返回一个 -pi 到 pi 之间的数值。
b. 角度与弧度的互转
在JavaScript中,三角函数等用的是弧度,例如Math.sin、Math.cos等方法;旋转是用角度。所以两者之间是需要换算的。
radians是弧度,degrees是角度
c. 两点间的距离
使用了直角三角形的勾股定理。坐标轴中的P1与P2,就相当于公式中的A与B。
6)绘制文本
文本一般会设置文本内容(fillText)、尺寸字体(font),颜色(fillStyle),对齐(textAlign、textBaseline)等。
ctx.fillStyle = '#1d1d72';//字体颜色 ctx.font = size+'px serif';//字体尺寸 ctx.textBaseline = 'middle';//上下居中 ctx.textAlign = 'center';//水平居中 ctx.fillText(param.txt, width/2, height/2);
三、canvas动画
要实现动画就需要用JavaScript实现很多物理概念。
关于canvas动画可以参考两本书《HTML5 JavaScript动画基础》和《HTML5 Canvas基础教程》。pdf和源码都已经分享了出来。
1)速度向量
速度(speed)是速度向量(velocity)中的一部分,速度向量还包括方向。
用vx表示x轴上的速度向量,有vy表示y轴上的速度向量。还可以表示角度旋转,用vr表示。
vx为正数表示向右,负数表示向左。vy为正数表示向下,负数表示向上。
下图就是一个向右移动的球,vx=1,详细代码可以参考这里。
2)角速度
假设物体以1个像素的速度向45°方向移动,那么vx和vy可以通过余弦与正弦获取。
速度和方向映射成一个直角三角形。
vx = Math.cos(angle)*speed;
vy = Math.sin(angle)*speed;4
上图就是经过计算后的角速度示例。
3)加速度
速度向量改变的是物体的物理位置,加速度改变的是速度向量。
下面的图片与速度向量中的gif内容是相同的,只是每次循环给vx加了0.1个值。
4)边界
处理边界,有多种选择,移除、置回边界内、屏幕环绕、反弹回边界内等。
左图是置回边界内,右图是反弹回边界内的效果。
5)摩擦力
一个比较简单的实现是,设置一个friction(摩擦力变量,一个小于1的数字),将这个数与vx和vy分别相乘,获取新的值。
这两个值会越来越小,最终停止。
除了上面几种基础的动画,还有些高级的动画,缓动、弹动、碰撞检测等。
四、canvas的优缺点
1)优点:
1. 在呈现图像、文本和动画的时候,由于 canvas 不存在与解析 HTML 和维护分层文档模型有关的开销,因此,在 canvas 中这些任务总是要比在 HTML 中快。
2. 可以实现一致的、跨平台的呈现。例如浏览器的transform属性,不同浏览器可能就需要使用自己独特的前缀。
3. 画出来的图形可以直接保存为 .png 或者 .jpg的图形。
4. 最适合于画光栅图像(如游戏和不规则几何图形等),编辑图片还有其他基于像素的图形操作。
2)缺点:
1. 由于 canvas 里面没有dom节点,当某个元素需要执行交互事件(如click)的时候只能是通过坐标来判断。
2. 没有实现动画的API,你必须依靠定时器和其他事件来更新 canvas。
3. 对文本的渲染支持是比较差,例如自动换行。canvas中也不存在超链接的概念。
4. 由于在 canvas 上以编程方式显示的文本其实就是位图,因此搜索爬行器将完全忽略文本。文本内容也无法被屏幕阅读器识别。