示例代码托管于:http://www.github.com/dashnowords/blogs博客园地址:《大史住在大前端》原博文目录华为云社区地址:[你要的前端打怪升级指南][TOC]1.任务描述使用原生canvasAPI绘制饼图(夜莺玫瑰)。(截图及数据来自百度Echarts官方示例库【查看示例链接】)。2、需要提醒的是,南丁格尔玫瑰图的绘制方法有很多种。Echarts提供了两种不同的半径或面积模式。本文以面积比绘制法为例。绘制算法如下:确定每个扇形角的面积。由于所有扇区的角度加起来是2π,我们先根据数据比计算角度:$$θ_i=\frac{data_i}{\sum_{i=0}^ndata_i}*2\pi$$对每个sector区域面积与总面积的比值为数值的比值。取给定参数数组options.radius中的最大值和最小值作为最大值扇区的绘图数据,代入下式即可得到总面积S:$$\frac{\pi(R_{i}^2-R_{min}^2)*θ_{i}/2\pi}{S}=\frac{data_i}{data_{sum}}$$然后用上面的公式计算每个扇区对应的外圆半径,在画布中绘制路径并填充。3.示例代码南丁格尔玫瑰图绘制示例代码://绘制饼图drawPieChart(options);/***画饼图*@param{[type]}options[description]*@return{[type]}[description]*/functiondrawPieChart(options){//记录最大值,计算总和面积options.maxValue=0;//对数据集求和计算后面每个饼的角度比例options.totalNum=options.data.reduce((pre,cur)=>{if(cur.value>options.maxValue){options.maxValue=cur.value;}returnpre+cur.value;},0);/*对应最大值最大半径用于计算面积之和,覆盖原值*使得最大扇形外圆的半径为options.radius[0]*的半径内圈是options.radius[1]*/letRmin=options.radius[0];让Rmax=options.radius[1];让r=Math.sqrt((Rmax*Rmax-Rmin*Rmin)*options.totalNum/options.maxValue+Rmin*Rmin);options.radius[1]=r;//将坐标系原点移动到绘图中心letpaintingCenter={x:parseInt(options.center[0],10)/100*(options.chartZone[2]-options.chartZone[0])+options.chartZone[0],y:parseInt(options.center[1],10)/100*(options.chartZone[3]-options.chartZone[1])+options.chartZone[1]}context.translate(paintingCenter.x,paintingCenter.y);//绘制每个扇区并累加过程中的旋转角度);返回上一个+角度;},0);//画一个空心的白色圆圈context.beginPath();context.fillStyle='白色';context.arc(0,0,options.radius[0],0,2*Math.PI,false);context.fill();}/***计算每个扇区需要的绘图参数*/functioncalcPaintingData(data,options){letscale=data.value/options.totalNum;设角度=比例*2*Math.PI;让Rmin=options.radius[0];让Rmax=options.radius[1];让r=Math.sqrt(scale*(Rmax*Rmax-Rmin*Rmin)+Rmin*Rmin);数据.r=r;//绘制扇形paintFan({r:r,angle:angle,data:data,options:options});returnangle;//返回角度值给外层函数进行累加}//画扇functionpaintFan(opt){context.beginPath();context.lineTo(opt.r,0);续ext.arc(0,0,opt.r,0,opt.angle,false);context.lineTo(0,0);context.closePath();context.fill();context.rotate(opt.angle);}在浏览器中可以看到效果:4.hover高亮的实现思路。绘制过程中,将每个扇区的绘制数据(半径、相对圆心的起始角、扇形角)挂载到绘制数据上,监听canvas标签上的鼠标移动事件mousemove,将鼠标移动进行转换eventsevent.clientX和event.clientY在回调函数中转化为相对于画布坐标(mouseX,mouseY)的值。圆心坐标(paintingCenter.x,paintingCenter.y)和(mouseX,mouseY)连接成一个向量。根据向量的角度和模数,可以判断鼠标是否在某个扇区上。如果在扇区上方,则绘制关键帧,并带有过渡动画,使悬停效果出现。先修改context.fillStyle的颜色为对应扇区的高亮颜色,然后让外圈绘制半径逐帧线性增加到目标大小(比如10%),使用canvas绘制上下文在每一帧重新圈出绘图区画线,然后填充。当悬停效果出现时,绘制突出显示的绘图区。当悬停效果消失后,从外圈开始逐帧画出白色的外扇区,最后画出数据扇区作为原色。
