有了canvas
以後,咱們能夠很容易地建立一個簡單圖標,不須要任何插件,不過,有的小夥伴以爲它很難,筆者仔細思考一番以後,只能吐嘈一下他們的繪圖技能...
因而在開始繪製以前,咱們首先畫一下草圖~git
講解以前,先貢獻出源碼:https://github.com/Sue1024/Ca...github
爲了建立一個能夠重用,而且能夠靈活地重用的餅圖,筆者決定最終的建立餅圖方法接收兩個參數,分別是要顯示的數據data
,繪製參數options
面試
在實際應用場景中,咱們從後端拿到的每每是諸如幾個年份的產量一類的數據,好比(這裏,咱們爲了簡化代碼,將顏色也放到了後臺返回的數據中):canvas
var data = [ { data: 10, color: "red", label: "2016" }, { data: 15, color: "grey", label: "2017" }, { data: 15, color: "black", label: "2018" } ];
而繪製餅圖時, 咱們須要根據比例"分餅", 而且在某些地方顯示出實際的數據(好比tooltip),所以咱們須要一個以下的數據處理函數:後端
function calculateData(data) { if(data instanceof Array) { var sum = data.reduce(function(a, b) { return a + b.data; }, 0); var map = data.map(function(a) { return { label: a.label, data: a.data, color: a.color, portion: a.data/sum } }); return map; } }
另外,即便咱們能夠根據不一樣的數據繪製不一樣的圖表,恐怕也只能知足個別需求,畢竟每一個人的喜愛都不同,咱們須要建立一個能夠顯示不一樣數據,又能夠擁有不一樣排版、不一樣佈局的圖表,實現上述目標,咱們須要以下參數列表:函數
var options = { legend: { font: { size: 18, family: 'Arial', weight: 'bold' } }, title: { text: 'Pie Chart', font: { size: 18, family: 'Arial', weight: 'bold' } }, tooltip: { template: '<div>Year: {{label}}</div><div>Production: {{data}}</div>', font: { size: 18, family: 'Arial', weight: 'bold' } } }
咱們的工具函數不該該能夠提早知道用戶想要用來繪製圖表的canvas,用戶可能想在頁面中的多個canvas上繪製圖表,所以工具函數應該能夠接受一個參數,用來肯定繪製圖表的canvas,不少開源庫都使用id做爲識別canvas的標識,筆者認爲接收element更好一些,由於不是全部的用戶都願意給canvas添加ID屬性, 有的時候,用戶想給擁有某一個class屬性的全部canvas批量繪圖,並根據它們的dataset屬性動態的生成數據。
綜上,最後咱們的工具函數應該長成下面這個樣子:工具
function drawPie(canvas, data, option) { // To Do }
首先獲取繪圖上下文,仍要注意先判斷是否存在getContext()
方法。佈局
var canvas = document.getElementById("canvas"); if(canvas.getContext) { var ctx = canvas.getContext("2d"); }
而後,咱們須要將自定義的參數和默認參數合併在一塊兒,組成一個新的完整的參數列表,原則就是沒有自定義的都採用默認值。字體
function mergeJSON(source1,source2){ var mergedJSON = JSON.parse(JSON.stringify(source2)); for (var attrname in source1) { if(mergedJSON.hasOwnProperty(attrname)) { if ( source1[attrname]!=null && source1[attrname].constructor==Object ) { mergedJSON[attrname] = mergeJSON(source1[attrname], mergedJSON[attrname]); } } else { mergedJSON[attrname] = source1[attrname]; } } return mergedJSON; } function generateOptions(givenOptions, defaultOptions) { return mergeJSON(defaultOptions, givenOptions); }
把標題繪製在畫布頂部的中間,距離頁面頂部留有20像素的空隙,而且根據參數,繪製具備特定內容和樣式的標題。spa
var width = canvas.width, height = canvas.height, op = generateOptions(options, defaultOptions), title_text = op.title.text, title_position = {}; ctx.font = op.title.font.weight + " " + op.title.font.size+"px " + op.title.font.family; title_position .x = (width - title_width)/2; title_position.y = 20 + op.title.font.size; title_width = ctx.measureText(title_text).width, title_height = op.title.font.size; ctx.fillText(title_text, title_position.x, title_position.y);
筆者決定使餅圖距離標題有30像素的空隙,距離左邊框和底部分別留有20像素的空隙,所以它的半徑和圓心分別是:
var radius = (height - title_height - title_position.y - 20) / 2 ; var center = { x: radius + 20, y: radius + 30 + title_position.y };
圖例的高設置爲圖例字體大小的1.2倍,寬設置爲圖例字體大小的2.5倍,距離餅圖40像素的間隙,第一個圖例頂部距離頁面頂端80像素,文字距離圖例5像素,垂直居中,因而圖例的大致信息總結以下:
var 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 = 'grey'; ctx.lineWidth = 3; ctx.strokeRect(0, 0, canvas.width, canvas.height);
遍歷數據繪圖。
var data_c = calculateData(data); var startAngle = 0, endAngle = 0; for(var i=0, len=data.length; i<len; i++) { endAngle += data_c[i].portion * 2*Math.PI; ctx.fillStyle = data_c[i].color; ctx.beginPath(); ctx.moveTo(center.x, center.y); ctx.arc(center.x, center.y, radius, startAngle, endAngle, false); ctx.closePath(); ctx.fill(); startAngle = endAngle; ctx.fillRect(legend_posX, legend_posY + (10 + legend_height) * i, legend_width, legend_height); ctx.font = 'bold 12px Arial'; var percent = data_c[i].label + ' : ' + (data_c[i].portion*100).toFixed(2) + '%'; ctx.fillText(percent, legend_textX, legend_textY + (10 + legend_height) * i); }
咱們的工具函數已經作到一半啦,能夠畫出一個帶有圖例的餅圖,而且標題和圖例文字大小 粗細 字體都可配置,下面試一下靈不靈~
var init = function(){ var data = [ { data: 10, color: "red", label: "2016" }, { data: 15, color: "grey", label: "2017" }, { data: 15, color: "black", label: "2018" } ]; var options = { title: { text: 'Production By Year', font: { size: 30 } } } drawCircle(data, document.getElementById("drawing"), options); }; init();
畫出來的餅圖長這個樣子~
下一篇筆者會加上Tooltip的繪製哦,那部分比較複雜,默默地給本身加油~