示例代碼託管在: http://www.github.com/dashnowords/blogs博客園地址:《大史住在大前端》原創博文目錄html
華爲雲社區地址:【你要的前端打怪升級指南】前端
[TOC]git
使用原生canvasAPI
繪製餅圖(南丁格爾玫瑰)。(截圖以及數據來自於百度Echarts官方示例庫【查看示例連接】)。github
南丁格爾玫瑰圖的畫法有不少種,Echarts
中提供的以半徑或面積兩種不一樣模式,本文中以面積比例畫法爲例,繪製算法以下:算法
肯定每一個扇區的角度。因爲全部扇區的角度加在一塊兒爲2π ,咱們先按照數據比例來計算角度:canvas
$$ θ_i = \frac{data_i}{\sum_{i=0}^ndata_i}*2\pi $$數組
options.radius
中的最大和最小數值做爲數值最大的一塊扇形的繪圖數據,代入以下公式便可求得總面積S
:$$ \frac{\pi(R_{i}^2 - R_{min}^2)*θ_{i}/2\pi}{S} = \frac{data_i}{data_{sum}} $$瀏覽器
南丁格爾玫瑰圖繪製示例代碼:echarts
//繪製餅圖 drawPieChart(options); /** * 繪製餅圖 * @param {[type]} options [description] * @return {[type]} [description] */ function drawPieChart(options) { //記錄最大數值以反求面積總和 options.maxValue = 0; //求數據集總和以在後續計算每一個扇形的角度比例 options.totalNum = options.data.reduce((pre,cur)=>{ if (cur.value > options.maxValue) { options.maxValue = cur.value; } return pre+cur.value; },0); /*以最大值對應最大半徑來計算面積總和,並覆蓋原值 *使得最大的一塊扇形外圓半徑爲options.radius[0] *內圓半徑爲options.radius[1] */ let Rmin = options.radius[0]; let Rmax = options.radius[1]; let r = Math.sqrt((Rmax*Rmax - Rmin*Rmin)*options.totalNum / options.maxValue + Rmin*Rmin); options.radius[1] = r; //移動座標系原點至繪圖中心 let paintingCenter={ 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); //繪製每一個扇形,過程當中累加旋轉角度 let allAngle = options.data.reduce((prev,cur,index)=>{ context.fillStyle = options.colorPool[index] let angle = calcPaintingData(cur,options); return prev + angle; },0); //繪製中空白色圓 context.beginPath(); context.fillStyle = 'white'; context.arc(0,0,options.radius[0],0,2*Math.PI,false); context.fill(); } /** * 計算每一個扇形所須要的繪圖參數 */ function calcPaintingData(data,options) { let scale = data.value / options.totalNum; let angle = scale * 2 * Math.PI; let Rmin = options.radius[0]; let Rmax = options.radius[1]; let r = Math.sqrt(scale * (Rmax*Rmax - Rmin*Rmin) + Rmin*Rmin); data.r = r; //繪製扇形 paintFan({ r:r, angle:angle, data:data, options:options }); return angle;//將角度值返回給外層函數以供累加 } //繪製扇形 function paintFan(opt) { context.beginPath(); context.lineTo(opt.r,0); context.arc(0,0,opt.r,0,opt.angle,false); context.lineTo(0,0); context.closePath(); context.fill(); context.rotate(opt.angle); }
瀏覽器中可查看效果:函數
canvas
標籤上監聽鼠標移動事件mousemove
,並在回調函數中將鼠標移動事件event.clientX
和event.clientY
轉換爲相對於canvas座標的數值(mouseX,mouseY)
。(paintingCenter.x,paintingCenter.y)
到(mouseX,mouseY)
鏈接爲向量,根據該向量的角度和模便可判斷鼠標是否處於某個扇區之上。context.fillStyle
顏色爲對應扇區的高亮色,而後讓外圓繪圖半徑以線性的方式逐幀增長至目標大小(例如10%),每一幀中使用canvas繪圖上下文從新對繪圖區域進行封閉畫線,而後填充便可。