目標
在這一章,我們將學習如何使SVG坐標空間是動態的,這樣我們的數據可視化不論數據是什么,都始終是可見的。
我們會使得SVG坐標空間尺度上調或下調來適於我們的數據。
三個SVG長方形
我們就從三個長方形作為開始:
1 var jsonRectangles = [ 2 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 3 { "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 4 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }]; 5 6 var svgContainer = d3.select("body").append("svg") 7 .attr("width", 100) 8 .attr("height", 100); 9 10 var rectangles = svgContainer.selectAll("rect") 11 .data(jsonRectangles) 12 .enter() 13 .append("rect"); 14 15 var rectangleAttributes = rectangles 16 .attr("x", function (d) { return d.x_axis; }) 17 .attr("y", function (d) { return d.y_axis; }) 18 .attr("height", function (d) { return d.height; }) 19 .attr("width", function (d) { return d.width; }) 20 .style("fill", function(d) { return d.color; });
得到的結果是:

漂亮!
其中SVG容器:
1 var svgContainer = d3.select("body").append("svg") 2 .attr("width", 100) 3 .attr("height", 100);
寬100單位,高100單位。
也就是說,圖中三個長方形中最右下角的點的坐標(90,90)仍然在SVG容器視窗范圍內。
但是,如果紫色的長方形的x坐標,突然增加了四倍,從40變成160,結果會怎么樣呢?
1 //原來 2 { "x_axis": 40, "y_axis": 40, "height": 20, "width":20, "color" : "purple" } 3 4 //變為 5 { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" } 6 7 //因此,數據集 jsonRectangles 變成了: 8 var jsonRectangles = [ 9 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 10 { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 11 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }];
這也就是說,紫色長方形有坐標值(160,40)。
這個坐標已經超出高100寬100的范圍。
這樣我們的數據可視化結果成了:

就像我們想象的那樣,太糟了!
動態調整SVG容器空間
我們真正需要的是,能夠根據我們的數據,對SVG容器的width、height屬性進行動態調節。
我們打算使用基礎JavaScript循環(loop)來對JSON對象數組處理,找出最大的X坐標he最大的Y坐標。
最大的x坐標和最大的y坐標就是長方形的最右下角點的坐標。
1 //新的jsonRectangle數據(其中紫色長方形的x坐標現在是160) 2 var jsonRectangles = [ 3 {"x_axis":10,"y_axis":10,"height":20,"width":20,"color":"green"}, 4 {"x_axis":160,"y_axis":40,"height":20,"width":20,"color":"purplr"}, 5 {"x_axis":70,"y_axis":70,"height":20,"width":20,"color":"red"} 6 ]; 7 8 var max_x = 0;//用於存儲最大x坐標 9 var max_y = 0;//用於存儲最大y坐標 10 11 //在jsonRectangle數組上的循環 12 for(var i = 0;i<jsonRectangles.length;i++){ 13 var temp_x,temp_y; 14 15 //為了得到最右的點,我們需要把x坐標和width相加 16 temp_x = jsonRectangles[i].x_axis+jsonRectangles[i].width; 17 18 //為了得到最下面的點,我們需要把y坐標和height相加 19 temp_y = jsonRectangles[i].y_axis+jsonRectangles[i].height; 20 21 /** 22 *如果臨時x坐標比max_x大, 23 *那么就讓max_x等於temp_x 24 *否則,什么都不用做 25 *同理,max_y也一樣 26 */ 27 if(temp_x>=max_x){ 28 max_x = temp_x; 29 } 30 31 if(temp_y>=max_y){ 32 max_y = temp_y; 33 } 34 35 }//循環停止 36 37 max_x; 38 //返回180 39 40 max_y; 41 //返回 90
如果數據發生了變化,max_x和max_y將始終都是數據中的最大值。
現在,我們可以更新我們的SVG容器:
1 //原來 2 var svgContainer = d3.select("body").append("svg") 3 .attr("height",200) 4 .attr("width",200); 5 6 //現在(使用變量max_x和max_y) 7 var svgContainer = d3.select("body").append("svg") 8 .attr("width",max_x + 20) 9 .attr("width",max_x + 20); 10 //注意 — 在這里給max_x和max_y各加了20,是為了給元素多一些文本空間
這樣,SVG容器就一直能夠顯示右邊的最大尺寸,我們的數據也就能正確的出現在其內部。
成品
既然問題解決了,完整的代碼如下:
1 var jsonRectangles = [ 2 { "x_axis": 10, "y_axis": 10, "height": 20, "width":20, "color" : "green" }, 3 { "x_axis": 160, "y_axis": 40, "height": 20, "width":20, "color" : "purple" }, 4 { "x_axis": 70, "y_axis": 70, "height": 20, "width":20, "color" : "red" }]; 5 6 var max_x = 0; 7 var max_y = 0; 8 9 for (var i = 0; i < jsonRectangles.length; i++) { 10 var temp_x, temp_y; 11 var temp_x = jsonRectangles[i].x_axis + jsonRectangles[i].width; 12 var temp_y = jsonRectangles[i].y_axis + jsonRectangles[i].height; 13 14 if ( temp_x >= max_x ) { max_x = temp_x; } 15 16 if ( temp_y >= max_y ) { max_y = temp_y; } 17 } 18 19 var svgContainer = d3.select("body").append("svg") 20 .attr("width", max_x) 21 .attr("height", max_y) 22 23 var rectangles = svgContainer.selectAll("rect") 24 .data(jsonRectangles) 25 .enter() 26 .append("rect"); 27 28 var rectangleAttributes = rectangles 29 .attr("x", function (d) { return d.x_axis; }) 30 .attr("y", function (d) { return d.y_axis; }) 31 .attr("height", function (d) { return d.height; }) 32 .attr("width", function (d) { return d.width; }) 33 .style("fill", function(d) { return d.color; });

現在,所有的長方形都出現啦!
SVG視窗能夠把最右的(max_x+20,max_y+20)包括進去。
而且,SVG視窗是動態生成,不需要我們去手動的更新width和height。
使用JavaScript的For循環,我們就能夠實現動態的resize我們的SVG視窗容器來適應數據。
如果數據再次改變,我們的視窗(容器)也能夠隨時包含全部的數據可視化結果。
