大家好,我是前端西瓜哥。在上一篇文章中,我们讨论了使用脏矩形渲染通过重新渲染本地图形来优化Canvas的性能,将GPU密集型转换为CPU密集型。CPU密集型在哪里?当你需要遍历所有图形时,判断它们是否与脏矩形相交(碰撞),把已经帮你抓取的图形保存下来,在本地重新绘制。有没有办法减少需要遍历的图形,不是遍历所有图形,而是遍历少量图形?一种方法是使用四叉树。四叉树碰撞检测原理我们将区域的划分表示为“节点”,因为它是四叉树;画布上真实的图形称为“图形”。四叉树本质上是利用空间分割对图形进行索引,将视口界面划分为多个区域,每个区域记住自己包含了哪些图形。然后在移动目标图形的时候,判断它落在哪个区域,取出该区域的图形。这些图形集合是与目标图形相冲突的图形的超集。这些区域之外的图形被我们排除在外。算法实现要点:创建根节点,根节点保存区域的x、y、宽、高信息。添加图形时,当一个节点中的节点数大于阈值时,将整个区域平均切割成4个相等的子节点,将图形从当前区域中取出,并放回这些子节点中,使节点的归属地划分成更小的区域。(原来的区域转换为索引层,真正保存节点的地方放在它的子区域上)我们提供碰撞矩形的时候,从四叉树的最顶层节点往下看,看有没有子节点。如果是,则使用矩形碰撞算法找出它在哪些子节点中(可能不止一个)。对这些子节点重复前面的操作,递归,找到所有的图。这些图形是碰撞矩形可能相交的矩形,但相对于所有图形来说不会太多。四叉树碰撞检测算法首先看经典算法的实现。算法我就不自己实现了,这里是quadtree-js库的代码实现。https://github.com/timohausmann/quadtree-js。构造函数:functionQuadtree(bounds,max_objects,max_levels,level){this.max_objects=max_objects||10;//一个节点中的最大对象数this.max_levels=max_levels||4;//树的最大深度this.level=level||0;this.bounds=边界;//面积,结构为{x,y,width,height}this.objects=[];//保存图形的地方this.nodes=[];//4个子节点,只有达到阈值时才会创建}这是一个内部私有方法。当节点中的图过多超过阈值时,会将当前节点拆分为4个子节点://Cut:generate4sub-nodesQuadtree.prototype.split=function(){varnextLevel=this.level+1,subWidth=this.bounds.width/2,subHeight=this.bounds.height/2,x=this.bounds.x,y=this.bounds.y;//右上角this.nodes[0]=newQuadtree({x:x+subWidth,y:y,width:subWidth,height:subHeight,},this.max_objects,this.max_levels,nextLevel);//左上角this.nodes[1]=newQuadtree({x:x,y:y,width:subWidth,height:subHeight,},this.max_objects,this.max_levels,nextLevel);//左下角this.nodes[2]=newQuadtree({x:x,y:y+subHeight,width:subWidth,height:subHeight,},this.max_objects,this.max_levels,nextLevel);//右下this.nodes[3]=newQuadtree({x:x+subWidth,y:y+subHeight,width:subWidth,height:subHeight,},this.max_objects,this.max_levels,nextLevel);};计算一个图落在当前节点的哪个子节点上,得到对应节点的Index值数组://Findanbox落在哪个区域Quadtree.prototype.getIndex=function(pRect){varindexes=[],verticalMidpoint=this.bounds.x+this.bounds.width/2,horizo??ntalMidpoint=this.bounds.y+this.bounds.height/2;varstartIsNorth=pRect.y
