canvas 基础

浏览: 280 发布日期: 2016-12-14 分类: html5

本文基本上可以用来当做 canvas 的一个基本参考。基本涵盖了所有的 canvas 内容,当然,不包括使用 canvas 来处理的高级应用。

线

基本的有:

画线

基本的架构为:

ctx.beginPath(); // 开始画线,里面没有任何参数
ctx.moveTo(x,y); // 定义起始点
ctx.lineTo(x,y); // 定义过程点
// 还可以定义线宽,线的颜色
ctx.stroke();  // 开始划线,里面没任何参数

line

// 简单就是 
ctx.moveTo(x,y)
ctx.lineTo(x,y)

lineWidth

控制线宽

ctx.lineWidth = 20; // 单位默认为 px

lineColor

线颜色定义可以使用:

ctx.strokeStyle = "#fff";

lineCap

定义线两端的格式为:

context.lineCap = 'butt';

该属性有 3 个取值:butt,round,square。分别为:

lineJoin

用来描述,多个路径之间的连接方式。基本取值有:round,bevel,miter。

context.beginPath();
      context.moveTo(379, 150);
      context.lineTo(429, 50);
      context.lineTo(479, 150);
      context.lineJoin = 'bevel';
      context.stroke();

详情为:

曲线

曲线和线段
基本区现有: 弧线,二次曲线,贝塞尔曲线。

arcTo

基本格式为:

ctx.arcTo(cx1, cy1, x2, y2, radius);

同样,使用 moveTo 或者 lineTo 确定第一个起始点。

    context.beginPath();
    context.moveTo(100, 225);             // P0
    context.arcTo(228, 40, 530, 70, 89); // P1, P2 and the radius
    context.lineTo(530, 70);             // P2 
    context.stroke();

上面,P2 点用到了 lineTo。 这有什么影响吗?有的。
如果没定义 lineTo,圆弧可能并不会过到 P2 点,因为圆弧实际的算法为:

它只会确定最终圆弧的范围的大小,并不会关注 P2 点是否连接。如果没定义 lineTo 的话,结果为:

Quadratic Curve

该是用来画二次曲线的:

ctx.quadraticCurveTo(cpx, cpy, x, y);

他通常结合 moveTo 来找到 3 个点,确定二次函数。

ctx.beginPath();
ctx.moveTo(50,20);  // x 轴上的点 (50,20)
ctx.quadraticCurveTo(230, 30, 50, 100); // 控制点为 (230,30)。
// 另外 x 轴上的点为 (50,100)
ctx.stroke();

bezier Curve

该 tag 是用来画贝塞尔曲线的,即通过定义 4 个点,即可确定,线的形状,具体格式为:

// 这里定义了两个控制点,一个基准点
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

上面说到 4 个点,还有一个点是通过 moveTo 来定义的。基本格式为:

ctx.beginPath();
ctx.moveTo(50,20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.stroke();

如图:

实际计算方法是取中点,然后取过中点的切线。

图形

基本的简单图形有:,圆,椭圆,自定义图形.
图形方面通常是结合,ctx.fill() 来进行触发渲染的操作。

rect()

该API 用来在 canvas 上画一个。

// 基本格式为
ctx.rect(x, y, width, height);
ctx.fill();

// 或者使用两者的结合属性
ctx.fillRect(x, y, width, height);

看 API 应该很容易就知道,这个是用来干啥的了。
rect 默认颜色是 black。当然,你也可以通过使用 fillStyle 来显示的改变颜色。

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100);

矩形框

上面那种形式,是用来画图形内容,接着,我们可以使用 strokeRect() 来画一个矩形边框使用。
基本格式为:

ctx.strokeRect(x, y, width, height);

实际画法。

ctx.strokeStyle = "green";
ctx.strokeRect(10, 10, 100, 100);

这里,直接使用 stroke 即可,不需要在显示触发渲染啥的了。画边框,当然可以使用 line 相关的属性,比如,定义线宽。

ctx.lineWidth = 5;

实际上,结合 rect 也可以来画一个矩形框:

      context.beginPath();
      context.rect(188, 50, 200, 100);
      context.fillStyle = 'yellow';
      context.fill();
      context.lineWidth = 7;
      context.strokeStyle = 'black';
      context.stroke();  // 触发画边框的效果

arc

基本格式为:

// 默认为逆时针
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

startAngle 是从 x 轴正方向,顺时针计算角度。
详细demo:

ctx.arc(75, 75, 50, 0, 2 * Math.PI);

// 顺时针画圆
ctx.arc(75, 75, 50, 0, 2 * Math.PI,false);

比如,另外画一个半圆。

      context.beginPath();
      context.arc(288, 75, 70, 0, Math.PI, false);
      context.closePath();  // 封闭图形
      context.lineWidth = 5;
      context.fillStyle = 'red';
      context.fill();
      context.strokeStyle = '#550000';
      context.stroke();

ellipse*

这个 API 是最近提出来的,比较新。所以,兼容性需要考虑。基本格式为:

// x,y 确定长轴,短轴的位置
// rotation 按照 x 轴正方向,按照 anticlockwise 的设置进行旋转,也是弧度制
// startAngle,endAngle 也是按照 x 轴正方向来的
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);

他的角度表示都是 弧度制(radians)。
看个实例:

ctx.beginPath();
ctx.ellipse(100, 100, 50, 75, 45 * Math.PI/180, 0, 2 * Math.PI);
ctx.stroke();

图为:

当然,你也可以使用 fill 等,来填充相关颜色。

自定义图形

如果你想画一个自定义图形的话,需要结合 line 相关的标签。最后使用 closePath() 来显示封闭图形。
closePath API 的实际作用是: 当你当前的点已经和起始点重合,那么 do nothing。否则,将当前点和起始点用直线连接起来,构成封闭图形,这样才可能使用 fill 相关来进行填充。

      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');

      // begin custom shape
      context.beginPath();
      context.moveTo(170, 80);
      context.bezierCurveTo(130, 100, 130, 150, 230, 150);
      context.bezierCurveTo(250, 180, 320, 180, 340, 150);
      context.bezierCurveTo(420, 150, 420, 120, 390, 100);
      context.bezierCurveTo(430, 40, 370, 30, 340, 50);
      context.bezierCurveTo(320, 5, 250, 20, 250, 50);
      context.bezierCurveTo(200, 5, 150, 20, 170, 80);

      // complete custom shape
      context.closePath();
      context.lineWidth = 5;
      context.strokeStyle = 'blue';
      context.stroke();

最后一定要记得使用 closePath() 这样,才能达到完整图形的效果。

填充

关于填充有:基本颜色填充,渐变填充,图片填充。基本的颜色填充会涉及到两个,一个是 fillStyle,还有一个是 strokeStyle。两个的基本形似是一模一样的:

// 填充基本颜色值,比如 #fff
ctx.fillStyle = color;
// 填充渐变值,比如 createLinear 创建的渐变等
ctx.fillStyle = gradient;
// 通常用来贴图用的值
ctx.fillStyle = pattern;

颜色填充

这个就不过说了,就是填 RGB 值。

ctx.fillStyle = "blue";

渐变色填充

渐变色有两种,一个是线性渐变:createLinearGradient(),一个是中心渐变:createRadialGradient()。 他们可以使用一个共同的 API : addColorStop()。来设置间隔色。基本格式为:

addColorStop(offset, color);
  • offset: 为 [0,1] 之间的数

  • color: rgb 的值

var gradient = ctx.createLinearGradient(0,0,200,0);
gradient.addColorStop(0,"green");
gradient.addColorStop(1,"white");
ctx.fillStyle = gradient;
ctx.fillRect(10,10,200,100);

createLinearGradient()

线性渐变的内容是:

ctx.createLinearGradient(x0, y0, x1, y1);

两个点确定一条直线,然后将颜色按照这个线段进行渐变。

实例:

代码为:

gradient.addColorStop(0,'red');
gradient.addColorStop(1,'black');
ctx.fillStyle=gradient;
ctx.fillRect(0,0,320,320);

createRadialGradient()

中心渐变内容为:

ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);

以及上就是两个圆,然后按照顺序,将颜色渐变,当然中间也可以添加多段。

实例:

代码为:

        let gradient = ctx.createRadialGradient(200,200,0,200,200,160);
        gradient.addColorStop(0,'red');
        gradient.addColorStop(1,'white');
        ctx.fillStyle=gradient;
        ctx.arc(200,200,160,0,2*Math.PI);
        ctx.fill();

pattern - 贴图

这一块应该算是 canvas 牛逼的地方,能够和图形相关的元素结合起来的关键点。格式为:

ctx.createPattern(image, repetition);
  • image: 该类型的取值有很多,比如 image,video,cavnas,imageData,blob 等。

    • HTMLImageElement (< img>)

    • HTMLVideoElement (< video>)

    • HTMLCanvasElement (< canvas>)

    • CanvasRenderingContext2D

    • ImageBitmap

    • ImageData

    • Blob

  • repetition:该取值内容就很简单,相当于 background 一样,用来设置图片的重复形式。

    • repeat (默认值)

    • repeat-x

    • repeat-y

    • no-repeat

这里就举一个贴图的列子:

// FROM MDN
var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
img.onload = function() {
  var pattern = ctx.createPattern(img, 'repeat');
  ctx.fillStyle = pattern;
  ctx.fillRect(0,0,400,400);
};

图片处理

图片处理相关的 API 有很多,这里先说最基本的,drawImage()。它有 3 种基本形式:

void ctx.drawImage(image, dx, dy);
void ctx.drawImage(image, dx, dy, dWidth, dHeight);
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

分别说一下:

简单绘制

void ctx.drawImage(image, dx, dy);

简单的 3 个参数,就用来确定图片在 canvas 上的位置,不进行任何缩放。

缩放绘制

基本格式为:

void ctx.drawImage(image, dx, dy, dWidth, dHeight);

通过, dWidth 和 dHeight 来确定在 canvas 中绘制的大小,可以放大和缩小。

代码为:

let img = new Image();
img.onload = function(){
        ctx.drawImage(img,20,20,150,100);
}
img.src = "...";

裁剪绘制

基本格式为:

void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

其中,sx,sy,sWidth,sHeight 用来确定在原来图片上,截取图像的区域。

具体代码为:

let img = new Image();
img.onload = function(){
        ctx.drawImage(img,20,20,60,95,60,60,100,200);
}
img.src = "...";

字体处理

在 canvas 里,还有显示文字的一个 trick 。文字方面的话,没什么特别的就是基本的 style, szie ,color 等。 一般结合 fillText/stokeText API 一起使用,进行绘制。这里两个 API 的区别也很普通,就是简单的 填充实心字体 和 边界字体。

context.font = 'italic 40pt Calibri';
context.fillText('Hello World!', 150, 100);

先简单说一下,这两个 API。他们的格式,基本上是一样的:

ctx.strokeText(text, x, y [, maxWidth]);
  • x,y 用来确定 text 的左下角起始点。很重要,是左下角!

  • maxWidth: 用来确定 text 渲染的宽度,如果字体大了,则会自动缩小,不会换行。

代码为:

ctx.font="20px serif";
ctx.strokeText("I dont Know how to Do",20,20,200);

字体-font

定义一个最简单的字体,使用的是:

ctx.font=value;

value 的值,就是一般的 css font 属性的值。默认为: 10px sans-serif

ctx.font = "48px serif";

这里,还可以使用比较新的 API FontFace() 来使用在线字体:

var f = new FontFace("font-name", "url(x)");
  f.load().then(function() {
    ctx.font="20px font-name";
    ctx.fillText("ABC",100,100);
});

字体颜色

字体的颜色相关和上面图形一样,同样使用的是 fillStylestrokeStyle 这里就不赘述了。

字体边框粗细

定义粗细的话,同样使用 lineWidth 即可。

字体的排列

基本格式为:

// 默认为 start
ctx.textAlign = "left" || "right" || "center" || "start" || "end";

这里,并不是用来定义字体在 canvas 中的排列位置,而是用来定义,基准点相对于字体的位置。
常用的就是居中布局:

代码为:

ctx.font="20px serif";
ctx.textAlign='center';
ctx.strokeText("I dont Know how to Do",200,200);

其余的取值,比如 left,right 都是相对于该点进行绘制的。
比如,取 right/end:

基线位置

基本格式为:

// 默认值为: ideographic
ctx.textBaseline = "top" || "hanging" || "middle" || "alphabetic" || "ideographic" || "bottom";

该属性和 baseline 差不多,是用来确定基线在字体的哪一个位置。 在定位的时候,线是不动的,动的是字。
详情参考:

测量字体

如果想知道当前字体的宽度,可以使用 measureText API 来完成。基本使用也很简单:

var text = ctx.measureText("foo"); // TextMetrics object
text.width; // 16;

当然,这个属性返回的对象上面,还挂载了很多其他测量值,不过兼容性比较差。关键点在于,可以结合该 API 来画出分行的字体内容。简单的说来就是通过将字符串拆分,判断渲染字符串是否超过本行的宽度,进而决定是否将标识点 y 轴值加 lineheight。
简单看个算法:

function wrapText(context, text, x, y, maxWidth, lineHeight) {
        // 通过使用 ' ' 进行字符的拆分 (这里就不针对中文了)
        var words = text.split(' ');
        var line = '';

        for(var n = 0; n < words.length; n++) {
         // 判断行
          var testLine = line + words[n] + ' ';
          // 测量渲染的宽度
          var metrics = context.measureText(testLine);
          var testWidth = metrics.width;
          if (testWidth > maxWidth && n > 0) {
           // 超过,则下一该行
            context.fillText(line, x, y);
            line = words[n] + ' ';
            y += lineHeight;
          }
          else {
            line = testLine;
          }
        }
        context.fillText(line, x, y);
      }
      
      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');
      var maxWidth = 400;
      var lineHeight = 25;
      var x = (canvas.width - maxWidth) / 2;
      var y = 60;
      var text = 'All the world \'s a stage, and all the men and women merely players. They have their exits and their entrances; And one man in his time plays many parts.';

      context.font = '16pt Calibri';
      context.fillStyle = '#333';

      wrapText(context, text, x, y, maxWidth, lineHeight);

canvas 中的变换

关于变化,最基本的就是 translate, scale,skew 等。在 canvas 中,这些就是针对于 canvas 坐标系来的。

translate

这是用来进行原点平移的变化。基本格式为:

ctx.translate(x, y);
  • x: 当前坐标系在 x 轴向的移动方向

  • y:当前坐标在 y 轴向的移动方向

当然,还有对应的 2D 变换矩阵,这和 transform 属性一样。可以使用下列的变换:

ctx.setTransform(1, 0, 0, 1, x, y);

rotate

这是用来旋转坐标系的。基本格式为:

// 里面的参数是弧度,可以使用 Math.PI 来进行转换
ctx.rotate(angle);

例如:

ctx.rotate(45 * Math.PI / 180);
ctx.fillRect(70,0,100,30);

当在旋转时,在矩阵中是根据 sin 函数来表示的旋转角度的值。

ctx.setTransform(cosθ,sinθ,-sinθ,cosθ,0,0) // 就是 cs-sc

scale

缩放坐标系,基本格式为:

ctx.scale(x, y);

它表达的意思是:

  • x: 将 x 轴放大/缩小 x 倍。即,设置的像素值会乘以该 x 值.

  • y: 将 y 轴放大/缩小 y 倍。即,设置的像素值会乘以该 y 值.

看个 demo:

ctx.scale(10, 3);
ctx.fillRect(10,10,10,10);
// 最后的结果就是,在 (100,30) 点,画出 width: 100,height: 30 的矩形。

另外,你还可以利用这个属性作颠倒:

ctx.scale(-1, 1); // x 轴对称
ctx.font = "48px serif";
ctx.fillText("Hello world!", -320, 120); // x 轴的值需要设为负数
ctx.setTransform(1, 0, 0, 1, 0, 0); // 还原坐标

它对应于矩阵的表达就是:

setTransform(A, 0, 0, B, 0,0);
// X 轴放大 A 倍
// Y 轴放大 B 倍

矩阵变换

矩阵变换的 API 和 css3 动画中的没啥区别:

ctx.setTransform(a, b, c, d, e, f);

最常使用的是用来进行坐标还原。因为,它每一次变换都是覆盖掉上一次变化,所以,还原坐标常使用:

ctx.setTransform(1,0,0,1,0,0);

不过,除了这个方法外,其他变换都是基于已经变化后的坐标来变换的。

transform

该 API 和 setTransform 有些不同。setTransform 相当于重置,而 transform 会基于前一个变换结果,接着进行变换。它的使用方式和 setTransform 差不多。

ctx.transform(a, b, c, d, e, f);

resetTransform

相当于就是 setTransform(1,0,0,1,0,0)的封装。

ctx.resetTransform();

state stack

在 canvas 里面,因为有时候操作比较多,可能会造成来回变换坐标。这时候,就可以使用 canvas 里面的状态管理。save & restore,这两个方法相当于 stack 的 push & pop 方法。一个入栈,一个出栈。那么,这些状态会保存什么呢?

  • 变形操作,基本的移动,缩放,旋转,矩阵变换等。

  • 裁剪区域

  • dash list

  • 以及相关的笔触,填充状态。比如: strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin。

不过,它仅仅只是作为一个状态进行保存的。当结合 restore 一起,才会发挥它应该有的效果。

ctx.save();
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100);
ctx.restore();
// 恢复原始的 fillStyle 内容
ctx.fillRect(150, 75, 100, 100);

composite

在 canvas 中的层合并,涉及到阴影,裁剪等效果。

阴影特效

关于 canvas 中的阴影涉及到 4 个API。

  • shadowBlur

  • shadowColor

  • shadowOffsetX

  • shadowOffsetY

分别介绍一下:

shadowBlur

这是用来添加阴影的,基本格式为:

ctx.shadowBlur = level;

level 的值默认为 0,表示不存在阴影。并且,不能取 Negative, Infinity or NaN。level 表示的意义只在于,规定阴影模糊的范围而已。需要注意,一旦你设置了 shadoeBlur,那么接下来,在 canvas 上画的所有元素,都会带上阴影的效果,即使是透明元素。所以,一般情况下可以手动回撤:

ctx.shadowBlur = 0;

或者,结合 save 和 restore 来进行状态回撤。

ctx.save();
ctx.shadowBlur = 5;
ctx.fillRect(0,0,400,400);
ctx.restore();

shadowColor

用来设置阴影的颜色值,默认是不透明的黑色。

基本格式为:

// color 默认为 “black”
ctx.shadowColor = color;

shadowOffsetX/Y

这个实际上和 box-shadow 设置的阴影效果是一样的,用来定义阴影相对于原始图形的偏移量。基本格式为:

// offset 默认值为 0,相当于取 canvas 上的像素值
// 可以为负值,但不能取 Infinity or NaN
ctx.shadowOffsetX = offset;

实例为:

// 在 x 轴上移动阴影
ctx.shadowOffsetX = 10;
// 在 Y 轴上移动阴影
ctx.shadowOffsetY = 10;

透明度

在 canvas 里面,定义颜色一般只支持 RGB 的格式,如果想要设置透明颜色的话,则需要使用 globalAlpha 属性值。基本格式为:

// value 为 [0.0,1.0] 
// 默认值为 0.0,表示不透明
ctx.globalAlpha = value;

看个 demo:

ctx.globalAlpha = 0.5;

ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 100, 100);

ctx.fillStyle = "red";
ctx.fillRect(50, 50, 100, 100);

裁剪效果

在 canvas 里面,一般使用的是 clip API 来进行对屏幕的裁剪。基本格式为:

// 最常用
void ctx.clip();
// fillRule 主要有两种,下面再解释
void ctx.clip(fillRule);
// 这里就是将需要画的路径,当参数传到 clip 里
void ctx.clip(path, fillRule);

fillRule

先解释一下 fillRule:
常用的 fillRule 有两种,一种是 nonzero,一种是 even-odd 。主要作用就是用来判断重叠区域是否属于裁剪区域。ok,什么叫重叠区域呢?就是:

这种情况下,canvas 怎么判断这样的区域是否重叠呢?
默认的算法是 nonzero。

nonzero

它具体的过程是,在重叠区域中,选择一个 P 点,然后随机的按照一个方向,做无限长的射线,检测该线和边界的交叉点,判断接触位置是 顺时针还是逆时针。如果为顺时针则 -1,如果为逆时针则 +1。统计最终的结果,如果为 0 则说明,该区域在外部,否则在内部。
所以,上面的结果是 -2,不是 0,则表示在内部。

even-odd rule

该算法主要约定的是,统计射线和边界相交的次数,如果为偶数,则表示不在内部,如果为奇数,则表示在内部。

所以,根据该算法,上面的结果为 2 (偶数),则不在内部。

不过,在大多数情况下,这你都不需要过多关心。接下来,我们来实践一下,如何绘制裁剪区域。这里,我们就可以将 clip 方法想象为 stroke 方法。在 clip 前,先手动绘制路径,绘制完成后,便可以触发 clip 进行裁剪就 ok 了。

ctx.beginPath();
ctx.ellipse(100,100,30,50,0,0,2*Math.PI);
ctx.clip();
ctx.fillRect(100,100,30,50);

实际样式为:

图层重叠的效果

图层重叠用到的 API 为: globalCompositeOperation。它的作用主要是用来规定,两个重叠图层绘制的效果。基本格式为:

globalCompositeOperation = type

基本的取值,可以参考: globalCompositeOperation - 取值内容

canvas 的图像处理

canvas 中的图像处理内容不多,基本上有 3 个API:createImageData(),putImageData(),getImageData()。第一个就是用来截屏用的,第二/三个就是用来获取图片的基本信息,在关于 image 有个比较重要的概念就是 imagedata。

  • imageData:实际上就是完整的图片内容。它是底层像素的上层表示,其挂载了 3 个属性。

    • width: 图片的宽

    • height:图片的高

    • data: 是 Uint8ClampedArray 格式,实际上一个一维数组,按顺序,每个像素点占 4 位,里面包含了 RGBA 的内容,每一位都是 0-255 大小的数。最后透明度有点特殊,取值为 [0-255] 表示不透明。如果需要用到 css 的 rgba 中,需要 /255。例如: [0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255] ,4个点。

  • 最常获取到 imagedata 就是通过 createImageData。注意,通过 image 是不能获取 imagedata 的,imagedata 只能通过 createImageData 来获取和创建。

ctx.rect(10, 10, 100, 100);
ctx.fill();

let imagedata = ctx.createImageData(100, 100);

createImageData

用来手动创建一个空图片内容,默认的像素都是透明的黑色,基本格式为:

ImageData ctx.createImageData(width, height);
ImageData ctx.createImageData(imagedata);
  • width/height:用来设置当前 imgObj 的大小。

  • imagedata:获得一个已知 imgObj 的大小,注意这里不是复制,只是 copy imagedata 的 width/height。

目前来说,它的用途,还不知道在哪里~

getImageData

该 API 常常用来作为图片分析,因为它可以直接获取 canvas 上像素的内容,然后进行相关操作。基本格式为:

ctx.getImageData(x, y, width, height);
  • x,y 用来指定截取像素的起始点

  • widht/height 用来指定截取像素点的宽/高

例如:

ctx.fillRect(0,0,200,200);
let new_one = ctx.getImageData(0,0,2,2);

该属性最大的用处在于,用来作为取色器。通过,传入 canvas 里的坐标,来获取指定位置的颜色值。

ctx.fillRect(0,0,200,200);
let new_one = ctx.getImageData(0,0,1,1);
let backColor = function(data){
       return `(${data[0]},${data[1]},${data[2]},${data[3]/255})`;
}

putImageData

该属性常常用来作为,像素的填写。基本格式为:

void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, startX, startY, width, height);

主要所一下第二种形式吧:

  • dx/y 代表在 canvas 上绘制的绘制

  • startX/Y 表示相对于图片其实的位置

  • width/height 表示获取的宽/高

具体的含义为:

ctx.fillRect(0,0,100,100);
var imagedata = ctx.getImageData(0,0,100,100); // 获取部分图像信息
ctx.putImageData(imagedata, 150, 0, 20, 20, 25, 25);
ctx.strokeRect(150,0,100,100);

该常常用来作为放大预览,灰度处理,导出图片等。详情可以参考: 图片像素处理。总而言之,putImageData 常作为处理图片本身,而 drawImage 常用于创建另外一新的 canvas。

生成 URI

这应该算一种快速生成图片格式的方法吧。该是通过算法,将图像中的像素点转化为序列值(也就是文本),我们可以直接将文本放入 img.src 中,即可显示图片。
在 canvas 中生成 URI 需要使用:

canvas.toDataURL(type, encoderOptions);

该 API 是直接挂在到 canvas 下的。

  • type: 用来设置导出图片类型,默认为:image/png。可选值有:image/jpeg,image/webp(chrome支持)。

  • encoderOptions[Number]:用来设置压缩比。只针对于 image/jpeg or webp 有用。通常取值为 0-1。默认值为 0.92。

DataURI 的基本格式为:

data:[<mediatype>][;base64],<data>
// 例如:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ9oADAMBAAIRAxEAPwD/AD/6AP/Z

生成的 DataURI 是根据 canvas 的大小来确定的。

var canvas = document.getElementById("canvas"); // canvas size: 5x5
var dataURL = canvas.toDataURL(); // 生成的大小为 5x5

不过并不是,任何图片都能生成 DataURI 的,有些浏览器由于内存的限制,对 DataURI 的长度也会有所限制。例如:Opera 限制为 65000 characters。

Canvas Animation

使用 Canvas 来做动画,我们需要了解幕布的盖帘--擦除,绘制。基本的 API 有: clearRect, requestAnimation

clearRect

该 API 用来清空一块幕布,基本格式为:

ctx.clearRect(x, y, width, height);

用来清除指定区域的内容。

  • x,y:用来指定区域的起始位置

  • width,height:指定区域的宽高

一般来说,通常是用来清除整个 canvas 内容。

ctx.clearRect(0, 0, canvas.width, canvas.height);

使用 requestAnimationFrame

主要还是依靠循环调用 RAF,来实现流畅动画。看一个 demo

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
  // online
 earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';

  // local
  earth.src = 'Canvas_earth.png';
  sun.src='Canvas_sun.png';
  window.requestAnimationFrame(draw);
}


function draw(){
  let canvas = document.getElementsByTagName('canvas')[0],
      ctx = canvas.getContext('2d'),
      width = canvas.width,
      height = canvas.height;
    // ctx.globalCompositeOperation = 'destination-over';
    ctx.clearRect(0,0,width,height);
    ctx.drawImage(sun,0,0,width,height);
    ctx.fillStyle= 'rgba(0,0,0,0.4)';
    ctx.strokeStyle = 'rgba(0,50,50,0.5)';
    ctx.arc(width/2,height/2,width/3,0,2*Math.PI);
  
  
    ctx.stroke();
    // ctx.fill();
  
    ctx.save();
    //earth
    let date = new Date();

    ctx.translate(width/2,height/2);
    // 公转
    ctx.rotate ((2*Math.PI)*1*(date.getSeconds()/60 + date.getMilliseconds()/60000) );
    ctx.translate(width/3,0);
    // 自转
    ctx.rotate ((2*Math.PI)*25*(date.getSeconds()/60 + date.getMilliseconds()/60000) );
    ctx.drawImage(earth,-earth.width/2,-earth.height/2);
    ctx.save();
    ctx.translate(20,0);
    ctx.drawImage(moon,-moon.width/2,-moon.height/2);

    ctx.restore();
    ctx.restore();
    window.requestAnimationFrame(draw);
}
init();
返回顶部