您的位置:首页 > Web前端 > JavaScript

【js学习笔记-110】--------<canvas>中的图形

2014-04-01 17:59 489 查看

<canvas>中的图形

<canvas>元素自身是没有任何外观的,但是它在文档创建了一个画板,同时还提供很多强大的绘制客户端js的API。尽管canvas元素在html5中才标准化,但实际上它很早就存在了。不过IE9之前的浏览器不支持canvas元素,但是可以使用开源ExplorerCanvas项目在IE6~8中模拟<canvas>元素。(http://code.google.com/plexplorercanvas/)

<canvas>元素和svg之间的一个重要区别是:使用canvas来绘制的图形是通过调用它提供的方法而使用SVG绘制图形是通过构建一棵XML元素树来实现的。这两种方式都很强大:两者之间都可以互相模拟。但是,从表面上看,这两者还是不同的,并且各有优劣。比如:使用SVG来绘制,要移除图片中的元素就不得不把当前的擦除再重新绘制一遍。canvas的绘制api是基于js的,并且相对比较简洁。

大部分的画布绘制API都不是在<canvas>元素自身上定义,而是定义在一个“绘制上下文”对象上,获取该对象可以通过调用画布的getContext()方法。调用getContext()传入”2d”参数,会获得一个CanvasRenderingContext2D对象,使用该对象可以在画布上绘制二维图形。这里很重要一点是要搞清楚,画布元素和它的上下文对象是两个完全不同的对象。由于CanvasRenderingContext2D名字太长,因此这里做约定,统一简称“上下文对象“。

画布中的3D图形的API。这些API称:“WebGL”,它是绑定到OpenGL标准API的一个javascript。将“webgl”字符串作为参数传递给画布的getContext()方法可以获得用于绘制3D图形的上下文对象。由于WebGL很庞大,而且非常复杂。Web开发者也更倾向于使用使用封闭了WebGL底层API的工具类库不喜欢直接使用WebGL API

绘制线段和填充多边形

要在画布上绘制线断以填充这线线段的闭合区域,从定义一条路径开始。路径有许多子路径组成,子路径又是由两个或多个点之间连接而成的线段组成。调用beginPath()方法开始定义一条新的路径,而调用moveTo()方法则开始定义一条新的子路径。一理使用moveTo()方法确定了子路径的起点,接下来就可以调用lineTo()方法来将该点与新的一个点通过直线连接起来。如:

c.beginPath();

c.moveTo(100,100); //从(100,100)开始定义一条新的子路径

c.lineTo(200,200); //(100,100)到(200,200)绘制一条线段

c.lineTo(100,200); //从(200,200)到(100,200)绘制一条线段

上述代码只是简单定义了一条路径,并没有在画布上绘制任何图形。要在路径中绘制两条线段,可以通过调用stroke()方法,要填充这些线段闭合的区域可以通过调用fill()方法

c.fill();

c.stroke();

要注意的是上述定义的子路径是“未闭合”的。它只包含两条线段,线段的终点并滑 和起点汇合。也就是,它并没有闭合一个区域。对于这样的“未闭合”的子路径,调用fill()方法填充的时候,会假设子路径的终点和子路径的起点连接起来。这就是为什么,上述代码填充成了一个三角形,但是只勾勒了三角形的两条边。

要想勾勒出上述三角形的三条边,可以调用cloasePath()方法将子路径的起点和终点真正连接起来;还可以调用lineTo(100,100),但是,这样的话,最终表现出来的只是三条线段共用一个起点和一个终点,但并未真正闭合。因此,当绘制比较粗的线段时,如果使用closePath()方法视觉效果会理好。

关于stoke()方法和fill()方法还有另上非常重要的两点

1. 这两个方法都是作用在当前路径上的所有子路径。

2. stroke()方法fill()方法都不更改当前路径。调用fill()方法,但是之后调用stroke()方法时候当前路径不变。完成一条路径后要再重新开始另一条路径,必须要记得调用beginPath()方法。如果没有调用beginPath()方法,那么之后添加的所有子路径都是在已有路径上,并且有可能重复绘制这些子路径。

//使用moveTo() lineTo()closePath()方法绘制规则多边形

//定义一个以(x,y)为中心,半径为r的规则多边形

//将第一个顶点放置在最上面,或者指定一定角度

//除非最后一个参数是true,否则顺时针旋转

functionpolygon(c,n,x,y,r,angle,counterclockwise){

angle = angle || 0;

counterclockwise = counterclockwise ||false;

c.moveTo(x+r*Math.sin(angle),y-r*Math.cos(angle));

var delta = 2*Math.PI/n;

for(var i=1;i<n;i++){

angle+=counterclockwise?-delta:delta;

c.lineTo(x+r*Math.sin(angle),y-r*Math.cos(angle));

}

c.closePath(); //将最后一个顶点和起点连接起来

}

//开始一个新的路径并添加一条多边形路径

var c =document.getElementById("test1").getContext("2d");

// alert(c);

c.beginPath();

polygon(c,3,50,70,50); //三角开

polygon(c,4,150,60,50,Math.PI/4); //正方形

polygon(c,5,255,55,50); //正方形

polygon(c,6,365,53,50,Math.PI/6); //六边形

polygon(c,4,365,53,50,20,Math.PI/6,true);//六边形中的小正方形

c.fillStyle = "#ccc";

c.strokeStyle = "#008";

c.lineWidth=5;

c.fill();

c.stroke();

绘制了一个内部包含正方形的六边形。正方形和六边形是两条独立的子路径,但它们互相重叠。当出现该情况时,画布需要能够确定哪些区域在路径里面。哪些在外面。画布采用“非零绕数原则”测试来判断它们。由于六边形和正方形绘制的方向不同:六边形的顶点是沿着顺时针方向来连接的,而正方形顶点则是沿着逆时针连接的,因此根据“非零绕数原则”,对内部的正方形不进行填充。换句话说,如果正方形也沿着顺时针方向连接的话,调用fill()方法的时候就会对正方形也进行填充了。

非零绕数原则:想象一条从点p出发没着任意方向无限延伸的射线。现在从0开始初始化一个计数器,然后对所有穿过这条射线的路径进行枚举。每当一条路径顺时针方向穿过射线的时候,计数器就加1;反之,就减1,最后枚举完所有的路径之后,如果计数器的值不是0,那么就认为p是在路径内。反之,如果计数器值是0,则认为p在路径 外。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: