本篇文章給大家?guī)淼膬?nèi)容是關(guān)于canvas繪制餅圖的方法介紹(代碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
一. 任務(wù)說明
使用原生canvasAPI
繪制餅圖(南丁格爾玫瑰)。(截圖以及數(shù)據(jù)來自于百度Echarts官方示例庫【查看示例鏈接】)。
二. 重點(diǎn)提示
南丁格爾玫瑰圖的畫法有很多種,Echarts
中提供的以半徑或面積兩種不同模式,本文中以面積比例畫法為例,繪制算法如下:
- 確定每個扇區(qū)的角度。由于所有扇區(qū)的角度加在一起為2π ,我們先按照數(shù)據(jù)比例來計算角度:
- 每個扇區(qū)面積與總面積之間的比例即為數(shù)值的比,將給定參數(shù)數(shù)組
options.radius
中的最大和最小數(shù)值作為數(shù)值最大的一塊扇形的繪圖數(shù)據(jù),代入如下公式即可求得總面積S
:
- 再利用上述公式分別計算出每個扇形對應(yīng)的外圓半徑,在canvas中繪制路徑并填充即可。
三. 示例代碼
南丁格爾玫瑰圖繪制示例代碼:
//繪制餅圖 drawPieChart(options); /** * 繪制餅圖 * @param {[type]} options [description] * @return {[type]} [description] */ function drawPieChart(options) { //記錄最大數(shù)值以反求面積總和 options.maxValue = 0; //求數(shù)據(jù)集總和以在后續(xù)計算每個扇形的角度比例 options.totalNum = options.data.reduce((pre,cur)=>{ if (cur.value > options.maxValue) { options.maxValue = cur.value; } return pre+cur.value; },0); /*以最大值對應(yīng)最大半徑來計算面積總和,并覆蓋原值 *使得最大的一塊扇形外圓半徑為options.radius[0] *內(nèi)圓半徑為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; //移動坐標(biāo)系原點(diǎn)至繪圖中心 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); //繪制每個扇形,過程中累加旋轉(zhuǎn)角度 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(); } /** * 計算每個扇形所需要的繪圖參數(shù) */ 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;//將角度值返回給外層函數(shù)以供累加 } //繪制扇形 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高亮的實(shí)現(xiàn)思路
- 繪圖過程中,將每個扇區(qū)的繪圖數(shù)據(jù)(半徑,相對于圓心的起始轉(zhuǎn)角,扇區(qū)角度)均掛載在繪圖數(shù)據(jù)上。
- 在
canvas
標(biāo)簽上監(jiān)聽鼠標(biāo)移動事件mousemove
,并在回調(diào)函數(shù)中將鼠標(biāo)移動事件event.clientX
和event.clientY
轉(zhuǎn)換為相對于canvas坐標(biāo)的數(shù)值(mouseX,mouseY)
。 - 從圓心坐標(biāo)
(paintingCenter.x,paintingCenter.y)
到(mouseX,mouseY)
連接為向量,根據(jù)該向量的角度和模即可判斷鼠標(biāo)是否處于某個扇區(qū)之上。 - 如果處于扇區(qū)之上,則以過渡動畫來繪制關(guān)鍵幀使得hover效果表現(xiàn)出來。先修改
context.fillStyle
顏色為對應(yīng)扇區(qū)的高亮色,然后讓外圓繪圖半徑以線性的方式逐幀增加至目標(biāo)大?。ɡ?0%),每一幀中使用canvas繪圖上下文重新對繪圖區(qū)域進(jìn)行封閉畫線,然后填充即可。 -
hover效果出現(xiàn)時繪制高亮色的繪圖區(qū)域,hover效果消失時從外圓開始逐幀繪制白色外層扇區(qū)即可,最終再將數(shù)據(jù)扇區(qū)繪制為原色。
【相關(guān)推薦:HTML5視頻教程】