突袭HTML5之Canvas 2D入门2 - Canvas绘制图形

  canvas只支持一种基本形状——矩形,所有其它形状都是通过一个或多个路径组合而成,甚至是基本的矩形也可以通过路径组合成。
一、设置画笔属性
  设想我们生活中画图的样子,我们首先是选取合适的颜料和笔,一样的道理,在canvas中画图同样也是根据需要,不断的去设置当前使用的颜色和线条类型。
设置当前使用的颜色
  
任何封闭的图形都是有轮廓部分和填充部分组成。设置当前的颜色也是分两部分设置:

  • 设置填充色:context.fillStyle = color
  • 设置轮廓色:context.strokeStyle = color

  参数color可以是表示CSS颜色值的字符串,渐变对象或者图案对象。默认情况下,线条和填充颜色都是黑色(CSS颜色值#000000)。
颜色的字符串表示
  下面都是正确的值:

// 这些 fillStyle 的值均为 '橙色'  
ctx.fillStyle = "orange";  
ctx.fillStyle = "#FFA500";  
ctx.fillStyle = "rgb(255,165,0)";  
ctx.fillStyle = "rgba(255,165,0,1)";  

如果你要给每个图形上不同的颜色,你需要重新设置 fillStyle或strokeStyle 的值,就像我们画画时需要不断换不同颜色的颜料一样。
设置透明度
1.设置全局透明度:context.globalAlpha = transparency value。
  这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。例子如下所示:

function draw2() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  // draw background  
  ctx.fillStyle = '#FD0';  
  ctx.fillRect(0,0,75,75);  
  ctx.fillStyle = '#6C0';  
  ctx.fillRect(75,0,75,75);  
  ctx.fillStyle = '#09F';  
  ctx.fillRect(0,75,75,75);  
  ctx.fillStyle = '#F30';  
  ctx.fillRect(75,75,75,75);  
  ctx.fillStyle = '#FFF';  
  
  // set transparency value  
  ctx.globalAlpha = 0.2;  
  
  // Draw semi transparent circles  
  for (var i=0;i<7;i++){  
      ctx.beginPath();  
      ctx.arc(75,75,10+10*i,0,Math.PI*2,true);  
      ctx.fill();  
  }  
}  

2.设置单个图形的透明度

  很简单,把rgba格式的字符串赋给fillStyle或者strokeStyle就可以了。
创建渐变色
  canvas中我们也可以用线性或者径向的渐变来填充或描边。创建渐变色要经过下面几个步骤:
1.创建渐变对象:

  • 线性渐变:context.createLinearGradient(x1,y1,x2,y2)
    方法接受4个参数,表示渐变的起点(x1,y1) 与终点(x2,y2)。
  • 径向渐变:context.createRadialGradient(x1,y1,r1,x2,y2,r2)
    方法接受6个参数,前三个定义一个以(x1,y1)为原点,半径为r1的圆,后三个参数则定义另一个以(x2,y2)为原点,半径为r2的圆。

  两个方法返回响应的渐变对象,下面就可以给这个对象添加渐变颜色了。

2.给渐变对象上色:
  上色:gradientObject.addColorStop(position, color)
  方法接受2个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间;如果第一个色标的该参数值不是0.0,则渐变会默认认为从起点到第一个色标之间都是黑色。

  color 参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等)。
  可以根据需要添加任意多个色标(color stops),也就是说渐变的色彩数目是任意的。但是要注意保持色标定义顺序和它理想的顺序一致,特别是当色标的位置重叠的时候。
3.把渐变对象赋给图形的fillStyle或strokeStyle属性。

例子如下所示:

function draw() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  
  // Create gradients  
  var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);  
  radgrad.addColorStop(0, '#A7D30C');  
  radgrad.addColorStop(0.9, '#019F62');  
  radgrad.addColorStop(1, 'rgba(1,159,98,0)');  
    
  var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);  
  radgrad2.addColorStop(0, '#FF5F98');  
  radgrad2.addColorStop(0.75, '#FF0188');  
  radgrad2.addColorStop(1, 'rgba(255,1,136,0)');  
  
  var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);  
  radgrad3.addColorStop(0, '#00C9FF');  
  radgrad3.addColorStop(0.8, '#00B5E2');  
  radgrad3.addColorStop(1, 'rgba(0,201,255,0)');  
  
  var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);  
  radgrad4.addColorStop(0, '#F4F201');  
  radgrad4.addColorStop(0.8, '#E4C700');  
  radgrad4.addColorStop(1, 'rgba(228,199,0,0)');  
    
  // draw shapes  
  ctx.fillStyle = radgrad4;  
  ctx.fillRect(0,0,150,150);  
  ctx.fillStyle = radgrad3;  
  ctx.fillRect(0,0,150,150);  
  ctx.fillStyle = radgrad2;  
  ctx.fillRect(0,0,150,150);  
  ctx.fillStyle = radgrad;  
  ctx.fillRect(0,0,150,150);  
}  

创建图案填充效果
简单两步即搞定。
1.创建图案pattern:context.createPattern(image,type)
  该方法接受两个参数。Image 可以是一个 Image 对象的引用,或者另一个 canvas 对象。Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。

2.pattern赋给fillStyle或strokeStyle属性。

function draw() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  
  // create new image object to use as pattern  
  var img = new Image();  
  img.src = 'Penguins.jpg';  
  img.onload = function(){  
  
    // create pattern  
    var ptrn = ctx.createPattern(img,'repeat');  
    ctx.fillStyle = ptrn;  
    ctx.fillRect(0,0,150,150);  
  
  }  

创建阴影效果(目前Google Chrome 16.0.912.75还是不支持的)
  主要是设置一下阴影效果的相关属性值:

context.shadowOffsetX = float
context.shadowOffsetY = float
context.shadowBlur = float
context.shadowColor = color

shadowOffsetX和shadowOffsetY用来设定阴影在X和Y轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,他们默认都是0。
shadowBlur用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为0。
shadowColor用于设定阴影效果的延伸,值可以是标准的CSS颜色值,默认是全透明的黑色。

function draw3() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  
  ctx.shadowOffsetX = 3;  
  ctx.shadowOffsetY = 3;  
  ctx.shadowBlur = 3;  
  ctx.shadowColor = "rgba(100, 100, 0, 0.5)";  
   
  ctx.font = "20px Times New Roman";  
  ctx.fillStyle = "Black";  
  ctx.fillText("Sample String", 5, 30);  
}  

设置画笔的类型
画笔粗细:context.lineWidth = value
  这个属性设置当前绘线的粗细。属性值必须为正数。默认值是1.0。

  线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。
端点样式:context.lineCap = type
  属性 lineCap 的指决定了线段端点显示的样子。它可以为下面的三种的其中之一:butt,round 和 square。默认是 butt。每种设置完的效果如下图lineCap部分从左到右所示。
连接点样式:context.lineJoin = type
  lineJoin 的属性值决定了图形中两线段连接处所显示的样子。它可以是这三种之一:round, bevel 和 miter。默认是 miter。每种设置完的效果如下图lineJoin部分从上到下所示。
斜面连接限制:context.miterLimit = value
  当应用miter效果时,线段的外侧边缘会延伸交汇于一点上。线段直接夹角比较大的,交点不会太远,但当夹角减少时,交点距离会呈指数级增大。这时可以用miterLimit属性设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了 bevel。

 

例子如下:

function draw6() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  var lineCap = ['butt','round','square'];  
  
  // Draw guides  
  ctx.strokeStyle = '#09f';  
  ctx.beginPath();  
  ctx.moveTo(10,10);  
  ctx.lineTo(140,10);  
  ctx.moveTo(10,140);  
  ctx.lineTo(140,140);  
  ctx.stroke();  
  
  // Draw lines  
  ctx.strokeStyle = 'black';  
  for (var i=0;i<lineCap.length;i++){  
    ctx.lineWidth = 15;  
    ctx.lineCap = lineCap[i];  
    ctx.beginPath();  
    ctx.moveTo(25+i*50,10);  
    ctx.lineTo(25+i*50,140);  
    ctx.stroke();  
  }  
}  
function draw() {  
  var ctx = document.getElementById('lesson01').getContext('2d');  
  var lineJoin = ['round','bevel','miter'];  
  ctx.lineWidth = 10;  
  for (var i=0;i<lineJoin.length;i++){  
    ctx.lineJoin = lineJoin[i];  
    ctx.beginPath();  
    ctx.moveTo(-5,5+i*40);  
    ctx.lineTo(35,45+i*40);  
    ctx.lineTo(75,5+i*40);  
    ctx.lineTo(115,45+i*40);  
    ctx.lineTo(155,5+i*40);  
    ctx.stroke();  
  }  
}  

二、绘制简单矩形
  矩形是唯一的基本图形,canvas提供了直接的API支持。

  • context.fillRect(x,y,width,height) : 绘制带填充色的矩形。
  • context.strokeRect(x,y,width,height) : 绘制矩形外框。
  • context.clearRect(x,y,width,height) : 清空指定的矩形区域,并设置该区域是透明的(Transparent)。
    它们都接受四个参数, x 和 y 指定矩形左上角(相对于原点)的位置,width 和 height 是矩形的宽和高。

  除了这种方式,还可以使用绘制路径的方式绘制矩形,这个参看路径绘图部分。

绘制矩形的例子如下:

function draw(){
  var canvas = document.getElementById('tutorial');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25,25,100,100);
    ctx.clearRect(45,45,60,60);
    ctx.strokeRect(50,50,50,50);
  }
}

三、路径绘图

  cavas只提供了绘制矩形的API,其他的图形都是靠路径绘制。

绘制一个图形主要的过程如下:
1.启动路径
方法:使用context.beginPath()启动路径绘图。
  在内存里,路径是以一组子路径(直线,弧线等)的形式储存的,它们共同构成一个图形。每次调用 beginPath,子路径组都会被重置,然后可以绘制新的图形。
2.移动画笔到起点
方法:使用moveTo(x, y)移动画笔。
  虽然大多数画图的时候,用不到这个方法。我们也不能用这个方法来画什么,但是你可以把它想象成是把笔提起,并从一个点移动到另一个点的过程。当你绘制不联系的路径的时候,你就会有这个动作了。
当 canvas 初始化或者调用 beginPath 的时候,起始坐标设置就是原点(0,0)。有时候,我们需要moveTo方法将起始坐标移至其它地方,用于绘制不连续的路径。
3.内存中绘制线段
画直线:lineTo(x, y)
  该方法接受终点的坐标(x,y)作为参数。起始坐标取决于前一路径,前一路径的终点即当前路径的起点,起始坐标通常也可以通过moveTo方法来设置。
画圆弧:arc(x, y, radius, startAngle, endAngle, anticlockwise)
  我们用 arc 方法来绘制弧线或圆,方法接受五个参数:x,y 是圆心坐标,radius 是半径,startAngle 和 endAngle 分别是起末弧度(以 x 轴为基准),anticlockwise 为 true 表示逆时针,反之顺时针。
注意:arc 方法里用到的角度是以弧度为单位而不是度。度和弧度直接的转换可以用这个表达式:var radians = (Math.PI/180)*degrees;
画二次贝塞尔曲线:quadraticCurveTo(cp1x, cp1y, x, y)
画三次贝塞尔曲线:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
  贝塞尔曲线 ,它可以是二次和三次方的形式,一般用于绘制复杂而有规律的形状。它们都有一个起点一个终点(下图中的蓝点),但二次方贝塞尔曲线只有一个(红色)控制点点)而三次方贝塞尔曲线有两个。
  参数x和y是终点坐标,cp1x和 cp1y是第一个控制点的坐标,cp2x和cp2y是第二个的。
  使用二次方和三次方的贝塞尔曲线是相当有挑战的,因为缺少直观性。理论上只要有耐心,再复杂的图形都可以绘制出来的。


 

 

 

 

 

 

 

绘制矩形路径:context.rect(x, y, width, height)
  当rect方法被调用时,moveTo方法会自动被调用,参数为(0,0),于是起始坐标又恢复成初始原点了。
4.关闭路径
方法:使用context.closePath()关闭路径。

  该方法它会尝试用直线连接当前端点与起始端点来关闭路径,但如果图形已经关闭或者只有一个点,它会什么都不做,这一步在某些情况并不是必须的,比如使用fill()绘制实际图形的时候,就不需要先调用closePath。
5.绘制路径到canvas
画图形边框:context.stroke()
填充实心图形:context.fill()

最后这一步是调用 stroke或 fill方法,这时,图形才是实际的绘制到 canvas 上去。

调用stroke之前必须要先调用关闭路径方法closePath。而调用fill之前不需要调用closePath关闭路径,调用fill时路径会自动闭合。

一个复杂的例子如下:

function draw() {
  var ctx = document.getElementById('lesson01').getContext('2d');
  roundedRect(ctx,12,12,150,150,15);
  roundedRect(ctx,19,19,150,150,9);
  roundedRect(ctx,53,53,49,33,10);
  roundedRect(ctx,53,119,49,16,6);
  roundedRect(ctx,135,53,49,33,10);
  roundedRect(ctx,135,119,25,49,10);

  ctx.beginPath();
  ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false); //chiensexu  ???true??,??
  ctx.lineTo(31,37);
  ctx.fill();
  for(i=0;i<8;i++){
    ctx.fillRect(51+i*16,35,4,4);
  }
  for(i=0;i<6;i++){
    ctx.fillRect(115,51+i*16,4,4);
  }
  for(i=0;i<8;i++){
    ctx.fillRect(51+i*16,99,4,4);
  }
  ctx.beginPath();
  ctx.moveTo(83,116);
  ctx.lineTo(83,102);
  ctx.bezierCurveTo(83,94,89,88,97,88);
  ctx.bezierCurveTo(105,88,111,94,111,102);
  ctx.lineTo(111,116);
  ctx.lineTo(106.333,111.333);
  ctx.lineTo(101.666,116);
  ctx.lineTo(97,111.333);
  ctx.lineTo(92.333,116);
  ctx.lineTo(87.666,111.333);
  ctx.lineTo(83,116);
  ctx.fill();
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.moveTo(91,96);
  ctx.bezierCurveTo(88,96,87,99,87,101);
  ctx.bezierCurveTo(87,103,88,106,91,106);
  ctx.bezierCurveTo(94,106,95,103,95,101);
  ctx.bezierCurveTo(95,99,94,96,91,96);
  ctx.moveTo(103,96);
  ctx.bezierCurveTo(100,96,99,99,99,101);
  ctx.bezierCurveTo(99,103,100,106,103,106);
  ctx.bezierCurveTo(106,106,107,103,107,101);
  ctx.bezierCurveTo(107,99,106,96,103,96);
  ctx.fill();
  ctx.fillStyle = "black";
  ctx.beginPath();
  ctx.arc(101,102,2,0,Math.PI*2,true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(89,102,2,0,Math.PI*2,true);
  ctx.fill();
}

function roundedRect(ctx,x,y,width,height,radius){
  ctx.beginPath();
  ctx.moveTo(x,y+radius);
  ctx.lineTo(x,y+height-radius);
  ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
  ctx.lineTo(x+width-radius,y+height);
  ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
  ctx.lineTo(x+width,y+radius);
  ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
  ctx.lineTo(x+radius,y);
  ctx.quadraticCurveTo(x,y,x,y+radius);
  ctx.stroke();
}

结果如下:

 

实用参考:
官方参考文档以及API详细说明:http://www.whatwg.org/specs/web-apps/current-

work/multipage/the-canvas-element.html
权威开发入门:https://developer.mozilla.org/cn/Canvas_tutorial

 

posted @ 2012-02-28 12:18  沙场秋点兵  阅读(1482)  评论(0编辑  收藏  举报