上一篇HTML5Canvas(实战:绘制饼图)后,笔者研究了如何添加鼠标悬停在饼图上时显示的提示框。在开始Plot对象的Coding之前,我能想到的最简单的方法就是给饼图的每个区域添加一个mousemove事件,当鼠标移到上面时,就会显示相应的提示框,soeasy!但事实并不是这样的~我们肉眼看起来就像一块一块的东西,canvas并没有真正把它们分成一块块的HTMLElement,我们只能给canvas绑定事件。那么如何知道鼠标当前停留在哪个区域呢?您可以计算鼠标位置与连接圆心和参考线的线之间的角度是否在区域的起始角度和结束角度之间。为此,我们需要保存每个区域的角度信息。为了保存方便,创建构造函数Plot。功能情节(开始,结束,颜色,数据){this.start=start;this.end=结束;this.color=颜色;this.data=data;}可以使用上一篇中的绘制图例方法和绘制饼图区域的方法放入Plot的原型链中。Plot.prototype.drawLegend=function(){ctx.fillRect(legend_posX,legend_posY,legend_width,legend_height);ctx.font='粗体12pxArial';varpercent=this.data.label+':'+(this.data.portion*100).toFixed(2)+'%';ctx.fillText(percent,legend_textX,legend_textY);}Plot.prototype.drawPlot=function(){ctx.fillStyle=this.color;ctx.beginPath();ctx.moveTo(center.x,center.y);ctx.arc(center.x,center.y,radius,this.start,this.end,false);ctx.closePath();ctx.fill();}自定义Tooltip在上一篇HTML5Canvas(实战:绘制饼图)中可以看到,在我们最初的设计中,Tooltip显示的内容是可以自定义的,用户可以设置一个模板如下:Year:{{year}},Data:{{data}}我们的目标是将上面的模板转换成:Year:2017,Data:3000新建一个工具方法,接受模板字符串,数据在鼠标当前停留的图中,返回实际显示的字符串:functionreplaceAttr(text,data){while(text.indexOf("{{")!=-1){varstart=text.indexOf("{{"),end=text.indexOf("}}"),attr=text.substring(start+2,end);text=text.replace("{{"+attr+"}}",data[attr]);}returntext;}注意,从代码中可以看出,{{和}}之间不要习惯性的加空格鼠标在哪?为了判断鼠标停留的区域,我们需要完成以下两步:计算鼠标位置与圆心的弧度角,遍历绘图判断该角度是否在startAngle和endAngle之间某个情节。如果找到该地块,则判断该地块是否为鼠标上次所在的区域。如果是,说明不需要绘制Tooltip,如果不是,则重新绘制图表。如果没有找到相应的区域,说明鼠标不在画布的饼图区域,可能指向图例、标题或空白区域。这时应该清除全局变量currentPlot,重新绘制画布。关于如何判断鼠标位置与圆心的弧度,小编画了如下饼图,只能帮到这里了……functiongetAngle(cx,cy,mx,my){varx=Math.abs(cx-mx),y=Math.abs(cy-my),z=Math.sqrt(Math.pow(x,2)+Math.pow(y,2)),cos=y/z,radina=数学.acos(cos);if(mx>cx&&my>cy){返回radina;}elseif(mxcy){returnMath.PI/2+radina;}elseif(mx>cx&&myangle){如果(currentPlot!=plots[i]){currentPlot=地块[i];画();}返回;}}currentPlot=null;画();}现在我们知道了鼠标当前停留的位置,我们也可以自定义提示的文字。现在我们可以绘制提示框了。下面的代码有点繁琐,在计算过程中出现了一些问题。我改天再计算~Plot.prototype.drawTooltip=function(){vartext=replaceAttr(op.tooltip.template,this.data);varwidth_tooltipText=ctx.measureText(text).width,height_tooltipText=parseInt(op.tooltip.font.size,10),angle=(this.start+this.end)/2/(2*Math.PI)*360;vartan=Math.tanh(角度),x=0,y=0;if(angle<90)((x=radius/2*tan+center.x)||true)&&((y=-radius/2+center.y)||true)elseif(angle>90&&angle<180)((x=radius/2*tan+center.x)||true)&&((y=radius/2+center.y)||true)elseif(angle>180&&angle<270)((x=-radius/2*tan+center.x)||true)&&((y=radius/2+center.y)||true)elseif(angle>270&&angle<360)((x=-radius/2*tan+center.x)||true)&&((y=-radius/2+center.y)||true)vartooltip_box_x=x-radius/4,tooltip_box_y=y,tooltip_box_width=width_tooltipText+10,tooltip_box_height=height_tooltipText+10,tooltip_text_x=x-radius/4+5,tooltip_text_y=2+;ctx.fillStyle='白色';ctx.fillRect(tooltip_box_x,tooltip_box_y,tooltip_box_width,tooltip_box_height);ctx.fillStyle='#000';ctx.fillText(text,tooltip_text_x,tooltip_text_y);}每次重绘Tooltip时都需要重绘饼图,每次绘制都会修改startAngleendAngle,所以需要在绘制前重新设置functionclear(){ctx.clearRect(0,0,cv.width,cv.height);开始角度=0;结束角度=0;cv.onmousemove=null;}我们最后的绘制方法~functiondraw(){clear();title_text=op.title.text;ctx.font=op.title.font.weight+""+op.title.font.size+"px"+op.title.font.family;title_width=ctx.measureText(title_text).width;title_height=op.title.font.size;title_position={x:(width,title_width)/2,y:20+title_height};ctx.fillText(title_text,title_position.x,title_position.y);radius=(height-title_height-title_position.y-20)/2;center={x:radius+20,y:radius+30+title_position.y};legend_width=op.legend.font.size*2.5;legend_height=op.legend.font.size*1.2;legend_posX=center.x*2+20;legend_posY=80;legend_textX=legend_posX+legend_width+5;legend_textY=legend_posY+op.legend.font.size*0.9;ctx.strokeStyle='灰色的';ctx.lineWidth=3;ctx.strokeRect(0,0,宽度,高度);对于(vari=0,len=data_c.length;i