摘要:綜上,最后我們的工具函數(shù)應(yīng)該長(zhǎng)成下面這個(gè)樣子首先獲取繪圖上下文,仍要注意先判斷是否存在方法。把標(biāo)題繪制在畫布頂部的中間,距離頁(yè)面頂部留有像素的空隙,并且根據(jù)參數(shù),繪制具有特定內(nèi)容和樣式的標(biāo)題。
有了canvas之后,我們可以很容易地創(chuàng)建一個(gè)簡(jiǎn)單圖標(biāo),不需要任何插件,不過,有的小伙伴覺得它很難,筆者仔細(xì)思考一番之后,只能吐嘈一下他們的繪圖技能...
于是在開始繪制之前,我們首先畫一下草圖~
講解之前,先貢獻(xiàn)出源碼:https://github.com/Sue1024/Ca...
Make It Reusable為了創(chuàng)建一個(gè)可以重用,并且可以靈活地重用的餅圖,筆者決定最終的創(chuàng)建餅圖方法接收兩個(gè)參數(shù),分別是要顯示的數(shù)據(jù)data,繪制參數(shù)options
Data在實(shí)際應(yīng)用場(chǎng)景中,我們從后端拿到的往往是諸如幾個(gè)年份的產(chǎn)量一類的數(shù)據(jù),比如(這里,我們?yōu)榱撕?jiǎn)化代碼,將顏色也放到了后臺(tái)返回的數(shù)據(jù)中):
var data = [ { data: 10, color: "red", label: "2016" }, { data: 15, color: "grey", label: "2017" }, { data: 15, color: "black", label: "2018" } ];
而繪制餅圖時(shí), 我們需要根據(jù)比例"分餅", 并且在某些地方顯示出實(shí)際的數(shù)據(jù)(比如tooltip),因此我們需要一個(gè)如下的數(shù)據(jù)處理函數(shù):
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; } }Options
另外,即使我們可以根據(jù)不同的數(shù)據(jù)繪制不同的圖表,恐怕也只能滿足個(gè)別需求,畢竟每個(gè)人的喜好都不一樣,我們需要?jiǎng)?chuàng)建一個(gè)可以顯示不同數(shù)據(jù),又可以擁有不同排版、不同布局的圖表,實(shí)現(xiàn)上述目標(biāo),我們需要如下參數(shù)列表:
var options = { legend: { font: { size: 18, family: "Arial", weight: "bold" } }, title: { text: "Pie Chart", font: { size: 18, family: "Arial", weight: "bold" } }, tooltip: { template: "CanvasYear: {{label}}Production: {{data}}", font: { size: 18, family: "Arial", weight: "bold" } } }
我們的工具函數(shù)不應(yīng)該可以提前知道用戶想要用來繪制圖表的canvas,用戶可能想在頁(yè)面中的多個(gè)canvas上繪制圖表,因此工具函數(shù)應(yīng)該可以接受一個(gè)參數(shù),用來確定繪制圖表的canvas,很多開源庫(kù)都使用id作為識(shí)別canvas的標(biāo)識(shí),筆者認(rèn)為接收element更好一些,因?yàn)椴皇撬械挠脩舳荚敢饨ocanvas添加ID屬性, 有的時(shí)候,用戶想給擁有某一個(gè)class屬性的所有canvas批量繪圖,并根據(jù)它們的dataset屬性動(dòng)態(tài)的生成數(shù)據(jù)。
綜上,最后我們的工具函數(shù)應(yīng)該長(zhǎng)成下面這個(gè)樣子:
function drawPie(canvas, data, option) { // To Do }Start Coding Get Context
首先獲取繪圖上下文,仍要注意先判斷是否存在getContext()方法。
var canvas = document.getElementById("canvas"); if(canvas.getContext) { var ctx = canvas.getContext("2d"); }Generate Options
然后,我們需要將自定義的參數(shù)和默認(rèn)參數(shù)合并在一起,組成一個(gè)新的完整的參數(shù)列表,原則就是沒有自定義的都采用默認(rèn)值。
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); }Draw Title
把標(biāo)題繪制在畫布頂部的中間,距離頁(yè)面頂部留有20像素的空隙,并且根據(jù)參數(shù),繪制具有特定內(nèi)容和樣式的標(biāo)題。
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);Radius & Center
筆者決定使餅圖距離標(biāo)題有30像素的空隙,距離左邊框和底部分別留有20像素的空隙,因此它的半徑和圓心分別是:
var radius = (height - title_height - title_position.y - 20) / 2 ; var center = { x: radius + 20, y: radius + 30 + title_position.y };Legend
圖例的高設(shè)置為圖例字體大小的1.2倍,寬設(shè)置為圖例字體大小的2.5倍,距離餅圖40像素的間隙,第一個(gè)圖例頂部距離頁(yè)面頂端80像素,文字距離圖例5像素,垂直居中,于是圖例的大體信息總結(jié)如下:
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;Draw Pie & Legends
先給圖表加一個(gè)邊框
ctx.strokeStyle = "grey"; ctx.lineWidth = 3; ctx.strokeRect(0, 0, canvas.width, canvas.height);
遍歷數(shù)據(jù)繪圖。
var data_c = calculateData(data); var startAngle = 0, endAngle = 0; for(var i=0, len=data.length; iLet"s try it! 我們的工具函數(shù)已經(jīng)做到一半啦,可以畫出一個(gè)帶有圖例的餅圖,并且標(biāo)題和圖例文字大小 粗細(xì) 字體均可配置,下面試一下靈不靈~
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();畫出來的餅圖長(zhǎng)這個(gè)樣子~
下一篇筆者會(huì)加上Tooltip的繪制哦,那部分比較復(fù)雜,默默地給自己加油~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94957.html
摘要:繼上一篇實(shí)戰(zhàn)繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。為了方便保存,創(chuàng)建一個(gè)構(gòu)造函數(shù)。最終我們的方法成品圖源碼地址 繼上一篇HTML5 Canvas(實(shí)戰(zhàn):繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。 Plot對(duì)象 在開始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個(gè)區(qū)域添加mousemove事件,鼠標(biāo)在其上移動(dòng)時(shí)則顯示...
摘要:繼上一篇實(shí)戰(zhàn)繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。為了方便保存,創(chuàng)建一個(gè)構(gòu)造函數(shù)。最終我們的方法成品圖源碼地址 繼上一篇HTML5 Canvas(實(shí)戰(zhàn):繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。 Plot對(duì)象 在開始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個(gè)區(qū)域添加mousemove事件,鼠標(biāo)在其上移動(dòng)時(shí)則顯示...
閱讀 1854·2023-04-25 14:49
閱讀 3134·2021-09-30 09:47
閱讀 3131·2021-09-06 15:00
閱讀 2240·2019-08-30 13:16
閱讀 1456·2019-08-30 10:48
閱讀 2688·2019-08-29 15:11
閱讀 1305·2019-08-26 14:06
閱讀 1685·2019-08-26 13:30