过个年一下荒废了个把月。 近来刚打仗canvas,将一些观点点简朴归结下,canvas是基于像素的图象API,与svg的最大的区分在于canvas须要重绘(canvas移除图片时须要从新绘制,而SVG能够经由过程编辑元素节点来编辑图片),而且基于基于像素绘制(svg望文生义是矢量),更细致的对照mark在此:?SVG 与 HTML5 的 canvas 各有什么长处 而且我个人以为虽然canvas的API也很庞杂,然则svg更庞杂,囧rz。以下是我将我打仗canvas过程当中以为须要厘清的观点点归结以下。
基本构造
canvas元素本身没有任何表面,它就是一块空缺画板,供应给JS的一套API,最早由Safari引入,IE9之前能够运用一些类库在IE中模仿canvas,大部分的API都不在canvas元素本身定义,canvas元素本身属性与通例的HTML元素并没有多大区分, 它的画图API都定义在一个CanvasRenderingContext2D
对象上(这里简朴翻译成上下文对象),该对象经由过程getContext()
要领取得,代码示例:
<html>
<head>
<title>坐标系demo</title>
</head>
<body>
<canvas id = 'square' width= 200 heigth=200></canvas>
</body>
<script>
var canvas = document.getElementById('square')
var ctx = canvas.getContext('2d')//2d示意画板维度,输入3d将获得一个更加庞杂的3d图形API,也称WebGL
默许坐标系与当前坐标系
图象绘制须要参考坐标系定位, canvas的默许坐标系即画布左上角原点(0,0),然则假如图象的每次绘制都参考一个牢固点将缺乏天真性,因而canvas引入了“当前坐标系”的观点,所谓“当前坐标系”即指图象在此时绘制的时刻所参考的坐标系,它也会作为图象状况(图象状况的观点将在后引见)的一部分。比方rotate
扭转操纵,转变当前坐标系也就是转变了rotate
的参考点,试想下假如没有当前坐标系的观点,无论是扭转,缩放,倾斜等操纵不就只能参考画布左上角原点了吗(默许坐标系)?
canvas供应了translate()
和setTransform()
这两个要领离别影响当前坐标系与默许坐标系。
translate()
与setTransform()
要领
translate()
要领将坐标原点举行上下左右挪动。它所影响的就是在图象在绘制的时刻所参考的“当前坐标系”,举个例子?:
能够直接在demo中操纵视察:
代码:
<html>
<head>
<title>坐标系demo</title>
</head>
<body>
<canvas id = 'square' width= 200 heigth=200></canvas>
</body>
<script>
var canvas = document.getElementById('square')
var ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.translate(20,20) //translate影响了当前坐标系
ctx.moveTo(0,0)
ctx.lineTo(100,20)
ctx.stroke()
</script>
</html>
无任何坐标系变化的图象绘制:
translate()
要领将坐标原定挪动到(20,20)后获得当前坐标系后的绘制
相识这点后setTransform()
也很轻易,该要领影响的是默许坐标系,也就是说它并非将原点移来移去,而是重置当前坐标系,定义一个新的默许坐标系,什么叫影响默许坐标系,比方说前面的translate()
所挪动的坐标原点(0,0)照样初始的默许坐标系,而如今setTransform()
所影响的就是这个原点(0,0)的坐标系,照样之前的demo,当到场ctx.setTransform(1,0.5,-0.5,1,30,10)
这条语句后,图象绘制将变成:
这是因为setTransform()
将默许坐标系从新定义了,因而translate()
基于新的默许坐标系来获得当前坐标系。理解了这两个观点也就掌握了canvas中坐标系的变更。
setTransform()
与transform()
要领
setTransform()
这个API略庞杂, 它所接收的参数与transform()
(运用transform()
可直接获得一个变更构造,可替代rotate()
等要领,而且更加天真)一样为6个参数,setTransform(a,b,c,d,e,f)
而坐标系变化的道理就是经由过程与这6个参数举行以下运算后得出的:
x’ = ax + cy +e
y’ = bx + dy +f
这类坐标系变更也被称为仿射变更(affine transform),关于该变更的栗子可参考这两篇博客:
?Html5 Canvas 变更矩阵与坐标变形之间的关联
途径
途径是绘制一切图形的基本,不同于SVG中path
运用属性M
,L
,A
等掌握的XML文档,canvas挪用上下文对象的要领来完成途径的绘制,挪用beginPath()
最先一段新途径,每段路经又有子途径,恰是依托这些子途径使得图构成形。挪用beginPath()
后挪用MoveTo()
最先一段子途径。绘制完成后运用closePath()
闭合途径,从而构成一个闭合地区,这时刻就能够运用fill()
等要领添补该地区了。每次最先一段新途径的绘制必需再次挪用beginPath()
,不然新绘制的途径将作为之前途径的子途径继承绘制。
类似于lineTo()
是最简朴的直线段途径要领, canvas还供应了bezierCurveTo()
和quadraticCurveTo()
这些庞杂的曲线途径要领,非常庞杂,所以预计平常这类操纵照样先找轮子处置惩罚。
别的须要注重的是,当一条途径的两便条途径不订交的时刻(比方绘制一个镂空的图形),画布将采纳“非零绕数准绳”推断某点是在途径内照样途径外, 如许以便于添补的时刻区分哪些地区是须要添补的。
有关非零绕数准绳的道理能够参考这里:mark? 非零围绕数划定规矩和奇-偶划定规矩
canvas的图象状况
canvas的属性与要领与我们面向对象中的属性要领并没有太大区分,只是这里触及到了一个图象状况的观点。在canvas中,没法经由过程getContext()
要领取得多个上下文(context)对象,而图象属性都是基于canvas的上下文对象,也就是说没法同时具有两个属性。抽象地比方就是图象属性就像画笔, 粗细,大小,色彩。因为同一时间只能有一个上下文对象所以只能同一时间运用一支画笔。这时刻当须要别的的图象属性(另一支画笔)的时刻就只能经由过程保留当前图象状况,然后新建一个图象状况来切换。
这时刻就须要借助save()
和restore()
来切换图象状况,每次save()
都将保留当前图象状况,图象状况包含当前的图象属性,当前坐标系,裁剪地区等信息。比方以下demo以两种色彩画线:
直接在demo中修正代码视察图象状况demo
JS代码:
var canvas = document.getElementById('square')
var ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.strokeStyle = "red"
ctx.moveTo(0,0)
ctx.lineTo(100,20)
ctx.stroke()
ctx.save()//保留当前图象状况(画笔)
ctx.beginPath()
ctx.strokeStyle = "blue"
ctx.moveTo(0,0)
ctx.lineTo(100,40)
ctx.stroke()
ctx.restore()//恢复到近来保留图象状况(画笔)
ctx.beginPath()
ctx.moveTo(0,0)
ctx.lineTo(100,60)
ctx.stroke()
输出以下:
这些图象属性包含:
fillStyle
font
globalAlpha
globalCompositeOperation
lineCap
lineJoin
lineWidth
miterLimit
textAlign
textBaseline
shadowBlur
shadowColor
shadowOffsetX
shadowOffsetY
strokeStyle
canvas背景
平常的纯色背景添补能够运用fillStyle
属性,然则当触及更庞杂的图片或许渐变色添补就须要CanvasPattern
和CanvasGradient
对象了,能够经由过程creatPattern()
要领获得CanvasPattern
,这里须要注重的是该API不仅能够代入平常的图片,也能够运用canvas元素,比方画面外一个不可见的canvas元素用于插进去。
关于这两个API的细节直接参考文档:
像素操纵
基于像素的canvas能够完成针对单个像素的操纵,这也是画布底层的API,经由过程挪用getImageData()
要领将返回一个ImageData
对象,该对象示意画布中原始的RGBA像素信息,经由过程挪用creatImageData()
要领也能够建立一个空的ImageData
对象,末了putImageData()
要领将处置惩罚后的像素输出到画布中。
微软有篇不错的教程(运用 Canvas 将彩色照片变成黑白照片)诠释像素操纵,个中的操纵是将彩色照片转成灰白,运用的道理是将RGB三个重量提取出来,经由盘算后(症结盘算语句以下)从新赋值为灰度变量。
myGray = parseInt((myRed + myGreen + myBlue) / 3);
// Assign average to red, green, and blue.
myImage.data[i] = myGray;
myImage.data[i + 1] = myGray;
myImage.data[i + 2] = myGray;