## Canvas **二、canvas** HTML5`<canvas>`元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成。 `<canvas> `标签只是图形容器,您必须使用脚本来绘制图形。 要使用Canvas API,首先需要新建一个`<canvas>`网页元素 ``` <canvas id="canvas" width="400" height="200"> 您的浏览器不支持canvas! </canvas> ``` width 和 height 属性定义的画布的大小。 接着,我们可以通过标签选择获取到<canvas>元素: ``` var canvas = document.getElementById('canvas'); ``` 然后,创建context对象: ``` if(canvas.getContext){ var ctx = canvas.getContext('2d'); } ``` 调用getContext()方法时,传递一个“2d”参数,会获得一个CanvasRenderingContext2D对象,使用该对象可以在画布上绘制二维图形。我们也可将CanvasRenderingContext2D简称为“上下文对象”。 **2.1 canvas的绘图用法** canvas画布提供了一个用来作图的平面空间,该空间的每个点都有自己的坐标,x表示横坐标,y表示竖坐标。原点(0, 0)位于图像左上角,x轴的正向是原点向右,y轴的正向是原点向下。 **(1)绘制矩形** - rect(x,y,width,height)方法用来绘制矩形。 - fillRect(x, y, width, height)方法用来绘制矩形,它的四个参数分别为矩形左上角顶点的x坐标、y坐标,以及矩形的宽和高。fillStyle属性用来设置矩形的填充色。 - strokeRect(x, y, width, height)方法同样是绘制矩形,但只画线不填充,也可以说是画空心矩形。strokeStyle属性用来设置矩形的绘制色。 - clearRect(x, y, width, height)方法用来清除某个矩形区域的内容。 **(2)绘制圆形和扇形** - arc(x, y, radius, startAngle, endAngle, anticlockwise)方法用来绘制圆形和扇形。 arc方法的x和y参数是圆心坐标,radius是半径,startAngle和endAngle则是扇形的起始角度和终止角度(以弧度表示),anticlockwise表示做图时应该逆时针画(true)还是顺时针画(false)。 - arcTo(x1,y1,x2,y2,r)方法在画布上创建介于两个切线之间的弧/曲线。 参数:x1 弧的起点的 x 坐标,y1 弧的起点的 y 坐标,x2 弧的终点的 x 坐标,y2 弧的终点的 y 坐标,r 弧的半径。 **(3)绘制文本** - fillText(string, x, y) 用来绘制文本,它的三个参数分别为文本内容、起点的x坐标、y坐标。使用之前,需用font设置字体、大小、样式(写法类似与CSS的font属性)。 strokeText(string,x,y)方法,用来添加空心字。 ``` // 设置字体 ctx.font = "Bold 20px Arial"; // 设置对齐方式,start、end、right、center ctx.textAlign = "left"; // 设置填充颜色 ctx.fillStyle = "#008600"; //设置垂直对齐方式,top、hanging、middle、alphabetic、ideographic、bottom ctx.textBaseline = top; ``` 我们还可以计算字体宽度(px): ``` var name = 'aaa'; ctx.measureText(name); ``` measureText() 方法返回包含一个对象,该对象包含以像素计的指定字体宽度。 ``` ctx.measureText(name).width ``` **(4)绘制路径** `beginPath()`方法表示开始绘制路径,`moveTo(x, y)`方法设置线段的起点,`lineTo(x, y)`方法设置线段的终点,`stroke()`方法用来给透明的线段着色。 ``` ctx.beginPath(); // 开始路径绘制 ctx.moveTo(20, 20); // 设置路径起点,坐标为(20,20) ctx.lineTo(200, 20); // 绘制一条到(200,20)的直线 ctx.lineWidth = 1.0; // 设置线宽 ctx.strokeStyle = '#CC0000'; // 设置线的颜色 ctx.stroke(); // 进行线的着色,这时整条线才变得可见 moveTo()和lineTo()方法可以多次使用。 ``` 注意:如果使用closePath()方法,会自动绘制一条当前点到起点的直线,形成一个封闭图形。 fill()和stroke()分别填充当前绘图(路径)和绘制已定义的路径。 **isPointInPath()** ``` isPointInPath(x,y); ``` 如果指定的点位于当前路径中,isPointInPath() 方法返回 true,否则返回 false。 **clip()** clip()方法从原始画布中剪切任意形状和尺寸。 注意:一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。 closePath()方法创建当前点回到起始点的路径。 **其他样式属性:** - lineCap 设置或返回线条的结束端点样式,可能值: butt|round|square(平直边缘|圆形线帽|正方形线帽) - lineJoin 设置或返回两条线相交时,所创建的拐角类型,可能值:bevel|round|miter(斜角|圆角|尖角(默认值)) - lineWidth 设置或返回当前的线条宽度。 - miterLimit 正数,设置或返回最大斜接长度。如果斜接长度超过 miterLimit 的值,边角会以 lineJoin 的 "bevel" 类型来显示。 - fillStyle 设置填充色 - strokeStyle 设置绘制线色 颜色格式: ``` //可直接用颜色名称 'red' 'green' //十六进制颜色 '#d9d9d9' //rgb rgb(0,0,0) //rgba rgba(0,0,0,1) ``` **(5)设置渐变色** **线性渐变** createLinearGradient方法用来设置渐变色。 ``` var gradient = ctx.createLinearGradient(0, 0, 0, 160); gradient.addColorStop(0, "#BABABA"); gradient.addColorStop(1, "#636363"); ``` createLinearGradient()方法的参数是(x1, y1, x2, y2),其中x1和y1是起点坐标,x2和y2是终点坐标。通过不同的坐标值,可以生成从上至下、从左到右的渐变等等。 使用方法: ``` ctx.fillStyle = myGradient; ctx.fillRect(10,10,200,100); ``` **环形渐变** createRadialGradient()方法的参数是(x0,y0,r0,x1,y1,r1),其中x0 渐变的开始圆的 x 坐标,y0 渐变的开始圆的 y 坐标,r0 开始圆的半径,x1 渐变的结束圆的 x 坐标,y1 渐变的结束圆的 y 坐标,r1 结束圆的半径。 ``` var grd=ctx.createRadialGradient(75,50,5,90,60,100); grd.addColorStop(0,"red"); grd.addColorStop(1,"white"); ctx.fillStyle=grd; ctx.fillRect(10,10,150,100); ``` **(6)设置阴影** 我们还可以设置阴影 ``` ctx.shadowOffsetX = 10; // 设置水平位移 ctx.shadowOffsetY = 10; // 设置垂直位移 ctx.shadowBlur = 5; // 设置模糊度 ctx.shadowColor = "rgba(0,0,0,0.5)"; // 设置阴影颜色 ctx.fillStyle = "#CC0000"; ctx.fillRect(10,10,200,100); ``` **(7)合成** `globalAlpha` 属性设置或返回绘图的当前透明值(alpha 或 transparency)。 `globalAlpha` 属性值必须是介于 0.0(完全透明) 与 1.0(不透明) 之间的数字。 ctx.globalAlpha = number `globalCompositeOperation`属性设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。 源图像 = 您打算放置到画布上的绘图。 目标图像 = 您已经放置在画布上的绘图。 ``` source-over 默认。在目标图像上显示源图像。 source-atop 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 source-in 在目标图像中显示源图像。只有目标图像之内的源图像部分会显示,目标图像是透明的。 source-out 在目标图像之外显示源图像。只有目标图像之外的源图像部分会显示,目标图像是透明的。 destination-over 在源图像上显示目标图像。 destination-atop 在源图像顶部显示目标图像。目标图像位于源图像之外的部分是不可见的。 destination-in 在源图像中显示目标图像。只有源图像之内的目标图像部分会被显示,源图像是透明的。 destination-out 在源图像之外显示目标图像。只有源图像之外的目标图像部分会被显示,源图像是透明的。 lighter 显示源图像 + 目标图像。 copy 显示源图像。忽略目标图像。 xor 使用异或操作对源图像与目标图像进行组合。 ``` **2.2 绘制图像** Canvas API 允许将图像文件插入画布。 我们可以使用Image对象来加载图片,然后绘制: ``` var image = new Image(); image.onload = function(){ ctx.drawImage(image,0,0); }; image.src='new.jpg'; ``` **drawImage方法** ``` drawImage(img,x,y) drawImage(img,x,y,width,height) drawImage(img,sx,sy,switch,sheight,x,y,width,height) ``` 参数: img 规定要使用的图像、画布或视频;sx 可选,开始剪切的 x 坐标位置;sy 可选,开始剪切的 y 坐标位置;swidth 可选,被剪切图像的宽度;sheight 可选,被剪切图像的高度;x 表示在画布上放置图像的 x 坐标位置;y 在画布上放置图像的 y 坐标位置;width 可选,表示要使用的图像的宽度(伸展或缩小图像);height 可选,表示要使用的图像的高度(伸展或缩小图像)。 **图像平铺** ``` createPattern(image,type) ``` 参数: type: no-repeat:不平铺 ;repeat-x:横方向平铺;repeat-y:纵方向平铺;repeat:全方向平铺 **2.3 像素处理** 通过`getImageData`方法和`putImageData`方法,可以处理每个像素,进而操作图像内容。 `getImageData()`方法可以用来读取Canvas的内容,返回一个对象,包含了每个像素的信息。 ``` var imageData = ctx.getImageData(x,y,w,h) ``` 参数:x是canvas的X轴坐标,y是canvas的Y轴坐标,w是宽度,h是高度。 getImageData()方法返回一个像素颜色数组(该数组的长度等于图像的像素宽度*图像的像素高度*4,每个值的范围是0-255,可读写),imageData的属性data就是指向它,顺序是所取像素范围的从左到右,从上到下,数组的元素是(所有图形,包括图片,和绘制的图形)每个像素的rgba : ``` [r1,g1,b1,a1,r2,g2,b2,a2...] ``` `putImageData()`方法可将数组内容重新绘制到Canvas上。 ``` putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight); ``` 参数:imgData 规定要放回画布的 ImageData 对象;x 是ImageData 对象左上角的 x 坐标,以像素计;y 是ImageData 对象左上角的 y 坐标,以像素计;dirtyX 可选,水平值(x),以像素计,在画布上放置图像的位置;dirtyY 可选,垂直值(y),以像素计,在画布上放置图像的位置;dirtyWidth 可选,在画布上绘制图像所使用的宽度;dirtyHeight 可选,在画布上绘制图像所使用的高度。 当然,我们也可以创建一个空白的ImageData对象。 ``` var imgData=context.createImageData(width,height); ``` 也可创建与指定的另一个ImageData对象尺寸相同的新ImageData对象(其不会复制图像数据): ``` var imgData=context.createImageData(imageData); ``` ImageData对象有data属性,它包含 color/alpha 信息的数组。 **2.4 保存与恢复** save()方法用于保存上下文环境,restore()方法用于恢复到上一次保存的上下文环境。 **2.5 保存图像** 我们可以使用toDataURL()方法将Canvas数据重新转化成图像文件形式: ``` canvas.toDataURL('image/png') ``` 上面的代码将Canvas数据,转化成PNG data URI。 **2.6 转换** **(1)scale()** scale() 方法缩放当前绘图至更大或更小。 ``` scale(scalewidth,scaleheight) ``` 参数:scalewidth 缩放当前绘图的宽度;scaleheight 缩放当前绘图的高度。 注意:如果您对绘图进行缩放,所有之后的绘图也会被缩放。定位也会被缩放。如果您 scale(2,2),那么绘图将定位于距离画布左上角两倍远的位置。 **(2)rotate()** rotate() 方法旋转当前的绘图。 ``` rotate(angle) ``` 参数:angle 旋转角度,以弧度计。 注意:旋转只会影响到旋转完成后的绘图。 **(3)translate()** translate() 方法重新映射画布上的 (0,0) 位置。 ``` translate(x,y) ``` 参数:x 添加到水平坐标(x)上的值;y 添加到垂直坐标(y)上的值。 **(4)transform()** ``` transform(a,b,c,d,e,f) ``` 参数:a 水平缩放绘图,b 水平倾斜绘图,c 垂直倾斜绘图,d 垂直缩放绘图,e 水平移动绘图,f 垂直移动绘图。 **(5)setTransform()** setTransform() 方法把当前的变换矩阵重置为单位矩阵,然后以相同的参数运行 transform()。 ``` setTransform(a,b,c,d,e,f) ``` 参数:a 水平缩放绘图,b 水平倾斜绘图,c 垂直倾斜绘图,d 垂直缩放绘图,e 水平移动绘图,f 垂直移动绘图。 注意:该变换只会影响 setTransform() 方法调用之后的绘图。 **2.7 动画** 我们可以使用`setInterval()`和`setTimeout()`来产生动画效果。 还可以使用`requestAnimationFrame()`来制作动画。 requestAnimationFrame()函数是全局函数。 考虑兼容,如下: ``` var requestAnimFrame = function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(a) {window.setTimeout(a, 1e3 / 60, (new Date).getTime())}; }(); ``` 使用方法: ``` function step(){ requestAnimationFrame(step); } ``` requestAnimationFrame()方法会返回一个requestID,是一个长整型非零值,作为一个唯一的标识符,可将该值作为参数传递给window.cancelAnimationFrame()来取消这个函数。 ``` cancelAnimationFrame(requestID); ``` 兼容性代码: ``` var cancelAnimFrame = function() { return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) { clearTimeout(id); }; }(); ```