ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 属性 * width * height > 当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。 > 元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲 > <br /> <br /> ## 渲染上下文 canvas 元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。我们将会将注意力放在2D渲染上下文中。其他种类的上下文也许提供了不同种类的渲染方式;比如, WebGL 使用了基于OpenGL ES的3D上下文 ("experimental-webgl") 。 canvas起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。canvas 元素有一个叫做 [**getContext()**](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext) 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数,上下文的格式。对于2D图像而言,可以使用 [CanvasRenderingContext2D](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D)。 ~~~ var canvas = document.getElementById('tutorial'); var ctx = canvas.getContext('2d'); ~~~ > 通过判断canvas标签是否有getContent属性检测浏览器是否支持canvas ~~~ var canvas = document.getElementById('tutorial'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); // drawing code here } else { // canvas-unsupported code here } ~~~ ## 栅格 canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。所以图中蓝色方形左上角的坐标为距离左边(X轴)x像素,距离上边(Y轴)y像素(坐标为(x,y))。 ![](https://box.kancloud.cn/f8e920127e249e1a4a36aecc8f72627e_220x220.png) <br /> <br /> ## 绘制图形 ### 绘制矩形 HTML中的元素canvas只支持一种原生的图形绘制:矩形。所有其他的图形的绘制都至少需要生成一条路径。 canvas提供了三种方法绘制矩形: ~~~ // 绘制一个填充的矩形 fillRect(x, y, width, height) ~~~ ~~~ // 绘制一个矩形的边框 strokeRect(x, y, width, height) ~~~ ~~~ // 绘制一个矩形的边框 clearRect(x, y, width, height) ~~~ 上面提供的方法之中每一个都包含了相同的参数。 x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。 width和height设置矩形的尺寸。 <br /> ### 绘制路径 图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。 1. 首先,你需要创建路径起始点。 2. 然后你使用画图命令去画出路径。 3. 之后你把路径封闭。 4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。 以下是所要用到的函数: ~~~ // 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径 // 每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形 beginPath() ~~~ ~~~ // 闭合路径之后图形绘制命令又重新指向到上下文中 // 这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。 closePath() ~~~ ~~~ // 通过线条来绘制图形轮廓 stroke() ~~~ ~~~ // 通过填充路径的内容区域生成实心的图形 fill() ~~~ > 当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。 > ~~~ // 将笔触移动到指定的坐标x以及y上 moveTo(x, y) ~~~ ~~~ // 绘制一条从当前位置到指定x以及y位置的直线 lineTo(x, y) ~~~ <br /> ### 圆弧 ~~~ // 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成 // startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise为一个布尔值。为true时,是逆时针方向,否则顺时针方向。 arc(x, y, radius, startAngle, endAngle, anticlockwise) ~~~ > arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。 ~~~ // 根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点 arcTo(x1, y1, x2, y2, radius) ~~~ <br /> ### 二次贝塞尔曲线及三次贝塞尔曲线 ~~~ // 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点 quadraticCurveTo(cp1x, cp1y, x, y) ~~~ ~~~ // 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) ~~~ >参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。 二次贝塞尔曲线有一个开始点(蓝色)、一个结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线有两个控制点。 ![](https://box.kancloud.cn/aba4dfabdd5eb97e364718f66473e4ff_190x190.png) <br /> ### 矩形 ~~~ // 绘制一个左上角坐标为(x,y),宽高为width以及height的矩形 rect(x, y, width, height) ~~~ 当该方法执行的时候,moveTo()方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。 ## 样式和颜色 ### 颜色 ~~~ // 设置图形的填充颜色 fillStyle = color // 设置图形轮廓的颜色 strokeStyle = color ~~~ > color 可以是表示 CSS 颜色值的字符串,渐变对象或者图案对象。 > <br /> ### 透明度 ~~~ // 这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。 globalAlpha = transparencyValue ~~~ <br> ### 线型 ~~~ // 设置线条宽度。 // 属性值必须为正数。默认值是1.0。 lineWidth = value ~~~ ~~~ // 设置线条末端样式。 // 可以为下面的三种的其中之一:butt,round 和 square。默认是 butt。 lineCap = type ~~~ ![](https://box.kancloud.cn/26c43fb3da38101df375bd134cdf6d0f_190x190.png) ~~~ // 设定线条与线条间接合处的样式。 // 值可以是round, bevel 和 miter。默认是 miter。 lineJoin = type ~~~ ![](https://box.kancloud.cn/55b7f2cb125bc6c75fb43a4fdab2adb6_190x190.png) ~~~ // 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。 miterLimit = value ~~~ ![](https://box.kancloud.cn/90a6a37120a50eedf3a31ec2ec6557b9_190x190.png) ~~~ // 返回一个包含当前虚线样式,长度为非负偶数的数组。 getLineDash() ~~~ ~~~ // 设置当前虚线样式。 setLineDash(segments) ~~~ ~~~ // 设置虚线样式的起始偏移量。 lineDashOffset = value ~~~ ### 渐变 ~~~ // 绘制线性渐变 // 参数表示渐变的起点 (x1,y1) 与终点 (x2,y2)。 createLinearGradient(x1, y1, x2, y2) ~~~ ~~~ // 绘制径向渐变 // 前三个参数定义一个以 (x1,y1) 为原点,半径为 r1 的圆 // 后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。 createRadialGradient(x1, y1, r1, x2, y2, r2) ~~~ <br> 创建出 canvasGradient 对象后,我们就可以用 addColorStop 方法给它上色了。 <br> ~~~ // position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间 // color 参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等) gradient.addColorStop(position, color) ~~~ <br> 例子 ~~~ function draw() { var ctx = document.getElementById('canvas').getContext('2d'); // Create gradients var lingrad = ctx.createLinearGradient(0,0,0,150); lingrad.addColorStop(0, '#00ABEB'); lingrad.addColorStop(0.5, '#fff'); lingrad.addColorStop(0.5, '#26C000'); lingrad.addColorStop(1, '#fff'); // assign gradients to fill and stroke styles ctx.fillStyle = lingrad; // draw shapes ctx.fillRect(10,10,130,130); } ~~~ <br> ### 图案样式 ~~~ // 指定的方向内重复指定的元素 // Image 可以是一个 Image 对象的引用,或者另一个 canvas 对象 // Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat createPattern(image, type) ~~~ > 与 drawImage 有点不同,你需要确认 image 对象已经装载完毕,否则图案可能效果不对的。 > ~~~ function draw() { var ctx = document.getElementById('canvas').getContext('2d'); // 创建新 image 对象,用作图案 var img = new Image(); img.src = 'images/wallpaper.png'; img.onload = function(){ // 创建图案 var ptrn = ctx.createPattern(img,'repeat'); ctx.fillStyle = ptrn; ctx.fillRect(0,0,150,150); } } ~~~ <br> ### 阴影 ~~~ // 设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0 shadowOffsetX = float shadowOffsetY = float // 设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0 shadowBlur = float // 用于设定阴影颜色效果,默认是全透明的黑色 shadowColor = color ~~~ 例子 ~~~ function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2; ctx.shadowBlur = 2; ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; ctx.font = "20px Times New Roman"; ctx.fillStyle = "Black"; ctx.fillText("Sample String", 5, 30); } ~~~ <br> ### Canvas 填充规则 当我们用到 fill(或者 clip和isPointinPath )你可以选择一个填充规则,该填充规则根据某处在路径的外面或者里面来决定该处是否被填充,这对于自己与自己路径相交或者路径被嵌套的时候是有用的。 两个可能的值: * "nonzero": non-zero winding rule, 默认值. * "evenodd": even-odd winding rule. <br> <br> ## 文本 ### 填充、绘制文本 ~~~ // 在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的 fillText(text, x, y [, maxWidth]) // 在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的 strokeText(text, x, y [, maxWidth]) ~~~ <br> ### 文本样式 ~~~ // 当前我们用来绘制文本的样式 // 这个字符串使用和 CSS font 属性相同的语法. 默认的字体是 10px sans-serif font = value // 文本对齐选项 // 可选的值包括:start, end, left, right or center. 默认值是 start textAlign = value // 基线对齐选项 // 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic textBaseline = value // 文本方向 // 可能的值包括:ltr, rtl, inherit。默认值是 inherit direction = value ~~~ <br> <br> ## 图像 ### 获得需要绘制的图片 canvas的API可以使用下面这些类型中的一种作为图片的源: * HTMLImageElement 这些图片是由Image()函数构造出来的,或者任何的 img元素 * HTMLVideoElement 用一个HTML的 video元素作为你的图片源,可以从视频中抓取当前帧作为一个图像 * HTMLCanvasElement 可以使用另一个 canvas 元素作为你的图片源。 * ImageBitmap 这是一个高性能的位图,可以低延迟地绘制,它可以从上述的所有源以及其它几种源中生成 > 这些源统一由 CanvasImageSource类型来引用。 > <br> ### 使用其他域名的图片 在 HTMLImageElement上使用crossOrigin属性,你可以请求加载其它域名上的图片。如果图片的服务器允许跨域访问这个图片,那么你可以使用这个图片而不污染canvas,否则,使用这个图片将会污染canvas。 这时不能再调用**toBlob()**, **toDataURL()**和**getImageData()**等方法 <br> 绘制图片 ~~~ // 其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标 drawImage(image, x, y) ~~~ 缩放图片 ~~~ // width 和 height,这两个参数用来控制 当向canvas画入时应该缩放的大小 drawImage(image, x, y, width, height) ~~~ 切片 ~~~ // 第一个参数是一个图像或者另一个 canvas 的引用。 // 前4个是定义图像源的切片位置和大小 // 后4个则是定义切片的目标显示位置和大小 drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) ~~~ ![](https://box.kancloud.cn/d38ed231d8a698184086a14d110b405b_300x290.png) <br> <br> ## 变形 ### 状态的保存和恢复 **save** 和 **restore** 方法是用来保存和恢复 canvas 状态的,都没有参数。Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。 Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。一个绘画状态包括: * 当前应用的变形(即移动,旋转和缩放) * strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值 * 当前的裁切路径(clipping path) 你可以调用任意多次 save 方法。 每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。 ### 移动 ~~~ // 用来移动 canvas 和它的原点到一个不同的位置 // x 是左右偏移量,y 是上下偏移量 translate(x, y) ~~~ ![](https://box.kancloud.cn/c4ccdfa50cff06e29d6eb3f7f8358f8f_220x220.png) > 在做变形之前先保存状态是一个良好的习惯。 > <br> ### 旋转 ~~~ // 用于以原点为中心旋转 canvas // 参数表示旋转的角度(angle),它是顺时针方向的,以弧度为单位的值 rotate(angle) ~~~ ![](https://box.kancloud.cn/f1f4f1119a9d594bcf8baf500918fcaa_220x220.png) > 旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到 translate 方法 > <br> ### 缩放 增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。 ~~~ // x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。 scale(x, y) ~~~ 默认情况下,canvas 的 1 单位就是 1 个像素。举例说,如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。 <br> <br> ### 变形 将当前的变形矩阵乘上一个基于自身参数的矩阵 ~~~ transform(m11, m12, m21, m22, dx, dy) ~~~ 矩阵如下 ~~~ m11 m21 dx m12 m22 dy 0 0 1 ~~~ 这个函数的参数各自代表如下: * m11:水平方向的缩放 * m12:水平方向的倾斜偏移 * m21:竖直方向的倾斜偏移 * m22:竖直方向的缩放 * dx:水平方向的移动 * dy:竖直方向的移动 <br> ~~~ setTransform(m11, m12, m21, m22, dx, dy) ~~~ 这个方法会将当前的变形矩阵重置为单位矩阵,然后用相同的参数调用 transform 方法。如果任意一个参数是无限大,那么变形矩阵也必须被标记为无限大,否则会抛出异常。从根本上来说,该方法是取消了当前变形,然后设置为指定的变形,一步完成。 <br> 重置当前变形为单位矩阵 ~~~ resetTransform() ~~~ 它和调用以下语句是一样的: ~~~ ctx.setTransform(1, 0, 0, 1, 0, 0); ~~~ ## 组合 ### globalCompositeOperation 这个属性设定了在画新图形时采用的遮盖策略,其值是一个标识12种遮盖方式的字符串。 * source-over 默认。在目标图像上显示源图像。 * source-atop 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 * source-in 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 * source-out 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。 * destination-over 在源图像上方显示目标图像。 * destination-atop 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。 * destination-in 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。 * destination-out 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 * lighter 显示源图像 + 目标图像。 * copy 显示源图像。忽略目标图像。 * xor 使用异或操作对源图像与目标图像进行组合。 ![](https://box.kancloud.cn/afc94111de69ee3a3fc7a9d1a0d8230c_1186x270.png) <br> ### 裁剪路径 将当前正在构建的路径转换为当前剪切路径 ~~~ clip() ~~~ 默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果)。 > 一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。 > <br> 例子 ~~~ function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.fillRect(0,0,150,150); ctx.translate(75,75); // Create a circular clipping path ctx.beginPath(); ctx.arc(0,0,60,0,Math.PI*2,true); ctx.clip(); // draw background var lingrad = ctx.createLinearGradient(0,-75,0,75); lingrad.addColorStop(0, '#232256'); lingrad.addColorStop(1, '#143778'); ctx.fillStyle = lingrad; ctx.fillRect(-75,-75,150,150); // draw stars for (var j=1;j<50;j++){ ctx.save(); ctx.fillStyle = '#fff'; ctx.translate(75-Math.floor(Math.random()*150), 75-Math.floor(Math.random()*150)); drawStar(ctx,Math.floor(Math.random()*4)+2); ctx.restore(); } } function drawStar(ctx,r){ ctx.save(); ctx.beginPath() ctx.moveTo(r,0); for (var i=0;i<9;i++){ ctx.rotate(Math.PI/5); if(i%2 == 0) { ctx.lineTo((r/0.525731)*0.200811,0); } else { ctx.lineTo(r,0); } } ctx.closePath(); ctx.fill(); ctx.restore(); } ~~~ ![](https://box.kancloud.cn/960447d3c1389119fd08aafed84602cf_190x190.png) <br> <br> ## 基本的动画 ### 动画的基本步骤 通过以下的步骤来画出一帧: * 清空 canvas 除非接下来要画的内容会完全充满 canvas (例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。 * 保存 canvas 状态 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。 * 绘制动画图形 这一步才是重绘动画帧。 * 恢复 canvas 状态 如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧 <br> ### 操控动画 通过 **setInterval** 、 **setTimeout** 和 **requestAnimationFrame** 方法来控制在设定的时间点上执行重绘。 如果我们需要做一个游戏,我们可以使用键盘或者鼠标事件配合上setTimeout()方法来实现。通过设置事件监听,我们可以捕捉用户的交互,并执行相应的动作。 <br> <br> ## 像素操作 ### ImageData对象 ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性: * width: 图片宽度,单位是像素 * height: 图片高度,单位是像素 * data: Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255) data属性返回一个 Uint8ClampedArray,它可以被使用作为查看初始像素数据。每个像素用4个1bytes值(按照红,绿,蓝和透明值的顺序; 这就是"RGBA"格式) 来代表。每个颜色值部份用0至255来代表。每个部份被分配到一个在数组内连续的索引,左上角像素的红色部份在数组的索引0位置。像素从左到右被处理,然后往下,遍历整个数组。 Uint8ClampedArray 包含**高度 × 宽度 × 4** bytes数据,索引值从0到(高度×宽度×4)-1 <br> ### 创建一个ImageData对象 创建了一个新的具体特定尺寸的ImageData对象。所有像素被预设为透明黑。 ~~~ var myImageData = ctx.createImageData(width, height); ~~~ 创建一个被anotherImageData对象指定的相同像素的ImageData对象。这个新的对象像素全部被预设为透明黑。这个并非复制了图片数据。 ~~~ var myImageData = ctx.createImageData(anotherImageData); ~~~ <br> ### 得到场景像素数据 这个方法会返回一个ImageData对象,它代表了画布区域的对象数据,此画布的四个角落分别表示为(left, top), (left + width, top), (left, top + height), 以及(left + width, top + height)四个点。这些坐标点被设定为画布坐标空间元素。 ~~~ var myImageData = ctx.getImageData(left, top, width, height); ~~~ > 任何在画布以外的元素都会被返回成一个透明黑的ImageData对像。 > <br> **例子:颜色选择器** 我们会使用getImageData()去展示鼠标光标下的颜色。为此,我们要当前鼠标的位置,记为layerX和layerY,然后我们去查询getImageData()给我们提供的在那个位置的像数数组里面的像素数据。最后我们使用数组数据去设置背景颜色和<div>的文字去展示颜色值。 ~~~ var img = new Image(); img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); img.onload = function() { ctx.drawImage(img, 0, 0); img.style.display = 'none'; }; var color = document.getElementById('color'); function pick(event) { var x = event.layerX; var y = event.layerY; var pixel = ctx.getImageData(x, y, 1, 1); var data = pixel.data; var rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + (data[3] / 255) + ')'; color.style.background = rgba; color.textContent = rgba; } canvas.addEventListener('mousemove', pick); ~~~ <br> ### 在场景中写入像素数据 ~~~ ctx.putImageData(myImageData, dx, dy); ~~~ dx 和 dy参数表示你希望在场景内左上角绘制的像素数据所得到的设备坐标。 **例子:图片灰度和反相颜色** 在这个例子里,我们遍历所有像素以改变他们的数值。然后我们将被修改的像素数组通过putImageData()放回到画布中去。invert函数仅仅是去减掉颜色的最大色值255.grayscale函数仅仅是用红绿和蓝的平均值。你也可以用加权平均,例如x = 0.299r + 0.587g + 0.114b这个公式。[更多资料请参考维基百科的Grayscale](https://en.wikipedia.org/wiki/Grayscale)。 ~~~ var img = new Image(); img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; img.onload = function() { draw(this); }; function draw(img) { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); img.style.display = 'none'; var imageData = ctx.getImageData(0,0,canvas.width, canvas.height); var data = imageData.data; var invert = function() { for (var i = 0; i < data.length; i += 4) { data[i] = 225 - data[i]; // red data[i + 1] = 225 - data[i + 1]; // green data[i + 2] = 225 - data[i + 2]; // blue } ctx.putImageData(imageData, 0, 0); }; var grayscale = function() { for (var i = 0; i < data.length; i += 4) { var avg = (data[i] + data[i +1] + data[i +2]) / 3; data[i] = avg; // red data[i + 1] = avg; // green data[i + 2] = avg; // blue } ctx.putImageData(imageData, 0, 0); }; var invertbtn = document.getElementById('invertbtn'); invertbtn.addEventListener('click', invert); var grayscalebtn = document.getElementById('grayscalebtn'); grayscalebtn.addEventListener('click', grayscale); } ~~~ <br> ### 缩放和反锯齿 我们得到鼠标的位置并裁剪出距左和上5像素,距右和下5像素的图片。然后我们将这幅图复制到另一个画布然后将图片调整到我们想要的大小。在缩放画布里,我们将10×10像素的对原画布的裁剪调整为 200×200 。 ~~~ zoomctx.drawImage(canvas, Math.abs(x - 5), Math.abs(y - 5), 10, 10, 0, 0, 200, 200); ~~~ <br> CanvasRenderingContext2D.imageSmoothingEnabled 是 Canvas 2D API 用来设置图片是否平滑的属性,true表示图片平滑(默认值),false表示图片不平滑。当我们获取 imageSmoothingEnabled 属性值时, 它会返回最新设置的值。 ~~~ // 一个Boolean 类型的值,表示图片是否平滑。 ctx.imageSmoothingEnabled = value; ~~~ 以缩放画布为例,这个属性对像素为主的游戏很有用。默认的改变大小的算法会造成图片模糊并且破坏图片原有的像素。 如果那样的话,设置属性值为false。 **例子** ~~~ <canvas id="canvas" width="300" height="227"></canvas> <canvas id="zoom" width="300" height="227"></canvas> <div> <label for="smoothbtn">   <input type="checkbox" name="smoothbtn" checked="checked" id="smoothbtn">   Enable image smoothing </label> </div> var img = new Image(); img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg'; img.onload = function() { draw(this); }; function draw(img) { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); img.style.display = 'none'; var zoomctx = document.getElementById('zoom').getContext('2d'); var smoothbtn = document.getElementById('smoothbtn'); var toggleSmoothing = function(event) { zoomctx.imageSmoothingEnabled = this.checked; zoomctx.mozImageSmoothingEnabled = this.checked; zoomctx.webkitImageSmoothingEnabled = this.checked; zoomctx.msImageSmoothingEnabled = this.checked; }; smoothbtn.addEventListener('change', toggleSmoothing); var zoom = function(event) { var x = event.layerX; var y = event.layerY; zoomctx.drawImage(canvas, Math.abs(x - 5), Math.abs(y - 5), 10, 10, 0, 0, 200, 200); }; canvas.addEventListener('mousemove', zoom); } ~~~ <br> ### 保存图片 当你从画布中生成了一个数据链接,例如,你可以将它用于任何<image>元素,或者将它放在一个有download属性的超链接里用于保存到本地 ~~~ // 默认设定。创建一个PNG图片 canvas.toDataURL('image/png') // 创建一个JPG图片。你可以有选择地提供从0到1的品质量,1表示最好品质,0基本不被辨析但有比较小的文件大小 canvas.toDataURL('image/jpeg', quality) ~~~ 你也可以从画布中创建一个Blob对象。 ~~~ // 这个创建了一个在画布中的代表图片的Blob对象 canvas.toBlob(callback, type, encoderOptions) ~~~