【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在路径 外。
相关文章推荐
- 【js学习笔记-111】-----<canvas>图形属性
- 【js学习笔记-113】------<canvas>绘制和填充曲线
- 【js学习笔记-112】------<canvas>坐标系变换
- 【js学习笔记-114】----<canvas>矩形、颜色、透明度、渐变及图案
- <了不起的NODEJS>学习笔记
- Windows学习笔记10——图形基础<二>
- 【js学习笔记-101】------借助<script>发送HTTP请求
- Cocos2d-js 学习笔记<一> 运行Hello World
- python基础学习笔记<数据库>
- React-native学习笔记之<BackAndroid>
- cocos2dx lua学习笔记 <一> quick 3.5定义本身C++类是必然lua
- [原]java专业程序代写(qq:928900200),学习笔记之基础入门<DOM解析>(三十二)
- Linux学习笔记之<Shell编程初入门>
- LDAP学习笔记<一>关于LDAP协议及其基本概念
- win32汇编 屏幕截图保存BMP 学习笔记<第四篇>之生成BMP文件
- windows学习笔记3——窗口和消息<一>
- JavaSE初学笔记之<nio的学习>
- osworkflow学习笔记-标签<initial-actions>、<restrict-to>、beanshell、class
- cmake 学习之路 笔记<一>