本文基本上可以用来当做 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);
});
字体颜色
字体的颜色相关和上面图形一样,同样使用的是 fillStyle
和 strokeStyle
这里就不赘述了。
字体边框粗细
定义粗细的话,同样使用 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();
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。