iT邦幫忙

DAY 22
1

SVG 30 天 ~ 就要醬玩系列 第 22

Day22 - 寫 jquery 產生 SVG 圓餅圖

之前我們提過了 SVG pie chart 圓餅圖實作控制 SVG 的注意事項,現在就要來將兩者結合, 使用 jquery 來畫出圓餅圖,同時使用 json 的格式來長出特定的角度和顏色,如此一來,在不需要 D3.js 或 C3.js 等 SVG js 框架的輔助下,也可以自己做出一個動態產生的圓餅圖,而且還很好用喔!如果對於圓餅圖的產生還不熟悉的人,請點選上面的連結回去再次閱讀一下,如果需要 D3.js 的,可以參考鐵人王志誠大大 ( http://wcc723.github.io/ ) 一系列的 D3.js 文章喔。

( oxxostudio.tw 同步發表:SVG 研究之路 (24) - 寫 jquery 產生圓餅圖 )

使用 jquery 畫出圓餅圖的原理很簡單,就是要在 SVG 內部放入 path 的路徑,不過由於 SVG 是 xmlns 而非 HTML,因此瀏覽器不認識 rect 或 path 等元素,所以在程式之初必須先加上這段:

 function makeSVG(tag, attrs) {
   var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
   for (var k in attrs)
     el.setAttribute(k, attrs[k]);
   return el;
 }

有了這段之後,我們就可以把 path 或 rect 宣告為 HTML 的元素,就可以使用 jquery 來做 append 的動作;接下來就是重頭戲了,回想一下之前是怎麼畫圓餅圖的,現在就要依樣畫葫蘆的畫出來,只是要轉成 jquery 啦~

一開始要先宣告變數, i和 j是 for 迴圈用的, path是配合上面的程式碼要給 jquery append 的, x0,y0則是從圓心出發的第一個座標, x1,y1是第二個座標, aa,bb,cc, all都只是後面會用到的變數, cx,cy是圓餅圖的圓心, r是半徑,最後一個 data則是 json 格式的資料。

 var i, j, path, x0, y0, x1, y1, aa, cc, bb = 0;
 var all = 0;
 var cx = 90;
 var cy = 90;
 var r = 70;
 var data = [{
   num: 12,
   fill: '#f00'
 }, {
   num: 28,
   fill: '#0f0'
 }, {
   num: 7,
   fill: '#c0c'
 }, {
   num: 36,
   fill: '#00f'
 }, {
   num: 7,
   fill: '#c0c'
 }, {
   num: 17,
   fill: '#0cc'
 }, {
   num: 12,
   fill: '#cc0'
 }, {
   num: 7,
   fill: '#c0c'
 }];

再來進入主程式的部分,一開始的 for 迴圈幫助我們由 data 計算總共有幾個扇形,才可以算出每個扇形的角度,跟著的 for 迴圈一開始先進行 if-else 的判斷,為什麼要這個判斷呢?單純只是因為第一個扇形和最後一個扇形的角度計算方式比較不同,所以必須獨立出來計算,判斷之後有了 path 所需要的數值,再藉由另外一個 if-else 來判斷畫出正確的扇形,最後緊跟著的 path 就是利用 jquery append 的方式畫入 SVG 裏頭。

 for (i = 0; i < data.length; i++) {
   all = all + data[i].num;
 }
 for (j = 0; j < data.length; j++) {
   if (j === 0) {
     x0 = cx + r * Math.cos((0 * Math.PI) / 180);
     y0 = cy + r * Math.sin((0 * Math.PI) / 180);
     bb = bb + data[0].num;
     cc = bb / all * 360;
     x1 = cx + r * Math.cos((cc * Math.PI) / 180);
     y1 = cy + r * Math.sin((cc * Math.PI) / 180);
   } else if (j > 0 && j < (data.length - 1)) {
     x0 = cx + r * Math.cos((cc * Math.PI) / 180);
     y0 = cy + r * Math.sin((cc * Math.PI) / 180);
     bb = bb + data[j].num;
     cc = bb / all * 360;
     x1 = cx + r * Math.cos((cc * Math.PI) / 180);
     y1 = cy + r * Math.sin((cc * Math.PI) / 180);
   } else {
     x0 = cx + r * Math.cos((cc * Math.PI) / 180);
     y0 = cy + r * Math.sin((cc * Math.PI) / 180);
     x1 = cx + r * Math.cos((0 * Math.PI) / 180);
     y1 = cy + r * Math.sin((0 * Math.PI) / 180);
   }

   if ((data[j].num / all * 360) > 180) {
     aa = 'M' + cx + ' ' + cy + ',' + 'L' + x0 + ' ' + y0 + ' ' + 'A' + r + ' ' + r + ' 0 1 1 ' + x1 + ' ' + y1 + ' ' + 'Z';
   } else {
     aa = 'M' + cx + ' ' + cy + ',' + 'L' + x0 + ' ' + y0 + ' ' + 'A' + r + ' ' + r + ' 0 0 1 ' + x1 + ' ' + y1 + ' ' + 'Z';
   }

   path = makeSVG('path', {
     'd': aa,
     'fill': data[j].fill,
     'stroke': '#fff',
     'stroke-width': '2'
   });
   $('#qq').append(path);

 }

就這樣,輕鬆愜意地完成了圓餅圖的製作,當然,了解原理之後,也可以做出一些不同的變化,例如半徑越來越大之類的圓餅圖,原理就是在第二個 for 迴圈裡面多個 r=r+5 ,每個扇形的半徑就會越來越大囉!( 範例:http://jsbin.com/jacugi/2 )


上一篇
Day21 - 控制 SVG 的小小注意事項
下一篇
Day23 - SVG + CSS 製作 Material Design 進度條
系列文
SVG 30 天 ~ 就要醬玩31

尚未有邦友留言

立即登入留言