目标
在这一章,我们将学习如何使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视窗容器来适应数据。
如果数据再次改变,我们的视窗(容器)也能够随时包含全部的数据可视化结果。