HTML5之Canvas

感性认识

canvas元素是HTML5中新增的一个重要元素,专门用来绘制图形,它就像页面的一个画布,利用canvas绘图的步骤很简单:

  1. 页面放置canvas;
  2. 获取canvas的context;
  3. 通过context的api用JavaScript来进行绘画;
  4. 绘制路径(矩形不需要),设置样式,使用stroke或fill来填充;

首先我们了解canvas的坐标是这样的

先来看下示例

<script type="text/javascript">
	var canvas=document.getElementById("mycanvas");
	//获取context
	var ctx=canvas.getContext("2d");
	//设置样式
	ctx.strokeStyle="green";
	ctx.lineWidth=2;
	//绘制矩形(不需要绘制路径)
	ctx.strokeRect(10,10,180,60);
	
	//绘制直线
	ctx.moveTo(200,30);
	ctx.lineTo(280,60);
	ctx.stroke();
	
	//绘制圆形
	ctx.beginPath();
	ctx.fillStyle="blue";
	ctx.arc(60,130,50,0,Math.PI*2,true);
	ctx.fill();
	
	//绘制三角形	
	ctx.beginPath();
	ctx.moveTo(120,130);
	ctx.lineTo(190,190);
	ctx.lineTo(280,150);
	ctx.closePath();
	ctx.stroke();
</script>

绘制的图形如下:

绘制简单图形

  1. 绘制矩形
    fillRect(x, y, width, height) //绘制实心矩形,被填充
    strokeRect(x, y, width, height)//绘制空心矩形,只是边框
    它的四个参数分别为矩形左上角顶点的x坐标、y坐标,以及矩形的宽和高。
  2. 绘制圆形
    矩形不用绘制路径,直接fill或stroke,而其他的图形需要先绘制路径,然后在fill或stroke。
    context.arc(x,y,r,sAngle,eAngle,counterclockwise);
    • x:圆的中心的 x 坐标。
    • y:圆的中心的 y 坐标。
    • r:圆的半径。
    • sAngle:起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
    • eAngle:结束角,以弧度计。
    • counterclockwise:可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。
    如需通过 arc() 来创建圆,请把起始角设置为 0,结束角设置为 2*Math.PI

  3. 绘制直线
    ctx.moveTo(200,30); //设置路径的起点
    ctx.lineTo(280,60); //设置路径的终点
    ctx.stroke(); //给路径着色
    如果上面示例,我们并没有使用beginPath,因为这是第一次绘图,所以不用,但如上例中绘制圆形,如果不用beginPath,你绘制出的图如

    这是因为没有使用beginPath来重置路径,而且这还牵涉到另一个API:closePath,它的作用是把起点终点链接起来,如绘制三角形,如果你注释掉closePath,将不会绘制成三角形只是折线而已。
    这里需要强调的是使用fill时会自动把起点和终点链接起来,就类似隐含先执行了closePath一样。这一点可以这样验证,把示例中的绘制圆形注释掉beginPath,而且只绘半圆,代码如下
    <!--放置canvas-->
    <canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;"></canvas>
    <script type="text/javascript">
    	var canvas=document.getElementById("mycanvas");
    	//获取context
    	var ctx=canvas.getContext("2d");
    	//设置样式
    	ctx.strokeStyle="green";
    	ctx.lineWidth=2;
    	//绘制矩形(不需要绘制路径)
    	ctx.strokeRect(10,10,180,60);
    	
    	//绘制直线
    	ctx.moveTo(200,30);
    	ctx.lineTo(280,60);
    	ctx.stroke();
    	
    	//绘制圆形
    	//ctx.beginPath();
    	ctx.fillStyle="blue";
    	ctx.arc(60,130,50,0,Math.PI,true);
    	ctx.fill();
    	
    	//绘制三角形	
    	ctx.beginPath();
    	ctx.moveTo(120,130);
    	ctx.lineTo(190,190);
    	ctx.lineTo(280,150);
    	ctx.closePath();
    	ctx.stroke();
    </script>
    绘制的图形如下

    是不是感觉奇怪,如果把绘制圆形使用closePath,再把fill改成stroke,你就会发现路径是这样的

    所以beginPath和closePath需要要搞懂,会用慎用!

绘制渐变

  1. 线性渐变
    示例:
    var canvas=document.getElementById("mycanvas");
    //获取context
    var ctx=canvas.getContext("2d");
    //设置样式
    ctx.lineWidth=20;
    var g=ctx.createLinearGradient(0,0,0,100);
    g.addColorStop(0,'green');
    g.addColorStop(0.5,'blue');
    g.addColorStop(1,'red');
    ctx.strokeStyle=g;
    
    ctx.moveTo(0,0);
    ctx.lineTo(0,100);
    ctx.stroke();
    绘图如

    绘制线性渐变需要使用createLinearGradient和addColorStop方法
    context.createLinearGradient(x0,y0,x1,y1);
    • x0:渐变开始点的 x 坐标
    • y0:渐变开始点的 y 坐标
    • x1:渐变结束点的 x 坐标
    • y1:渐变结束点的 y 坐标
    gradient.addColorStop(stop,color);
    • stop:介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。
    • color:在结束位置显示的 CSS 颜色值
  2. 径向渐变
    示例
    <!--放置canvas-->
    <canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;"></canvas>
    <script type="text/javascript">
    var canvas=document.getElementById("mycanvas");
    //获取context
    var ctx=canvas.getContext("2d");
    //设置样式
    var g=ctx.createRadialGradient(100,100,5,100,100,30);
    g.addColorStop(0,'green');
    g.addColorStop(0.5,'blue');
    g.addColorStop(1,'red');
    ctx.fillStyle=g;
    ctx.arc(100,100,50,0,Math.PI*2,true);
    ctx.fill();
    </script>
    使用的径向渐变方法
    context.createRadialGradient(x0,y0,r0,x1,y1,r1);
    • x0:渐变的开始圆的 x 坐标
    • y0:渐变的开始圆的 y 坐标
    • r0:开始圆的半径
    • x1:渐变的结束圆的 x 坐标
    • y1:渐变的结束圆的 y 坐标
    • r1:结束圆的半径

绘制变形图

  1. 坐标变换
    可以通过变化canvas坐标来达到变换图形的效果,坐标有三种变换方式:
    • 平移:重新映射画布上的 (0,0) 位置
      context.translate(x,y);
      x和y分别表示把坐标原点向x和y方向移动多少个单位,默认的单位是像素;
    • 缩放:缩放当前绘图至更大或更小
      context.scale(x,y);
      x和y分别表示把x和y方向放大或缩小的倍数;
    • 旋转:旋转当前绘图
      context.rotate(angle);
      angle表示旋转角度,以弧度计。如需将角度转换为弧度,请使用 degrees*Math.PI/180 公式进行计算。
    示例
    <!--放置canvas-->
    <canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;"></canvas>
    <script type="text/javascript">
    var canvas=document.getElementById("mycanvas");
    //获取context
    var ctx=canvas.getContext("2d");
    ctx.strokeStyle="green";
    ctx.strokeRect(0,0,50,50);
    //移动坐标
    ctx.translate(10,10);
    ctx.strokeRect(0,0,50,50);
    //缩放2倍
    ctx.scale(2,2);
    ctx.strokeRect(10,10,50,50);
    //旋转10度
    ctx.rotate(10*Math.PI/180);
    ctx.strokeRect(10,10,50,50);
    </script>
    绘制的图形

  2. 坐标变换和路径结合的使用
    路径一旦绘制好后是不会随着坐标变换而变换的,如
    var canvas=document.getElementById("mycanvas");
    //获取context
    var ctx=canvas.getContext("2d");
    //坐标变换前的路径
    ctx.moveTo(0,0);
    ctx.lineTo(100,20);
    ctx.lineTo(20,80);
    //变换坐标
    ctx.rotate(20*Math.PI/180);
    //为路径填色
    ctx.strokeStyle="green";
    ctx.stroke();
    
    //坐标变换后绘制的图像,路径与上一路径相同
    ctx.strokeStyle="red";
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(100,20);
    ctx.lineTo(20,80);
    ctx.stroke();
    绘制的图像

    可见坐标变换对路径是没影响的,一般这种情况我们会把绘图操作封装成一个函数,方便坐标变换后操作。
    var canvas=document.getElementById("mycanvas");
    //获取context
    var ctx=canvas.getContext("2d");
    
    doPic(ctx,"green");
    ctx.rotate(20*Math.PI/180);
    doPic(ctx,"red");
    ctx.rotate(20*Math.PI/180);
    doPic(ctx,"blue");
    
    function doPic(ctx,color){	
    	ctx.beginPath();
    	ctx.moveTo(0,0);
    	ctx.lineTo(100,20);
    	ctx.lineTo(20,80);
    	ctx.strokeStyle=color;
    	ctx.stroke();	
    }
  3. 矩阵变换
    如果上述都不能满足需求,HTML5还提供一种更复杂的变化:矩阵变换,这块牵涉到矩阵的数学知识,稍后单一节示例。

图像组合

通常当绘制的图像重叠时将以绘制的先后顺序来处理重叠的部分,但HTML5提供了globalCompositeOperation 选项来设置如何处理重叠。

<canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;"></canvas>
<script type="text/javascript">
var canvas=document.getElementById("mycanvas");
//获取context
var ctx=canvas.getContext("2d");

ctx.fillStyle="red";
ctx.fillRect(20,20,75,50);
ctx.fillStyle="blue";	
ctx.globalCompositeOperation="source-over";
ctx.fillRect(50,50,75,50);	
ctx.fillStyle="red";

ctx.fillRect(150,20,75,50);
ctx.fillStyle="blue";
ctx.globalCompositeOperation="destination-over";
ctx.fillRect(180,50,75,50);	
</script>

绘制的图像

globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。

  • 源图像 = 您打算放置到画布上的绘图。
  • 目标图像 = 您已经放置在画布上的绘图。

属性值和描述

  • source-over:默认。在目标图像上显示源图像。
  • source-atop:在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。
  • source-in:在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。
  • source-out:在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。
  • destination-over:在源图像上方显示目标图像。
  • destination-atop:在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。
  • destination-in:在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。
  • destination-out:在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
  • lighter:显示源图像 + 目标图像。
  • copy:显示源图像。忽略目标图像。
  • source-over:使用异或操作对源图像与目标图像进行组合。

绘制阴影

HTML5给添加阴影也非常简单,主要涉及到下面几个属性:

  • shadowColor:设置或返回用于阴影的颜色,如果取消只需设置为rgba(0,0,0,0);
  • shadowBlur:设置或返回用于阴影的模糊级别,一般设置在0-10之间;
  • shadowOffsetX:设置或返回阴影距形状的水平距离;
  • shadowOffsetY:设置或返回阴影距形状的垂直距离;
ctx.fillStyle="blue";
ctx.shadowColor="#ccc";
ctx.shadowBlur=5;
ctx.shadowOffsetX=10;
ctx.shadowOffsetY=10;
ctx.fillRect(10,10,60,60);
ctx.fillRect(80,80,60,60);

使用图片

    1. 绘制图片
      插入图片的方法只有一个,但是有三个重载方法,具体如下:
      context.drawImage (image, dx, dy)
      context.drawImage (image, dx, dy, dw, dh)
      context.drawImage (image, sx, sy, sw, sh, dx, dy, dw, dh)
      image是一个Image对象,用来装载图像文件,而其他参数参考下图:

      示例
      <canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;">不支持canvas.</canvas>
      <script type="text/javascript">
      var mycanvas=document.getElementById("mycanvas");
      var ctx=mycanvas.getContext("2d");
      var img=new Image();
      img.src="p.jpg";
      ctx.drawImage(img,0,0);
      </script>
      但这样有时可能并不能绘制出图片,因为当image的src设置好后,但由于网络或图片过大导致不能及时加载,所以一般我们会在image的onload事件中操作
      <canvas id="mycanvas" width="300" height="200" style="border:1px solid #ccc;">不支持canvas.</canvas>
      <script type="text/javascript">
      var mycanvas=document.getElementById("mycanvas");
      var ctx=mycanvas.getContext("2d");
      var img=new Image();
      img.src="p.jpg";
      img.onload=function(){
      	ctx.drawImage(img,0,0); 
      	ctx.drawImage(img,10,10,50,50,250,0,50,50);
      }
      绘制的图片如:
    2. 图像平铺 HTML5提供了一个非常方便的方法
createPattern(image, repetitionStyle)
      创建并返回一个 CanvasPattern 对象,该对象表示一个贴图图像所定义的模式。要使用一个模式来勾勒线条或填充区域,可以把一个 CanvasPattern 对象用作 strokeStyle 属性或 fillStyle 属性的值。repetitionStyle 说明图像如何贴图。可能的值如下所示:
      • "repeat" - 在各个方向上都对图像贴图。默认值。
      • "repeat-x" - 只在 X 方向上贴图。
      • "repeat-y" - 只在 Y 方向上贴图。
      • "no-repeat" - 不贴图,只使用它一次。
      示例
img.onload=function(){
	var ptt=ctx.createPattern(img,'repeat');
	ctx.fillStyle=ptt;
	ctx.fillRect(0,0,300,200);
}

  1. 裁剪
    clip() 方法从原始画布中剪切出任意形状和尺寸。
    一旦再勾画出路径后调用clip方法,则所有只有的绘图都会被限制在这个路径区域,从而达到裁剪的效果,可以结合save和restore方法。
    img.onload=function(){	
    	ctx.beginPath();
    	ctx.arc(60,60,50,0,Math.PI*2,true);	
    	ctx.clip();
    	ctx.drawImage(img,0,0);
    }
  2. 像素
    html5还有一个令人赞叹的技术就是可以处理像素,这设计到两个API:
    var imgData=context.getImageData(x,y,width,height);
    • x - 开始复制的左上角位置的 x 坐标。
    • y - 开始复制的左上角位置的 y 坐标。
    • width - 将要复制的矩形区域的宽度。
    • height - 将要复制的矩形区域的高度。
    getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。对于 ImageData 对象中的每个像素,都存在着四方面的信息,即rgba 值:
    • r - 红色 (0-255)
    • g - 绿色 (0-255)
    • b - 蓝色 (0-255)
    • a - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
    color/alpha 以数组形式存在储于 ImageData对象的 data 属性中,形式如[r1,g1,b1,a1,r2,g2,b2,a2,....]。
    context.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
    • imgData - 规定要放回画布的 ImageData 对象。
    • x - ImageData 对象左上角的 x 坐标,以像素计。
    • y - ImageData 对象左上角的 y 坐标,以像素计。
    • dirtyX - 可选。水平值(x),以像素计,在画布上放置图像的位置。
    • dirtyY - 可选。水平值(y),以像素计,在画布上放置图像的位置。
    • dirtyWidth - 可选。在画布上绘制图像所使用的宽度。
    • dirtyHeight - 可选。在画布上绘制图像所使用的高度。
    putImageData() 方法将图像数据(从指定的 ImageData 对象)放回画布上。
    示例:我们把图片上的一部分颜色取反,并把透明度降到60%:
    img.onload=function(){	
    	ctx.drawImage(img,0,0);
    	var imgData=ctx.getImageData(10,10,80,80);
    	for(var i=0;i<imgData.data.length;i+=4){
    		imgData.data[i]=255-imgData.data[i+2];
    		imgData.data[i+1]=255-imgData.data[i+1];
    		imgData.data[i+2]=255-imgData.data[i];
    		imgData.data[i+3]=imgData.data[i+3]*0.6;
    	}
    	ctx.putImageData(imgData,150,100);
    }

    PS:在chrome等浏览器中这个API函数设计到跨域的问题,因此不能通过本地浏览(即file:///),需要通过webserver(即http://)才能看到效果,否则报类似下面的异常:

    但firefox是好用的。

绘制文字

在canvas中也可以绘制文字,这涉及到两个API:

context.fillText(text,x,y,maxWidth);
context.strokeText(text,x,y,maxWidth);
  • text - 规定在画布上输出的文本。
  • x - 开始绘制文本的 x 坐标位置(相对于画布)。
  • y - 开始绘制文本的 y 坐标位置(相对于画布)。
  • maxWidth - 可选。允许的最大文本宽度,以像素计。

在绘制之前可以先设置文字的相关属性:

  • font属性:设置文字的字体,语法与 CSS font 属性相同,可以按顺序设置font-style、font-variant、font-weight、font-size/line-height、font-family;
  • textAlign属性:设置文字的水平对齐方式,可选值start、end、left、right、center,默认是start;
  • textBaseline属性:设置文字的垂直对齐方式,可选值top、hanging、middle、alphabetic、ideographic、bottom,默认是alphabetic;

有时为了方便定位,需要获取文字的宽度,html5提供了一个API:

context.measureText(text)

返回的TextMetrics对象的width属性表示使用当前指定的字体后文字的总长度。

ctx.font="italic 30px sans-serif";
ctx.textBaseline="top";
ctx.fillStyle="red";
var txt='爱我中华';
ctx.fillText(txt,0,0);
var tml = ctx.measureText(txt);
ctx.strokeStyle="green";
ctx.strokeText(txt,tml.width+20,0);

善后工作

  1. 保存及恢复状态
    在绘制过程中,可能临时要改变一下属性设置,当临时绘制结束又想回到之前的状态,这就涉及到恢复,涉及到两个API:
    save() //把当前环境的状态压入栈
    restore() //状态出栈
    示例
    ctx.fillText(txt,0,0);
    ctx.save();//状态入栈
    ctx.fillStyle="green";
    ctx.textBaseline="bottom";
    ctx.fillText(txt,0,80);
    ctx.restore();//状态出栈
    ctx.fillText(txt,0,120);
  2. 保存文件
    在canvas绘制后可以使用toDataURL方法把它保存成浏览器能识别的data URL数据
    ctx.font="italic 30px sans-serif";
    ctx.textBaseline="top";
    ctx.fillStyle="red";
    var txt='爱我中华';
    ctx.fillText(txt,0,0);
    
    document.getElementById("img").src=mycanvas.toDataURL("image/jpeg");
    data URL是目前大多数浏览器能够识别的一种base64编码的URL,主要用于小型的、可以在网页中直接嵌入的数据,格式类似“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAADICAYAAA....”
  3. 简单动画
    可以在canvas重复绘制、擦除、绘制的动作来实现动画,这其中涉及到一个擦除的API
    context.clearRect(x,y,width,height);
    setInterval函数
    setInterval(code,millisec[,"lang"])
    setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
    例如我们来绘制一个运动的小圆:
    var i=0;
    var intervalId=setInterval(doarc,1000);
    
    function doarc(){
    	ctx.clearRect(0,0,mycanvas.width,mycanvas.height);
    	ctx.beginPath();
    	ctx.arc(i,i,20,20,Math.PI*2,true);
    	ctx.fillStyle="blue";
    	ctx.fill();
    	i+=30;
    	if(i>200){clearInterval(intervalId);}
    }

原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4621670.html