当您按方向键时,电视如何找到下一个焦点?里面很多页面都是H5开发的。我们都知道电视是通过遥控器操作的,没有鼠标是无法触摸屏幕的,所以“点击”操作就变成了按遥控器上的“上下左右”键,所以一个“焦点”"必须需要告诉用户当前焦点在哪里。当时开发页面用的是前人开发的焦点库,会监听方向键,自动计算下一个焦点元素。为什么时隔多年突然想到这个?事实上,当我最近在我的开源思维导图中添加箭头键导航功能时,我认为它实际上与电视焦点功能非常相似。它是通过按箭头键计算并自动聚焦的。转到下一个元素或节点:那么如何找到下一个焦点呢,结合我当时使用的焦点库的原理,下面来实现一下。1.最简单的算法第一种算法是最简单的。根据方向,先找到当前节点所在方向的所有其他节点,再找到直线距离最近的那个。例如,当按下左箭头键时,后面的节点都是满足要求的节点:选择最近的一个作为下一个关注的节点。节点的位置信息如下所示:focus(dir){//当前获得焦点的节点letcurrentActiveNode=this.mindMap.renderer.activeNodeList[0]//当前获得焦点的节点的位置信息letcurrentActiveNodeRect=this.getNodeRect(currentActiveNode)//下一个要查找的焦点节点lettargetNode=nulllettargetDis=Infinity//保存并维护最近的节点letcheckNodeDis=(rect,node)=>{letdis=this.getDistance(currentActiveNodeRect,rect)if(dis{//跳过当前获得焦点的Nodeif(node===currentActiveNode)return//当前遍历的节点的位置信息letrect=this.getNodeRect(node)let{left,top,right,bottom}=rectletmatch=false//按下左方向键if(dir==='Left'){//判断节点是否在当前节点的左侧match=right<=currentActiveNodeRect.left//按右方向键}elseif(dir==='Right'){//判断节点是否在当前节点的右侧match=left>=currentActiveNodeRect.right//按向上方向键}elseif(dir==='Up'){//判断节点是否在当前节点之上match=bottom<=currentActiveNodeRect.top//按下方向键}elseif(dir==='Down'){//判断n是否ode低于当前节点match=top>=currentActiveNodeRect。底部}//满足要求,判断是否是最近的节点,比如按下向上按钮,我们期望关注上兄弟节点,但实际上关注的是子节点:因为这个子节点确实在当前节点上并且距离最近,那么如何解决这个问题,我们来看一个再看算法2.ShadowAlgorithm该算法同样是对四个方向分别进行处理,但是与上面第一种算法相比,额外要求节点在指定方向的延伸需要有交叉,延伸可以想象成的影子thenode,也就是名字的来源:找到所有有交集的节点后,找到距离最近的节点作为下一个聚焦节点,修改聚焦方法,使用影子算法:focus(dir){//当前聚焦节点letcurrentActiveNode=this.mindMap.renderer.activeNodeList[0]//当前聚焦节点的位置信息letcurrentActiveNodeRect=this.getNodeRect(currentActiveNode)//下一个要查找的聚焦节点//...//保存并维护最近的node//...//2.阴影算法this.getFocusNodeByShadowAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis})//如果找到,让目标节点聚焦if(targetNode){targetNode.active()}}//2.影子算法getFocusNodeByShadowAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis}){bfsWalk(this.mindMap.renderer.root,node=>{if(node===currentActiveNode)returnletrect=this.getNodeRect(node)let{左,上,右,下}=rectletmatch=falseif(dir==='Left'){match=leftcurrentActiveNodeRect.top}elseif(dir==='Right'){match=right>currentActiveNodeRect.right&&topcurrentActiveNodeRect.top}elseif(dir==='Up'){match=topcurrentActiveNodeRect.left}elseif(dir==='Down'){match=bottom>currentActiveNodeRect.bottom&&leftcurrentActiveNodeRect.left}if(匹配){checkNodeDis(rect,node)}})}是加入判断条件是否越过的比较。效果如下:可以看到影子算法成功解决了之前的跳跃问题,但还不够完美。比如下面这种情况,按左方向键找到Nodes是可以聚焦的:因为左边没有交集节点,但是实际上可以聚焦到父节点上,怎么办呢,我们来看一下下一个算法优先3.面积算法所谓面积算法也很简单。将当前关注节点的周边区域划分为四个区域,分别对应四个方向。找哪个方向的下一个节点,先找到中心点在这个区域的所有节点,然后选择距离最近的就选:focus(dir){//当前聚焦的节点letcurrentActiveNode=this.mindMap.renderer.activeNodeList[0]//当前聚焦节点的位置信息letcurrentActiveNodeRect=this.getNodeRect(currentActiveNode)//寻找下一个聚焦节点//...//保存并维护最近的节点//...//3.区域算法this.getFocusNodeByAreaAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis})//找到然后让目标节点聚焦if(targetNode){targetNode.active()}}//3.区域算法getFocusNodeByAreaAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis}){//当前焦点节点的中心点letcX=(currentActiveNodeRect.right+currentActiveNodeRect.left)/2letcY=(currentActiveNodeRect.bottom+currentActiveNodeRect.top)/2bfsWalk(这个。mindMap.renderer.root,node=>{if(node===currentActiveNode)returnletrect=this.getNodeRect(节点)let{left,top,right,bottom}=rect//遍历节点的中心点letccX=(right+left)/2letccY=(bottom+top)/2//中心点的坐标该节点与当前关注节点的中心点坐标之差letoffsetX=ccX-cXletoffsetY=ccY-cYif(offsetX===0&&offsetY===0)returnletmatch=falseif(dir==='左'){match=offsetX<=0&&offsetX<=offsetY&&offsetX<=-offsetY}elseif(dir==='右'){match=offsetX>0&&offsetX>=-offsetY&&offsetX>=offsetY}elseif(dir==='Up'){match=offsetY<=0&&offsetY0&&-offsetYoffsetX}if(match){checkNodeDis(rect,node)}})}比较逻辑可以参考下图:结合阴影算法和面积算法介绍阴影算法的时候说它有一定的局限性,面积算法计算的结果可以补充,但是理想情况下,阴影算法的结果是最符合我们预期的,所以很简单,我们可以把它们组合在一起,调整顺序,先用影子算法计算节点,如果影子算法没有找到,再用面积算法找节点,简单的算法也可以加在end:focus(dir){//当前聚焦节点letcurrentActiveNode=this.mindMap.renderer.activeNodeList[0]//当前聚焦节点位置信息letcurrentActiveNodeRect=this.getNodeRect(currentActiveNode)//下一个聚焦节点find//...//保存并维护最近的节点//...//第一优先级:阴影算法this.getFocusNodeByShadowAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis})//第二优先级:区域算法if(!目标Node){this.getFocusNodeByAreaAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis})}//第三优先级:简单算法if(!targetNode){this.getFocusNodeBySimpleAlgorithm({currentActiveNode,currentActiveNodeRect,dir,checkNodeDis})}//如果找到,聚焦目标节点if(targetNode){targetNode.active()}}效果如下:是不是很简单,可以点击思维导图详细体验