本节主要解决的有三个问题,求任意几何图形的外接矩形。这个外接矩形会在性能优化时候用到,当然不仅仅只在这一方面使用。最后对初步优化过的渲染器进行压力测试。
还是先上demo,之前的demo在firefox下滚动缩放漏做了。现在更新的可以再ie9,chrome,firefox中使用(当然需要支持canvas)。
1.第一个demo表示了几何图形的外接矩形。大家发现点和图片没有外接矩形,因为他们的范围表示成一个点,均为中心点。
为什么图片也没有范围呢?因为异步的图片加载使我们无法在一开始就得知图片的宽高。
添加点添加圆添加线添加面添五角星
2.第二个demo为性能测试demo,点击测试按钮会随机添加500个点和500个五角星,多次点击多次添加(大家悠着些啊,别把浏览器点崩了。。)。
可以发现在我们放大2倍左右后,性能还是比较不错的,因为这里做了个小优化,通过上面我们取得的外接矩形和当前视图的范围做相交比较,只有和当前视图范围相交的几何图形才会被绘制。
测试添加500个点和500个五角星
在渲染器中规定所有继承自geometry基类的几何类型必须要有一个计算其范围的方法(getBounds),当然我们的几何图形不同,计算范围的方法也就不同。
Circle.prototype.getBounds=function(){if(!this.bounds){this.bounds=newCanvasSketch.Bounds(this.x-this.radius,this.y-this.radius,this.x+this.radius,this.y+this.radius);returnthis.bounds;}else{returnthis.bounds;}}大家一看代码就明白了,这不是初中、小学数学么。。
在现在所有的几何图形,除了point(点)、circle(圆)、img(图像)是直接继承自geometry基类的其余的均继承自line。这样只要我们得到line计算范围的方法就可以解决所有的问题了。
Line.prototype.getBounds=function(){if(!this.bounds){varp0=this.points[0];this.bounds=newCanvasSketch.Bounds(p0.x,p0.y,p0.x,p0.y);for(vari=1,len=this.points.length;i extend方法如下: CanvasSketch.Bounds.prototype.extend=function(bounds){if(this.left>bounds.left){this.left=bounds.left;}if(this.bottom>bounds.bottom){this.bottom=bounds.bottom;}if(this.right 通过上面的两个方法就让我们获得了所有几何图形的外接矩形,下面我们讨论如何用外接矩形优化性能。 本次的优化其实非常简单,也是所有图形库必须的优化:绘制能看见的图形,其余的不进行考虑。在图形学中称这个过程为“二维裁剪”,有兴趣的同学可以baidu一下。 有了范围后,我们需要一个判断两个范围相交的函数: CanvasSketch.Bounds.prototype.intersect=function(bounds){varinBottom=(((bounds.bottom>=this.bottom)&&(bounds.bottom<=this.top))||((this.bottom>=bounds.bottom)&&(this.bottom<=bounds.top)));varinTop=(((bounds.top>=this.bottom)&&(bounds.top<=this.top))||((this.top>bounds.bottom)&&(this.top Canvas.prototype.redraw=function(){this.context.clearRect(0,0,this.layer.size.w,this.layer.size.h);vargeometry;if(!this.lock){for(varidinthis.geometrys){geometry=this.geometrys[id][0];varbounds=geometry.getBounds();if(this.layer.bounds.intersect(bounds)){style=this.geometrys[id][1];this.draw(geometry,style,geometry.id);}}}}我们在绘制每一个图形之前,判断其范围是否和当前视图范围有相交,如果相交才绘制,不相交什么也不做了。 这样我们基本的优化就做完了,计划以后有更高级的优化,现在还在斟酌方案。 //CLASS:显示图像类。functionImg(point,image){Geometry.apply(this,arguments);this.point=point;if(typeofimage==Image){this.useUrl=false;this.image=image;}else{this.useUrl=true;this.image=image;}}Img.prototype=newGeometry();Img.prototype.geoType="Img";Img.prototype.getBounds=function(){returnnewCanvasSketch.Bounds(this.point.x,this.point.y,this.point.x,this.point.y);}我们这里支持直接传入image对象(前提是你必须保证他已经请求完成)。我们还可以传入图像的url,程序内部帮大家请求图片;在请求完成后图片将被设置为Img对象的一个资源,以后再使用的时候就不用再次请求了。 //针对图片的绘制方法。Canvas.prototype.drawImage=function(geometry,style,id){varcanvas=this;if(!geometry.useUrl){varimg=geometry.image;imageLoad();}else{varimg=newImage();img.onload=imageLoad;img.loadErro=imageErro;img.src=geometry.image;}functionimageLoad(){canvas.setCanvasStyle("fill",style);varfixedSize=style.fixedSize;varpt=canvas.getLocalXY(geometry.point);varwidth=style.width||img.width;varheight=style.width||img.height;if(fixedSize){varoffsetX=width/2;varoffsetY=height/2;canvas.context.drawImage(img,pt.x-offsetX,pt.y-offsetY,width,height);}else{varres=canvas.layer.getRes();varoffsetX=width/2/res;varoffsetY=height/2/res;canvas.context.drawImage(img,pt.x-offsetX,pt.y-offsetY,width/res,height/res);}if(geometry.useUrl){geometry.useUrl=false;geometry.image=img;}canvas.setCanvasStyle("reset");}functionimageErro(){}}这里我们使用了一个闭包的小技巧,我们用局部变量canvas保存了对this的引用(在图片加载完成后this已经指向图像本身了)。 添加了图像加载完成事件,其中我们首先将图像绘制到canvas画布上(这里我们添加了一个style属性:fixedSize;如果fixedSize设置为true,我们就不对图像进行缩放了)。 之后将图像作为一个资源保存起来。 这样我们的图像类也就制作完成了。 尝试一下:下载这次的源码,修改里面的demo开开能否有新的发现(bug?呵呵~)