赵工的个人空间


专业技术部分转网页计算转业余爱好部分


  网站建设

首页 > 专业技术 > 网站建设 > HTML5画布canvas
HTML5画布canvas
  1. <canvas>元素:
  2. 在Canvas上绘图:
  3. 处理图像:
  4. 在Canvas上实现动画:
  5. 在Cancas上处理视频:

Canvas称为画布,通过使用Canvas API可以解决图形绘制需求,绘图、渲染图形、创建动画和处理图像与文字,而且还能与标准的其他特性相结合,创建完整的应用程序,甚至是2D和3D Web游戏。

1.<canvas>元素:

这个元素会在网页上创建一个空白区域,然后通过API操作这个区域。这个元素只需要少量的属性,渲染元素上的所有内容都会参考这些值。示例:
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Canvas API </title>
  <script src="context.js"></script>
</head>
<body>
<section id="canvasbox">
  <canvas id="canvas" width="500" height="300">
    你使用的浏览器不支持此功能
  </canvas>
</section>
</body>
<html>
在不支持Canvas API的浏览器上,<canvas>标记之间的内容会显示在界面上。
在使用<canvas>元素时,第一个要调用的方法是getContext(),它会返回画布的绘图上下文。通过这个引用,就能够使用其他API。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
}
window.addEventListener("load", initiate, false);
上述代码中,<canvas>元素的引用保存在canvas变量中,getContext()则创建所需要的上下文。这个方法可以接受两个值,2d和3d,分别代表2维和3维环境。目前,HTML5只支持2d。
画布绘图上下文由从上到下、从左到右的像素网格组成,其原点位于正方形的左上角。

2.在Canvas上绘图:

在<canvas>元素及其内容准备好之后,就可以开始创建和绘制实际的图形了。通常,开发者必须准备需要绘制的图形,然后再将它发到上下文中,但是有一些方法支持直接在画布上绘图。

1)绘制矩形:

一些专门的方法用于绘制矩形,它们是唯一能够生成基础形状的方法,要想绘制其他形状必须组合使用各种绘图方法和复杂路径。这些方法包括:
·fillRect(x, y, width, height):这个方法可以绘制实心矩形,x和y属性指定矩形的左上角位置,width和height声明其尺寸。
·strokeRect(x, y, width, height):这个方法可以绘制空心矩形,即只有轮廓。
·clearRect(x, y, width, height):这个方法可用于清除属性所指定区域的像素,类似于矩形擦除器。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.strokeRect(100,100,120,120);
  context.fillRect(110,110,100,100);
  context.clearRect(120,120,80,80);
}
window.addEventListener("load", initiate, false);

2)颜色:

canvas默认颜色为黑色,可以使用CSS语法和以下属性指定绘图颜色:
·strokeStyle:声明形状线条的颜色
·fillStyle:声明形状内部区域的颜色
·globalAlpha:可以设置画布上绘制的所有图形的透明度
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.fillStyle="#000099";
  context.strokeStyle="#990000";
  context.strokeRect(100,100,120,120);
  context.fillRect(110,110,100,100);
  context.clearRect(120,120,80,80);
}
window.addEventListener("load", initiate, false);
代码中指定的颜色值是十六进制格式,也可以使用rgb()等函数,或者使用rgba()函数指定形状的透明度,这些函数的值必须添加双引号,例如,strokeStyle="rgba(255,165,0,1)"。还有另一个属性globalAlpha可用于设置透明度级别,语法为:globalAlpha=value,其中value是0.0~1.0之间的数值,0.0表示完全不透明,1.0表示完全透明。
如果使用这些方法指定颜色值,那么指定的颜色会成为后续绘图的默认颜色。

3)渐变:

Canvas支持的渐变效果包括线性渐变或射线渐变,并且支持设置颜色转折点。
·createLinearGradient(x1,y1,x2,y2):这个函数可以在画布上创建一个渐变对象
·createRadialGradient(x1,y1,r1,x2,y2,r2):这个函数使用两个圆在画布上创建一个渐变对象,这些值表示每个圆的圆心和半径
·addColorStop(position,color):这个函数可以指定渐变颜色值,position的值在0.0~1.0之间,颜色退化效果始于color指定的颜色值。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  var grad=context.createLinearGradient(0,0,10,100);
  grad.addColorStop(0.5, '#0000FF');
  grad.addColorStop(1, '#000000');
  context.fillStyle=grad;
  context.fillRect(10,10,100,100);
  context.fillRect(150,10,200,100);
}
window.addEventListener("load", initiate, false);
上述代码创建一个从(0,0)到(10,100)的渐变对象,并且稍微向左倾斜。addColorStop()方法负责设置颜色,最后在fillStyle属性上应用渐变效果。
渐变位置是相对于画布而定的,而非相对于所绘制的图形。如果将代码后面的矩形移到屏幕的新位置,渐变效果就会发生变化。
放射渐变使用表达式createRadialGradient(0,0,30,0,0,300)。示例:
   var grd=context.createRadialGradient(50,50,0,100,100,90);
   grd.addColorStop(0,"#0f0");
   grd.addColorStop(1,"#f90");
   context.fillStyle=grd;
   context.beginPath();
   context.arc(100,100,90,0,Math.PI*2,true);
   context.fill();

4)创建路径:

通常,会在后台处理图形和图像,处理完成后发送给上下文,绘制出结果。为此,Canvas API引入了几种生成路径的方法。
路径,就像是画笔移动的地图。建立路径后,发送到上下文,就可以在画布上实际地绘制出图形。路径可能包括多种笔画,如直线、弧线、矩形等,以此构成复杂的形状。
有两种方法分别用于开始和结束一个路径:
·beginPath():这个方法会开始一个新的形状描述。创建路径前必须先调用这个方法。
·closePath():这个方法会关闭路径,用直线将最后一个点与原点相连。如果想保留开放路径,或者使用fill()方法绘制路径,则不需要调用这个方法。
有三种方法可以在画布上绘制路径:
·stroke():这个方法可以将路径绘制为轮廓形状。
·fill():这个方法会将路径绘制为实心形状,路径会自动通过一条连接最后一个点与第一个点的直线实现封闭,不需要使用closePath()。
·clip():这个方法可以在上下文中设置裁剪区域。上下文初始化后画布会占据整个区域,clip()方法会将区域裁剪变成新的形状,从而创建遮罩效果。外面的内容不会绘制。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.beginPath();
  context.stroke();
}
window.addEventListener("load", initiate, false);
上述代码并没有绘制任何内容,只是在画布上下文中开始路径,然后使用stroke()绘制路径,将来会在界面上显示形状的轮廓。
下述方法可用于设置路径和创建真正的形状:
·moveTo(x,y):这个方法会将笔尖移到指定位置,可以开始或继续绘制路径
·lineTo(x,y):这个方法可以绘制一条直线,连接当前笔尖位置到x和y属性声明的新位置
·rect(x,y,width,height):这个方法可以生成一个构成路径的矩形,而非直接绘制在画布上
·arc(x,y,radius,startAngle,endAngle,direction):这个方法可以在位置(x,y)上生成弧线或圆形,半径和弧度由属性指定,最后一个是布尔值,表示顺时针或逆时针方向。
·arcTo(x1,y1,x2,y2,radius):使用切线绘制弧线,使用两个目标点和一个半径,而起始点通常会使用moveTo()方法来指定。这种方法绘制圆弧借助了两条辅助线,起始点与P1点的线和P1与P2点的线,圆弧与这个两条线相切。如context.arcTo(150,60,180,130,50);
·quadraticCurveTo(cpx,cpy,x,y):这个方法会生成二次样条曲线,连接当前笔尖位置到x和y属性声明的位置。起点是当前位置,终点由x和y确定,cpx和cpy是曲线形状控制点。
·bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y):这个方法生成贝塞尔曲线,起点是当前位置,终点由x和y确定,要声明两个控制点,以确定曲线的形状。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.beginPath();
  context.moveTo(100,100);
  context.lineTo(200,200);
  context.lineTo(100,200);
  context.stroke();
}
window.addEventListener("load", initiate, false);
上述代码在屏幕上会显示一个未闭合的三角形。建议在开始路径之后马上设置笔尖开始的位置。
示例2:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.beginPath();
  context.moveTo(100,100);
  context.lineTo(200,200);
  context.lineTo(100,200);
  context.clip();

  context.beginPath();
  for (f=0;f<300;f=f+10) {
    context.moveTo(0,f);
    context.lineTo(500,f);
  }
  context.stroke();
}
window.addEventListener("load", initiate, false);
上述代码示例了clip()方法的作用。这个方法并没有绘制任何内容,而是创建了一个由路径形成的遮罩,所有落在遮罩外的内容都不会绘制。其中,使用for循环生成多条相隔10像素的水平线,这些线条在画布上从左到右绘制,但只有落入三角形遮罩的线条才会显示。
绘制圆和圆弧的方法示例:
context.arc(100,100,50,0,Math.PI*2,false);
这个方法使用弧度值设定起始点,所以要使用Math.PI*2表示360°。而把角度转化为弧度,可以使用公式:radians=Math.PI/180*n,其中n为角度值。

5)线型:

线条的宽度、端点等都可以根据实际需要进行调整。有4个属性:
·lineWidth:这个属性可以指定线条粗细,默认值为1
·lineCap:这个属性可以指定线条端点形状,三个值butt、round和square
·lineJoin:这个属性可以指定两条线之间的连接点形状,三个值round、bevel和miter
·miterLimit:这个属性与lineJoin一起使用,当lineJoin属性设为miter时,可以用于确定线条交接点的延伸范围。
这些属性会影响整条路径,必须先修改线条特性,再使用新属性创建新的路径。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.beginPath();
  context.arc(200,150,50,0,Math.PI*2,false);
  context.stroke();
  context.beginPath();

  context.lineWidth=10;
  context.lineCap="round";
  context.beginPath();
  context.moveTo(230,150);
  context.arc(200,150,30,0,Math.PI,false);
  context.stroke();
  context.lineWidth=5;
  context.lineJoin=”miter”;
  context.beginPath();
  context.moveTo(195,135);
  context.lineTo(215,155);
  context.lineTo(195,155);
  context.stroke();
}
window.addEventListener("load", initiate, false);
线条样式strokeStyle不仅可以设置颜色,还可以设置渐变和图案模式。fillStyle可以设置填充颜色、渐变和图案模式。

6)文字:

在画布上写文字非常简单,只需要定义一些属性和调用相应的方法。配置文字的3个属性:
·font:这个属性与CSS的font属性类似,所接受的值完全相同
·textAlign:这个属性表示对齐方式,可以接受的值有start、end、left、right和center
·textBaseline:这个属性用于设置垂直对齐方式,包括top、hanging、middle、alphabetic、ideographic和bottom
还有两个方法支持在画布上绘制文字:
·strokeText(text,x,y):这个方法会在位置(x,y)绘制指定文字的轮廓。还可以加入第4个值,以声明最大尺寸,如果文字超出这个值,那么会缩小以适应显示范围。
·fillText(text,x,y):这个方法与前一个方法类似,但绘制的是实心文字。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');

  context.font="bold 24px verdana, sans-serif";
  context.textAlign="start";
  context.fillText("my message", 100, 100);
}
window.addEventListener("load", initiate, false);
API还提供了另一个绘制文字的方法:
·measureText():这个方法会返回指定文字的大小信息,可用于将画笔上的文字与其他形状进行组合,计算出位置与动画碰撞等信息。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');

  context.font="bold 24px verdana, sans-serif";
  context.textAlign="start";
  context.textBaseline="bottom";
  context.fillText("my message", 100, 124);

  var size=context.measureText("My message");
  context.strokeRect(100, 100, size.width, 24);
}
window.addEventListener("load", initiate, false);
上述代码添加了垂直对齐方式,可以帮助了解文字在画布上的确切垂直位置。使用measureText()方法和width属性就可以计算出文字的水平尺寸。在测量得到所有尺寸后,就能够在文字周围绘制一个矩形。

7)阴影:

阴影效果也是canvas API的重要组成部分。每一条路径和文字都可以创建阴影效果。API提供了4个实现阴影效果的属性:
·shadowColor:这个属性使用CSS语法声明阴影的颜色
·shadowOffsetX:这个属性接受一个数字,确定对象阴影的水平投射距离
·shadowOffsetY:这个属性接受一个数字,确定对象阴影的垂直投射距离
·shadowBlur:这个属性可以为阴影生成模糊效果
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');

  context.shadowColor="rgba(0,0,0,0.5)";
  context.shadowOffsetX=4;
  context.shadowOffsetY=4;
  context.shadowBlur=5;

  context.font="bold 50px verdana, sans-serif";
  context.fillText("My message", 100, 100);
}
window.addEventListener("load", initiate, false);
上述代码使用rgba()函数设置半透明的黑色阴影,偏离对象4像素,模糊值为5。也可以在文字外的另一个图形上应用阴影效果,比如在轮廓或实心形状上。

8)转换:

Canvas支持在图形与画布本身上执行复杂的操作。这些操作可以通过5个不同的转换方法实现:
·translate(x,y):这个转换方法可用于移动画布的原点。每一个画布原点都位于(0,0),即左上角,然后在画布内的各个方向上增加,负值则落在画布外。有时候,使用负值创建复杂图形很有用,translate()方法可以将原点移到另一个位置,作为绘制的新参考点。
·rotate(angle):这个转换方法可以使画布以原点为中心旋转一定的角度,使用弧度值。
·scale(x,y):这个转换方法可以增加或减小画布的单位,从而缩小或放大所绘制的内容。水平会垂直比例都可以使用x和y进行单独修改。这些值可以是负值,从而产生镜像效果,默认值是1.0。
·transform(m1,m2,m3,m4,dx,dy):画布拥有一组属性矩阵值。此方法可以在当前矩阵之上应用一组新矩阵,从而修改画布。
·setTransform(m1,m2,m3,m4,dx,dy):这个方法可以重置当前的转换,使用其属性值创建新的转换配置。
示例:平移、旋转和缩放
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.font="bold 20px verdana, sans-serif";
  context.fillText("TEXT", 50, 20);

  context.translate(50,70);
  context.rotate(Math.PI/180*45);
  context.fillText("TEXT", 0, 0);

  context.rotate(-Math.PI/180*45);
  context.translate(0,100);
  context.scale(2,2);
  context.fillText("TEXT", 0, 0);
}
window.addEventListener("load", initiate, false);
每一次转换都会累加。例如,如果使用scale()执行两次转换,那么第二次方法会基于当前状态执行缩放。对矩阵转换方法也不例外:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');

  context.transform(3,0,0,1,0,0);

  context.font="bold 20px verdana, sans-serif";
  context.fillText("TEXT", 20, 20);

  context.transform(1,0,0,10,0,0);

  context.font="bold 20px verdana, sans-serif";
  context.fillText("TEXT", 100, 20);
}
window.addEventListener("load", initiate, false);
画布矩阵默认值是(1,0,0,1,0,0),上述代码的第一次转换中,将第一个值修改为3,就可以在水平方向拉伸矩阵,转换后绘制的文字在水平方向较宽;而下一次矩阵转换,将第4个值修改为4,使矩阵在垂直方向进行拉伸。矩阵转换是在前一个转换基础上进行的,所以第2组文字会同时在水平和垂直方向拉伸。如果要重置矩阵和设置全新的转换值,则可使用setTransform()方法。

9)恢复状态:

转换的积累增加了返回前一状态的难度。Canvas API提供了两种方法以保存和取回画布状态:
·save():这个方法可以保存画布状态,包括应用的转换、样式属性值和当前裁剪路径。
·restore():这个方法可以恢复上一次保存的状态。
示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.save();
  context.translate(50,70);
  context.font="bold 20px verdana, sans-serif";
  context.fillText("TEXT1", 0, 30);
  context.restore();
  context.fillText("TEXT2", 0, 30);
}
window.addEventListener("load", initiate, false);
无论执行了多少次转换,在调用restore()方法时,状态都可以完全恢复到前一个状态。

10)globalCompositeOperation:

有一个属性globalCompositeOperation可用于确定形状定位方式及其与画布上已绘制图形的组合方式,其默认值是source-over,表示新的图形绘制在画布已绘制图形之上。这个属性还可以设置另外11个值:
·source-in:只绘制新图形中覆盖已绘制图形的部分,其余部分及已绘制图形的其余部分都变成透明的。
·source-out:只绘制新图形中不覆盖已绘制图形的部分,其余部分及已绘制图形的其余部分都变成透明的。
·source-atop:只绘制新图形中覆盖已绘制图形的部分,已绘制图形保留,但是新图形的其余部分变成透明的。
·lighter:两个图形都绘制,但是重叠部分的颜色为指定颜色。
·xor:两个部分都绘制,但是重叠部分变成透明的。
·destination-over:这是默认值的相反值,将新图形绘制在画布已绘制的图形之下。
·destination-in:保留画布现有图形中与新图形重叠的部分,其余部分(包括新图形)都变成透明的。
·destination-out:保留画布现有图形中不与新图形重叠的部分,其余部分(包括新图形)都变成透明的。
·destination-atop:只保留现有图形与新图形中重叠的部分。
·darker:两个图形都绘制,但是重叠部分的颜色取颜色的差值。
·copy:只绘制新图形,将已绘制图形变成透明的。
只有查看globalCompositeOperation属性各个值的可视化表现,才能够理解其工作方式。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  context.fillStyle="#990000";
  context.fillRect(100,100,300,100);
  context.globalCompositeOperation="destination-atop";
  context.fillStyle="#AAAAFF";
  context.font="bold 80px verdana, sans-serif";
  context.fillAlign="center";
  context.fillBaseline="middle";
  context.fillText("TEXT", 250, 110);
}
window.addEventListener("load", initiate, false);

3.处理图像:

图像处理是Canvas API中不可或缺的特性,但是只有一个原生方法支持图像处理:

1)drawImage():

这是画布中唯一支持绘制图像的方法,这个方法可以接受很多值,得到不同的结果:
·drawImage(image, x, y):在x和y指定的位置上绘制图像,第一个值是图像引用
·drawImage(image, x, y, width, height):在画布上绘制图像之前对图像执行缩放操作,将其尺寸修改为width和height指定的尺寸值
·drawImage(image, x1, y1, width1, height1, x2, y2, width2, height2):对图像进行切割,然后以定制的尺寸和位置绘制到画布上。值x1和y1设置图像所切割部分的左上角位置,width1和height1值表示图像切割的尺寸,其余的值表示切割部分绘制在画布上的位置及其尺寸,可以与原始尺寸不同。
图像,总是同一个文档中的图像引用,可以是getcanvasentById()等方法获取,或者由一般的JavaScript方法创建的图像对象,但不支持URL,也不支持从外部资源加载文件。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  var img=new Image();
  img.src="http://www.minkbooks.com/content/snow.jpg";
  img.addEventListener("load", function() {
      context.drawImage(img, 20, 20)}, false);
}
window.addEventListener("load", initiate, false);
上述代码先加载图像,然后将图像绘制到画布中。因为画布只能接收已经加载的图像,所以必须通过load事件控制图像的加载。代码添加了监听器,并且声明了一个匿名函数来处理事件。
其中匿名函数的代码可以使用另外两种形式:
context.drawImage(img,0,0,canvas.width,canvas.height)
context.drawImage(img,135,30,50,50,0,0,200,200)
上述第一段代码会把图像拉伸到整个画布,第二段代码则抽取图像从(135,50)开始尺寸为(50,50)像素的正方形区域并将这块图像的尺寸调整为(200,200)像素然后绘制到画布的(0,0)位置。

2)图像像素数据:

其实,还有一些方法可以将图像绘制到画布上,但不是操作图像,而是操作像素数据。
每一幅图像都可以由一串表示rgba值的整数表示,这些信息是一维数组,可用于生成一幅图像。Canvas API提供了3个用于操作图像数据和处理图像的方法:
getImageData(x, y, width, height):这个方法接受属性所声明尺寸的画布矩形,然后将它转换为数据。这个方法返回一个对象,将来可以使用它的属性width、height和data访问。
putImageData(imagedata, x, y):这个方法会将imagedata的数据转换为图像,然后将它绘制到画布中由x和y指定的位置。这个方法还有4个可选参数,以dx、dy、width、height确定一个矩形范围,绘制的图像仅限制在该矩形内,类似一个裁剪的区域。
createImageData(width, height):这个方法可以创建一幅空白图像,所有像素都是透明黑色。此外,还接受数据作为属性,然后返回该数据设置的尺寸。
数组中每一个值的位置都由公式(width*4*y)+(x*4)计算得到,这是像素的第一个值(红色),其他值需要加上1,绿色为(width*4*y)+(x*4)+1,蓝色为(width*4*y)+(x*4)+2,透明值为(width*4*y)+(x*4)+3。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  var img=new Image();
  img.src="snow.jpg";
  img.addEventListener("load", modimage, false);
}
function modimage(e) {
  img=e.target;
  context.drawImage(image,0,0);
  var info=context.getImageData(0,0,175,262);
  var pos;
  for (x=0;x<=175;x++) {
    for (y=0;y<=262;y++) {
      pos=(info.width*4*y)+(x*4);
      info.data[pos]=255-info.data[pos];
      info.data[pos+1]=255-info.data[pos+1];
      info.data[pos+2]=255-info.data[pos+2];
    }
  }
  context.putImageData(info,0,0);
}
window.addEventListener("load", initiate, false);
上述代码创建了一个新函数用于处理加载的图像,变为底片效果。先利用target属性创建图像引用,然后使用这个引用和drawImage()方法,将图像绘制到画布的(0,0)位置。
由于安全限制,在将外部来源的图像绘制到画布元素之后,就无法从画布元素获取图像信息。只有当文档和图像来自同一个源(URL),getImageData()方法才能正确执行。
还有另一种方法是将画布内容作为base64编码字符串返回,toDataURL(type)方法,将来可以使用这个字符串作为另一个画布的数据源、HTML元素(如<img>)的数据源,发送到服务器或保存在文件中。
<canvas>元素有两个属性<width>和<height>,两个方法getContext()和toDataURL()。

3)图案:

图案可用于改进绘图路径。图案支持在使用图像绘制的形状上添加纹理。使用createPattern()方法创建图案,然后将它作为颜色应用到路径上。语法格式:
createPattern(image, type);
其中,image是图像的引用,可以接受4个值repeat、repeat-x、repeat-y和no-repeat。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  var img=new Image();
  img.src="http://www.dwenzhao.cn/content/bricks.jpg";
  img.addEventListener("load", modimage, false);
}
function modimage(e) {
  img=e.target;
  var pattern=context.createPattern(img, "repeat");
  context.fillStyle=pattern;
  context.fillRect(0,0,500,300);
}
window.addEventListener("load", initiate, false);

4.在Canvas上实现动画:

动画由JavaScript代码创建。实际上,需要先擦除创建动画的画布内容,然后再绘制形状,不断地重复这个过程。在形状绘制之后就无法移动,只有擦除绘图区域,然后再次绘制内容,以此实现动画效果。在游戏或需要创建动画的应用程序中,最好使用图像代替由复杂路径构成的形状,游戏一般使用PNG图像。示例:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  img.addEventListener("mousemove", animation, false);
}
function animation(e) {
  context.clearRect(0,0,300,500);
  var xmouse=e.clientX;
  var ymouse=e.clientY;
  var xcenter=220;
  var ycenter=150;
  var ang=Math.atan2(xmouse-xcenter,ymouse-ycenter);
  var x=xcenter+Math.round(Math.sin(ang)*10);
  var y=ycenter+Math.round(Math.cos(ang)*10);

  context.beginPath();
  context.arc(xcenter,ycenter,20,0,Math.PI*2,false);
  context.moveTo(xcenter+70,150);
  context.arc(xcenter+50,150,20,0,Math.PI*2,false);
  context.stroke();

  context.beginPath();
  context.moveTo(x+10,y);
  context.arc(x,y,10,0,Math.PI*2,false);
  context.moveTo(x+60,y);
  context.arc(x+50,y,10,0,Math.PI*2,false);
  context.fill();
}
window.addEventListener("load", initiate, false);
上述代码显示了两个紧跟鼠标指针转动的眼睛。为了移动眼睛,必须在鼠标移动时更新它们的位置。为此,添加mousemove事件监听器,当事件触发时,animation()函数就会执行。
这个函数,首先清除画布内容,然后捕捉鼠标指针位置,将第一只眼睛的位置保存在变量xcenter和ycenter中。使用鼠标位置和左眼中心位置,就可以使用atan2方法计算出两点间不可见直线的角度,使用这个角度计算出瞳孔的中心位置。在得到这些值后,就可以在画布上绘制眼睛,先绘制表示眼睛的两个圆,其中第一只眼睛的开始位置是xcenter和ycenter,第二只眼睛对应的圆向右偏移50像素。然后创建动画,使用前面通过角度计算得到的位置变量x和y,两个瞳孔用fill()方法绘制黑色实心圆表示。

5.在Cancas上处理视频:

没有专门在画布元素上处理视频的方法,只能通过获取<video>元素的每一帧,然后使用drawImage()将它作为图像绘制到画布上。示例:
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Video on Canvas </title>
  <style>
.boxes {
  display: inline-block;
  margin: 10px;
  padding: 5px;
  border: 1px solid #999999;
}
  </style>
  <script src="canvasvideo.js"></script>
</head>
<body>
<section id="boxes">
  <video id="media" width="483" height="272">
    <source src="http://www.dwenzhao.cn/content/trailer2.mp4">
    <source src="http://www.dwenzhao.cn/content/trailer2.ogg">
  </video>
</section>
<section id="boxes">
  <canvas id="canvas" width="483" height="272">
    你使用的浏览器不支持此功能
  </canvas>
</section>
</body>
<html>
这个模板包括一个JavaScript文件canvasvideo.js。内容为:
function initiate() {
  var canvas=document.getcanvasentById('canvas'),
    context=canvas.getContext('2d');
  video=document.getcanvasentById('media');
  video.addEventListener("click", push, false);
}
function push() {
  if (!video.paused && !video.ended) {
    video.pause();
    window.clearInterval(loop);
  } else {
    video.play();
    loop=setInterval(processFrames, 33);
  }
}
function processFrames() {
  context.drawImage(video,0,0);
  var info=context.getImageData(0,0,483,272);
  var pos;
  var gray;
  for (x=0;x<=483;x++) {
    for (y=0;y<=272;y++) {
      pos=(info.width*4*y)+(x*4);
      gray=parseInt(info.data[pos]*0.2989+info.data[pos+1]*0.5870
              +info.data[pos+2]*0.1140);
      info.data[pos]=gray;
      info.data[pos+1]=gray;
      info.data[pos+2]=gray;
    }
  }
  context.putImageData(info,0,0);
}
window.addEventListener("load", initiate, false);
上述代码使用push()函数,在单击时开始和停止视频播放。另外创建了一个processFrames()函数,使用公式将视频中每一帧的所有颜色转换为灰度颜色,将彩色视频变成黑白视频。
push()函数初始化一个轮询,每隔33毫秒执行一次processFrames()函数,从<video>中提取帧,然后使用drawImage()指令绘制到画布上,然后使用getImageData()方法从画布提取数据,在两个for循环中处理该帧的所有像素。颜色转换为灰度的公式为:Rx0.2989+Gx0.5870 +Bx0.1140。最后使用putImageData()方法将帧绘制到画布上。
出于安全考虑,文档所属域与视频所属域不同,那么画布禁止在绘制内容后再提取信息。因此,需要下载视频,并将所有文件上传到同一个服务器,才能正常使用。实际应用中,不推荐采用实时处理视频的方法,在一些计算机配置和浏览器上,视频处理可能会发生延迟。

Copyright@dwenzhao.cn All Rights Reserved   备案号:粤ICP备15026949号
联系邮箱:dwenzhao@163.com  QQ:1608288659