突袭HTML5之Canvas 2D入门2 - Canvas绘制图形
canvas只支持一种基本形状——矩形,所有其它形状都是通过一个或多个路径组合而成,甚至是基本的矩形也可以通过路径组合成。
一、设置画笔属性
设想我们生活中画图的样子,我们首先是选取合适的颜料和笔,一样的道理,在canvas中画图同样也是根据需要,不断的去设置当前使用的颜色和线条类型。
设置当前使用的颜色
任何封闭的图形都是有轮廓部分和填充部分组成。设置当前的颜色也是分两部分设置:
- 设置填充色:context.fillStyle = color
- 设置轮廓色:context.strokeStyle = color
参数color可以是表示CSS颜色值的字符串,渐变对象或者图案对象。默认情况下,线条和填充颜色都是黑色(CSS颜色值#000000)。
颜色的字符串表示
下面都是正确的值:
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。例子如下所示:
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属性。
例子如下所示:
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属性。
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.shadowOffsetY = float
context.shadowBlur = float
context.shadowColor = color
shadowOffsetX和shadowOffsetY用来设定阴影在X和Y轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,他们默认都是0。
shadowBlur用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为0。
shadowColor用于设定阴影效果的延伸,值可以是标准的CSS颜色值,默认是全透明的黑色。
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。
例子如下:
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 是矩形的宽和高。
除了这种方式,还可以使用绘制路径的方式绘制矩形,这个参看路径绘图部分。
绘制矩形的例子如下:
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时路径会自动闭合。
一个复杂的例子如下:
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