构建Canvas矢量图形渲染器(四)—— 各种矢量元素(线、面、五角星)
这次随笔接着上次的内容继续,为我们的geometry类添加更多的矢量元素。
(目录上线了,大家如果对这个系列了解不是很多请看目录!)
其实,通过设计好的架构我们很容易通过geometry基类扩展新几何类型。
本次随笔内容不是很多,先上demo。
1.增加各种几何对象。
1.几何类型:线(Line)
线也是一继承自Geometry的类。简单的来说,线其实就是一点数组的集合。
//CLASS:几何对象线类。 function Line(points) { Geometry.apply(this, arguments); this.points = points; } Line.prototype = new Geometry(); Line.prototype.geoType = "Line";
简单不?一个线的几何类型就这么4、5行代码就搞定了~
2.几何类型:面(LinerRing)
面也可以称作是一条封闭的线,这么我们的面继承自线,并对线做了一些修改。
//CLASS:几何对象封闭线类 function LinerRing(points) { Line.apply(this, arguments); if(points) { this.points = points; var len = this.points.length; if(this.points[0].x != this.points[len-1].x || this.points[0].y != this.points[len-1].y) { this.points.push(this.points[0].clone()); } } } LinerRing.prototype = new Line(); LinerRing.prototype.geoType = "LinerRing";
是不是也很easy,判断线的首尾是否相连(最后一个点的x,y值是否和第一个点相等)。如果不相等则克隆第一个点,并添加到数组最后。
3.扩展几何类型:五角星(Star)
//CLASS:几何图形五角星 function Star(center, r) { //中心点 this.center = center; //五角星的长半径 this.r = r; var points = this.getPoints(center, r); LinerRing.call(this, points); } Star.prototype = new LinerRing(); Star.prototype.getPoints = function(center, r) { var point, points = []; var angle = 0; var degree = Math.PI / 180; for(var i = 0; i < 10; i++) { var radius = (i % 2 == 0)? r : r/2; point = new Point(center.x + Math.sin(angle * degree) * radius, center.y + Math.cos(angle * degree) * radius); points.push(point); angle+=36; } return points; } Star.prototype.geoType = "LinerRing";
五角星的具体画法这里就不详细阐述了,简单说下就是定义一个长半径r,其短半径这里设为r/2。和画圆的方法一样,交替使用长半径和短半径绘制其顶点,这样就画出来五角星的各个顶点了。其实五角星也就是LinerRing,所以这里他也继承自LinerRing。
说完这几个基本类型,下面我们就再说说如何在渲染器中解析这几种几何类型。
2.渲染器端解析几何类型
1.增加渲染的种类
//每一个矢量元素的绘制,这里我们在以后会添加更多的矢量图形。 Canvas.prototype.draw = function(geometry, style, id){ if(geometry.geoType == "Point"){ this.drawPoint(geometry, style, id); } if(geometry.geoType == "Circle") { this.drawCircle(geometry, style, id); } if(geometry.geoType == "Line") { this.drawLine(geometry, style, id); } if(geometry.geoType == "LinerRing") { this.drawLinerRing(geometry, style, id); } //{todo} 我们在这里判断各种矢量要素的绘制。 }
2.绘制线
Canvas.prototype.drawLine = function(geometry, style, id) { this.setCanvasStyle("stroke", style); this.rendererPath(geometry, {fill: false, stroke: true}, id); this.setCanvasStyle("reset"); }
由于线只需要调用stroke方法,则我们这里只为其设置stroke的样式。
rendererPath这个方法一会儿和绘制面一起说。
3.绘制面
Canvas.prototype.drawLinerRing = function(geometry, style, id) { if(style.stroke) { this.setCanvasStyle("stroke", style); this.rendererPath(geometry, {fill: false, stroke: true}, id); } if(style.fill) { this.setCanvasStyle("fill", style); this.rendererPath(geometry, {fill: true, stroke: true}, id); } this.setCanvasStyle("reset"); }
绘制面的时候我们会根据style的设定是否需要stroke、fill来调用不同的设置样式的方式,并调用rendererPath方法。
3.rendererPath
Canvas.prototype.rendererPath = function(geometry, rendererType, id) { var points = geometry.points; var len = points.length; var context = this.context; context.beginPath(); var start = this.getLocalXY(points[0]); var x = start.x; var y = start.y; if (!isNaN(x) && !isNaN(y)) { context.moveTo(x, y); for (var i=1; i<len; ++i) { var pt = this.getLocalXY(points[i]); context.lineTo(pt.x, pt.y); } if (rendererType.fill) { context.fill(); } if (rendererType.stroke) { context.stroke(); } } }
其实不管是线还是面,都是对一段path进行渲染的。无非区别在于一个不需要填充、一个需要。
那么我们在调用这个函数的时候传入rendererType来区分是否需要填充。
本节的内容就到这里。到目前为止一个矢量渲染器的大体结构就已经完成。后面要做的就是将绘制点、绘制面、绘制线段添加为鼠标点击支持的形式,通过扩展geometry可以得到更多的几何图形。
尝试一下:大家下载源码后,可以扩展geometry类型为其添加一个矩形图形类。
下次随笔预告:1.将添加图片的支持。
2.初步性能优化。
本次随笔的所有源码+demo,请点击下载。