示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] 一. 任務說明 使用原生 繪製餅圖(南丁格爾玫瑰)。(截圖以及數據來自於百度Echar ...
目錄
示例代碼托管在:http://www.github.com/dashnowords/blogs
博客園地址:《大史住在大前端》原創博文目錄
華為雲社區地址:【你要的前端打怪升級指南】
一. 任務說明
使用原生canvasAPI
繪製餅圖(南丁格爾玫瑰)。(截圖以及數據來自於百度Echarts官方示例庫【查看示例鏈接】)。
二. 重點提示
南丁格爾玫瑰圖的畫法有很多種,Echarts
中提供的以半徑或面積兩種不同模式,本文中以面積比例畫法為例,繪製演算法如下:
- 確定每個扇區的角度。由於所有扇區的角度加在一起為2π ,我們先按照數據比例來計算角度:
- 每個扇區面積與總面積之間的比例即為數值的比,將給定參數數組
options.radius
中的最大和最小數值作為數值最大的一塊扇形的繪圖數據,代入如下公式即可求得總面積S
:
- 再利用上述公式分別計算出每個扇形對應的外圓半徑,在canvas中繪製路徑並填充即可。
三. 示例代碼
南丁格爾玫瑰圖繪製示例代碼:
//繪製餅圖
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);
}
瀏覽器中可查看效果:
四. hover高亮的實現思路
- 繪圖過程中,將每個扇區的繪圖數據(半徑,相對於圓心的起始轉角,扇區角度)均掛載在繪圖數據上。
- 在
canvas
標簽上監聽滑鼠移動事件mousemove
,併在回調函數中將滑鼠移動事件event.clientX
和event.clientY
轉換為相對於canvas坐標的數值(mouseX,mouseY)
。 - 從圓心坐標
(paintingCenter.x,paintingCenter.y)
到(mouseX,mouseY)
連接為向量,根據該向量的角度和模即可判斷滑鼠是否處於某個扇區之上。 - 如果處於扇區之上,則以過渡動畫來繪製關鍵幀使得hover效果表現出來。先修改
context.fillStyle
顏色為對應扇區的高亮色,然後讓外圓繪圖半徑以線性的方式逐幀增加至目標大小(例如10%),每一幀中使用canvas繪圖上下文重新對繪圖區域進行封閉畫線,然後填充即可。 - hover效果出現時繪製高亮色的繪圖區域,hover效果消失時從外圓開始逐幀繪製白色外層扇區即可,最終再將數據扇區繪製為原色。