利用highcharts繪制樹形結構圖


highcharts不僅可以繪制官網上面指定的報表,同時,還可以利用其封裝好的繪圖庫來繪制自定義圖形。

有朋友在工作中剛好有這樣的需求,當時看了兩個類似的js繪圖庫:

highcharts重在報表,但是同時提供了封裝好的繪圖庫,屏蔽了瀏覽器間差異(IE8及以下不支持SVG,使用VML繪圖)

Raphaël僅僅提供了各式各樣的繪圖的API,也非常的好用。

僅僅實現當前功能的話,Raphaël應該是首選,但是,我們做web應用的時候,項目中難免用到其他的報表樣式,所以權衡之下,還是選擇highcharts比較划算。

先把效果圖貼上,比較好描述問題:

1. 首先,我們定義一組這樣的樹形結構,大家學過數據結構的,應該很容易使用類似鏈表的結構進行描述。

2. 關於怎么添加節點之類的就不多說了,主要說一下,畫圖需要知道繪制圖形的位置,而這個位置需要根據節點來進行計算。

3. 計算位置的時候,采用遞歸的方式來進行計算,當前節點的位置始終位於,其子節點的的中間偏上的位置,所以需要一個獲取子節點左邊位置和一個獲取右邊位置的函數,此函數會遞歸調用。和子節點之間的高度則由一個常數來進行定義。

4. 繪制圖形,通過第三步可以計算出每個節點對應的位置,那么就可以進行繪圖了,首先是節點圖形,這個相對簡單。其次是父子節點之間的連線。每條線均使用3次貝澤爾曲線來繪制。需要提供3個點,共6個參數。剛好這6個參數可以由父節點的位置和子節點的位置通過組合得到。

具體代碼如下所示:(也可以通過http://jsfiddle.net/lloydzhou/JY59J/5/ 在線查看)

定義節點類
 1 var Node = {
 2     height: 20,
 3     width: 60,
 4     padding: 30,
 5     paddingTop: 50,
 6     paddingText: 5,
 7     paddingLeft: 20,
 8     arrowLength: 4,
 9     arrowWidth: 3,
10     create: function(textVal){
11         return {
12             text: textVal,
13             childNodes: [],
14             parentNode: null,
15             index: null,
16             x: null,
17             y: null,
18             add: function(node) {
19                 this.childNodes[node.index = this.childNodes.length] = node;
20                 return node.parentNode = this;
21             },
22             leftNode: function() {
23                 return this.parentNode ? (this.index > 0 ? this.parentNode.childNodes[this.index - 1] : this.parentNode.leftNode()) : null;
24             },
25             top: function() {
26                 if (this.y) return this.y;
27                 else return this.y = (this.parentNode ? this.parentNode.top() + Node.height + Node.padding : Node.paddingTop);
28             },
29             left: function() {
30                 if (this.x) {
31                     return this.x;
32                 }else{
33                     return this.x = this.leftNode() ? this.leftNode().left() + Node.padding + this.leftNode().childWidth() : Node.paddingLeft;
34                 }
35             },
36             childHeight: function() {
37                 var h = 0;if (this.childNodes.length > 0) {for(var i=0;i<this.childNodes.length;i++) h = Math.max(h,this.childNodes[i].childHeight() + Node.padding);}
38                 return (h === 0) ? Node.height : h ;
39             },
40             childWidth: function() {
41                 var w = 0;if (this.childNodes.length > 0) {for(var i=0;i<this.childNodes.length;i++) w = w + this.childNodes[i].childWidth() + Node.padding;}
42                 return (w === 0) ? Node.width : w - Node.padding;
43             },
44             toString: function() {
45                 return this.text + '[' + this.left() + ',' + this.top() + ']' + (this.childNodes.length > 0 ? ':{' + this.childNodes + '}' : '');
46             },
47             arrowX: function () {
48                 return this.left() + this.childWidth() / 2 - (this.parentNode.left() + this.parentNode.childWidth() / 2 - Node.width / 2) - Node.width / 2;
49             },
50             arrowY: function () {
51                 return Node.padding - 2 * Node.paddingText;
52             },
53             arrow: function(renderer, colors) {
54                 var a = Node.arrowLength, b = Node.arrowWidth, x2 = this.arrowX(), y2 = this.arrowY(),
55                     x = this.parentNode.left() + this.parentNode.childWidth() / 2 - Node.width / 2+Node.width/2 + Node.paddingText,
56                     y = this.parentNode.top() + Node.height + 2 * Node.paddingText
57                 renderer.path(['M', 0, 0, 'C', 0, y2/1.8, x2, 0, x2, y2]).attr({'stroke-width': 2, stroke: colors[1]}).translate(x,y).add()
58             },
59             drow: function(renderer, colors) {
60                 var x = this.left() + this.childWidth() / 2 - Node.width / 2, y = this.top();
61                 renderer.label(this.text, x, y).attr({fill: colors[0], stroke: 'white','stroke-width': 3,padding: Node.paddingText, r: 2 * Node.paddingText, width: Node.width, height: Node.height}).css({color: 'white', textAlign:'center',fontWeight: 'bold',fontSize: '10px'}).add().shadow(true);
62                 if (this.childNodes.length > 0) {
63                     for(var i=0;i<this.childNodes.length;i++) {
64                         this.childNodes[i].drow(renderer, colors);
65                         this.childNodes[i].arrow(renderer, colors);
66                     }
67                 }
68             }
69         };
70     }
71 }

1 var n = Node.create
2 
3 var datas = n(1)
4     .add(n(2))
5     .add(n(3).add(n(7)).add(n(8)))
6     .add(n(4))
7     .add(n(5).add(n(6)).add(n(9)))
生成節點
1     var chart = new Highcharts.Chart({
2         chart: {
3             renderTo: 'container',
4             events: {load: function () { 
5                 datas.drow(this.renderer, Highcharts.getOptions().colors);
6             }}
7         },
8         title: {text: 'demo'}
9     });
繪制圖形

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM