饿了么布局组件
这个由两篇文章组成的系列文章的第1部分概述了SVG和D3如何协同工作,并提供了一些基本示例,这些示例创建了可视化的社交媒体浏览数据。 第二部分将引导您完成在SVG图纸中使用图形组件的不同排列或布局的步骤。 您将学习如何使用D3强大的图形计算将组件放置在SVG画布上,以及如何将自己的图形操作与D3的布局相结合。 我还将探讨如何将JavaScript对象表示法(JSON)用作可视化的数据格式。 本文最后通过展示如何使用布局组合在单个SVG画布上排列各种图形组件来结束。
本文中的概念和示例建立在第1部分中的概念和示例的基础上,因此请确保在继续阅读之前阅读或阅读它们。 在阅读第2部分时,您可能会发现在浏览器中同时打开这两篇文章很有用,因此您可以参考第1部分中的图像。请参阅下载以获取第2部分示例代码。
介绍D3的图形布局
我首先从您在第1部分中学到的有关D3功能的知识开始。
回想一下, 第1部分中的图1和图2仅在圆的排列方式上有所不同,并且这些图JavaScript代码中的transform
属性值(第1部分的清单2和4中所示)计算了每个圆的相对位置。中央。
确定各个组件相对位置的图形计算在D3术语中称为布局 。 D3提供了各种功能强大且可重复使用的布局。 第1部分中的弧线和弦的排列就是其中之一。 知道如何单独使用D3的布局以及结合您自己的图形计算使用D3的布局非常方便。
如图1所示,D3 包布局是一个较大圆内的一组圆。 (与第1部分中一样 ,圆圈描述了第1周的页面浏览量。)在本文中,我演示了pack布局的用法以及我自己的一些计算。
图1.包布局显示更大的圆圈内的圆圈
![D3包的布局:更大的圆圈内的圆圈](https://i-blog.csdnimg.cn/blog_migrate/f9db7bf41643698f9753c9b7b75a7bd2.png)
图1中的圆圈与第1部分中生成的圆圈相同,但布置不同。 差异源自D3的包装布局进行的图形计算。
没有外圆, 图1中的布局类似于图2:
图2.没有外圈的包装布局
![包装布局无外圆](https://i-blog.csdnimg.cn/blog_migrate/2171a461640ab2d47276133865eb1293.png)
第1部分的图1使用了一个简单的布局,该布局基于我自己的简单图形计算,而图2此处使用了D3的包布局。 如果将由两种布局完成的图形计算结合起来,则可以可视化数周的受欢迎程度数据,如图3所示:
图3.一张图中的三周受欢迎程度数据
您还可以将图3与第1部分的 图3的圆圈内组合在一起,以在几张画布上描绘几周的流行度数据以及用户交互数据,如图4所示:
图4.组合多种布局以显示几周的流行度数据和用户互动数据
现在,我将描述用于显示图1至4的基于SVG和D3JavaScript代码。
画一圈圆圈
清单1显示了绘制图1所示的圆圈的SVG代码:
清单1.绘制图1中的圆圈的SVG代码
<?xml version=";1.0"; standalone=";no";?>
<!DOCTYPE svg PUBLIC ";-//W3C//DTD SVG 1.1//EN";
";http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
<svg xmlns=";http://www.w3.org/2000/svg"; version=";1.1";
width=";1000"; height=";1000";>
<g>
<circle
r=";250";
style=";fill: #da70d6;";
transform=";translate(250,250)";>
</circle>
<text
x=";-10";
fill=";grey";
transform=";translate(250,250)";>
26733
</text>
</g>
<g>
<circle
r=";91.28916405756645";
style=";fill: #0000ff;";
transform=
";translate(172.869706621408,159.71405581800542)";>
</circle>
<text
x=";-10"; fill=";grey";
transform=
";translate(172.869706621408,159.71405581800542)";>
7057
</text></g>
<g>
<circle
r=";94.00415374552455";
style=";fill: #ffd700;";
transform=
";translate(120.90301328980084,337.57095449403647)";>
</circle>
<text
x=";-10";
fill=";grey";
transform=
";translate(120.90301328980084,337.57095449403647)";>
7483
</text>
</g>
<g>
<circle
r=";66.53756320431968";
style=";fill: #008000;";
transform=
";translate(271.77788076862765,282.7036851574789)";>
</circle>
<text
x=";-10";
fill=";grey";
transform=
";translate(271.77788076862765,282.7036851574789)";>
3749
</text>
</g>
<g>
<circle
r=";67.39284824138821";
style=";fill: #ff0000;";
transform=
";translate(405.70829221433553,282.7036851574789)";>
</circle>
<text
x=";-10";
fill=";grey";
transform=
";translate(405.70829221433553,282.7036851574789)";>
3846
</text>
</g>
<g>
<circle
r=";73.68747158608183";
style=";fill: #800000;";
transform=
";translate(337.8448727920879,159.01774033373852)";>
</circle>
<text
x=";-10";
fill=";grey";
transform=
";translate(337.8448727920879,159.01774033373852)";>
4598
</text>
</g>
</svg>
您可以看到清单1中的根<svg>
标记包含六个<g>
子标记,每个子标记都有一对<circle>
和<text>
标记。 第一个<g>
标记绘制了较大的外圆,我仅将其包括在内是为了让您看到D3的包装布局。 在示例应用程序中不需要外圈,因此我通过将其颜色从da70d6
(一种称为兰花的颜色)更改为fffff
(白色)来使其不可见,从而得到了图2而不是图1 。 其余五个<circle>
标签中的每一个都绘制了图2的五个圆圈之一,每个圆圈代表一种社交资源的受欢迎程度。
关于五个<circle>
标签,有两点值得注意:
- 每个
<circle>
标签的r
属性(每个圆的半径)代表该圆的社交资源的受欢迎程度。 (资源越受欢迎,圈越大。) -
transform
属性的translate(x,y)
值将每个圆放置在包中。
r
, x
和y
值完全确定每个圆的大小和位置。 D3的包布局为您计算这三个属性。 清单2显示了使用D3执行图形计算并生成清单1中的SVGJavaScript代码:
清单2.基于D3JavaScript代码生成圆的填充布局
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<!--
<script src="https://github.com/douglascrockford/JSON-js/blob/master/json2.js"></script>
-->
<script src="d3.v3.js"></script>
<!--Save d3.v3.js in the same folder-->
<script>
var views = [
[7057, 7483, 3749, 3846, 4598],
[ 2371, 7397, 4589, 2861, 8249],
[ 5972, 5672, 9152, 9725, 8983],
[ 9763, 8462, 9782, 1953, 5182],
[ 9567, 1571, 2895, 2783, 1874],
[ 2371, 7397, 4589, 2861, 8249]
];
var width = 1000, height = 1000;
var colors = [
"white",//"orchid", if you want to see the outer bigger circle.
"blue",
"gold",
"green",
"red",
"maroon"
];
var week = 0;
//Step 1: Make JSON representation of popularity data.
var viewsJSON = getJSONForOneWeekOfPopularityData(0);
//Step 2: Pass on JSON to pack layout and get graphical
//calculations in return.
var pack = d3.layout.pack()
.size([500, 500])
.value(function(d) { return d.popularity; });
var packCalculations = pack.nodes(viewsJSON);
//Step 3: Use calculations to generate SVG.
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.selectAll("g").data(packCalculations).enter();
var g = svg.append("g");
g.append("circle")
.attr("r", function(d){return d.r})
.style("fill", function(d, i){return colors[i%6];})
.attr("transform", function(d,i){return "translate(" + d.x + "," + d.y + ")"});
g.append("text")
.attr("x", -10)
.attr("fill", "grey")
.text(function(d){return d.value})
.attr("transform", function(d,i){return "translate(" + d.x + "," + d.y + ")"});
function getJSONForOneWeekOfPopularityData(week){
var viewsJSONString =
"{ \"name\": \"week" + week + "\", \"children\": [" ;
for (var count=0;
count<5;
count++ ){
viewsJSONString +=
"{ \"name\": \"" + colors[count+1] + " \", \"popularity\": "
+ views[week][count] + " } ";
if (count<4) viewsJSONString += ",";
}
viewsJSONString += "]}"
return JSON.parse(viewsJSONString);
}
</script>
</body>
您可以在清单2中看到三个步骤:
- 对受欢迎程度数据进行JSON表示,D3的包布局可用于进行图形计算。
- 将JSON对象传递到包布局并获得图形计算作为回报。
- 使用计算生成所需的SVG代码。
现在,我将描述每个步骤。
步骤1:使用JSON
包布局是分层布局:它以打包在较大(父)圆中的较小(子)圆的形式表示分层数据。 (D3并没有限制层次级别的数量;您可以有父母,祖父母,曾祖父母等等。)示例的受欢迎程度和用户交互数据只有两个级别:父级别是星期数字,子级别是每周的受欢迎程度和用户互动数据。
包布局不适用于JavaScript数组。 相反,它将数据的JSON表示形式作为输入。 JSON数据格式可以很好地表示分层数据,并且现代浏览器直接支持。
清单3展示了流行度数据的JSON表示形式与相同数据的数组表示形式:
清单3.三个星期的受欢迎程度数据的数组和JSON表示形式
//array representation
var views = [
[7057, 7483, 3749, 3846, 4598],
[ 2371, 7397, 4589, 2861, 8249],
[ 5972, 5672, 9152, 9725, 8983]
];
//JSON representation
var viewsJSON =
{
"name": "PopularityData",
"children": [
{
"name": "week1",
"children": [
{"name": "blue", "popularity": 7057},
{"name": "gold", "popularity": 7483},
{"name": "green", "popularity": 3749},
{"name": "red", "popularity": 3846},
{"name": "maroon", "popularity": 4598}
]
},
{
"name": "week2",
"children": [
{"name": "blue", "popularity": 2371},
{"name": "gold", "popularity": 7397},
{"name": "green", "popularity": 4589},
{"name": "red", "popularity": 2861},
{"name": "maroon", "popularity": 8249}
]
}
{
"name": "week3",
"children": [
{"name": "blue", "popularity": 5972},
{"name": "gold", "popularity": 5672},
{"name": "green", "popularity": 9152},
{"name": "red", "popularity": 9725},
{"name": "maroon", "popularity": 8983}
]
}
]};
请注意,JSON表示使用名称/值对。 整个数据对象的名称为PopularityData
。 PopularityData
有三个孩子,其中被命名为Week1
, Week2
和Week3
。 每个星期有五个孩子,每个孩子都是以社交资源命名的。 (正如您在第1部分中所回顾的那样,社会资源的名称是颜色名称。)所有这些对象都称为节点 ; 没有其他子节点的节点(即在树层次结构的末尾)称为叶节点 。 流行度数据是每个叶节点的popularity
属性的值。
清单4显示了如何仅用一周的流行度数据制作一个JSON对象:
清单4.一周的JSON格式的流行数据
var viewsJSON =
{
"name": "week1",
"children": [
{"name": "blue", "popularity": 7057},
{"name": "gold", "popularity": 7483},
{"name": "green", "popularity": 3749},
{"name": "red", "popularity": 3846},
{"name": "maroon", "popularity": 4598}
]
};
清单5显示了如何将用户交互添加到清单3的JSON对象中的流行度数据中:
清单5. JSON格式的三周受欢迎程度和用户交互数据
var viewsJSON =
{
"name": "PopularityData",
"children": [
{
"name": "week1",
"children": [
{"name": "blue", "popularity": 7057,
"user-interaction": 2052},
{"name": "gold", "popularity": 7483},
"user-interaction": 2089},
{"name": "green", "popularity": 3749},
"user-interaction": 1586},
{"name": "red", "popularity": 3846},
"user-interaction": 1426},
{"name": "maroon", "popularity": 4598}
"user-interaction": 2632},
]
},
{
"name": "week2",
"children": [
{"name": "blue", "popularity": 2371},
"user-interaction": 2071},
{"name": "gold", "popularity": 7397},
"user-interaction": 2190},
{"name": "green", "popularity": 4589},
"user-interaction": 7214},
{"name": "red", "popularity": 2861},
"user-interaction": 3782},
{"name": "maroon", "popularity": 8249}
"user-interaction": 2721},
]
}
{
"name": "week3",
"children": [
{"name": "blue", "popularity": 5972},
"user-interaction": 3076},
{"name": "gold", "popularity": 5672},
"user-interaction": 3190},
{"name": "green", "popularity": 9152},
"user-interaction": 4532},
{"name": "red", "popularity": 9725},
"user-interaction": 3825},
{"name": "maroon", "popularity": 8983}
"user-interaction": 4831},
]
}
]};
从清单5中可以看到,要添加用户交互数据,只需向每个叶子节点添加一个名为user-interaction
的属性。
您可以通过多种方式在JavaScript中使用JSON数据。 一种方法是将其直接包含在JavaScript文件中。 另一个方法是通过<script>
标记将JSON文件(扩展名为.json)链接到您的页面。 或者,您可以使用其他格式的数据(例如JavaScript数组),您可以在运行时将其动态转换为JSON。
将外部JSON文件与JavaScript页面链接可能会导致安全问题。 相反,在服务器端将JavaScript文件放在一起时,数组提供了一种简单明了的数据存储方式。 这就是为什么我选择第三个选项的原因:将数据存储在JavaScript数组中,并在同一JavaScript文件中使用数组到JSON的转换逻辑。 使用此选项,可以在需要数据的JSON表示形式时调用数组到JSON转换逻辑。
如您所料,要绘制图1中的布局,您需要一个星期流行度数据的JSON表示,其格式如清单4所示。 清单6中所示的名为getJSONForOneWeekOfPopularityData()
的简单JavaScript函数生成了以下格式的JSON对象:
清单6.从JavaScript数组生成JSON对象
function getJSONForOneWeekOfPopularityData(week){
//Step 1: Author a string to JSON format.
var viewsJSONString =
"{ \"name\": \"week" + week + "\", \"children\": [" ;
for (var count=0;
count<5;
count++ ){
viewsJSONString +=
"{ \"name\": \"" + colors[count+1] + " \", \"popularity\": "
+ views[week][count] + " } ";
if (count<4) viewsJSONString += ",";
}
viewsJSONString += "]}"
//Step 2: Parse the string to create a JSON object.
return JSON.parse(viewsJSONString);
}
</script>
</body>
从清单6中可以看到,生成JSON对象是一个简单的两步转换过程。 首先,您完全按照要生成的JSON格式编写JavaScript String
表示形式。 在编写字符串表示形式时,请使用社交资源的实际名称,以使其与清单4完全相同。 在第二步中,使用JSON.parse()
方法将字符串转换为JSON对象。 大多数现代浏览器都支持与JSON相关的功能,因此您可以直接调用JSON.parse()
函数。 我使用Chrome版本26.0.1410.64 m尝试了本文的代码。 如果您的应用程序针对较旧的浏览器,则可以通过<script>
标记在页面中包含https://github.com/douglascrockford/JSON-js/blob/master/json2.js库(实现JSON功能)。
步骤2:使用包装布局进行图形计算
现在有了合适的JSON对象,您可以将其传递给D3的包布局以执行所需的图形计算。 清单7(为方便起见,重复了清单2中的步骤2)显示了从包布局中获取图形计算是多么简单:
清单7.使用D3对一组圈子中一周的流行度数据执行图形计算
//Step 2: Pass on JSON to pack layout and get graphical
//calculations in return.
var pack = d3.layout.pack()
.size([500, 500])
.value(function(d) { return d.popularity; });
var packCalculations = pack.nodes(viewsJSON);
您只需要创建一个d3.layout.pack()
对象,然后通过调用其size()
函数来设置包布局的大小即可。 size()
函数将长度和宽度作为参数,这些参数设置绘制圆包的尺寸。 D3根据布局的大小在内部缩放每个圆的大小。
接下来,调用包布局对象的value()
函数,并编写自己的函数作为值。 此函数为每个JSON节点返回一个值。 在进行图形计算时,D3在内部为每个节点调用一次此函数,并将该节点作为d
参数传递。 您可以在清单7中看到,我返回d.popularity
作为节点的值,从而告诉D3基于流行度数据进行图形计算。 结果,根据节点的受欢迎程度数据来确定包中每个圆圈的大小。
现在,包布局对象已准备好接受JSON对象并进行计算。 接下来,我调用布局对象的nodes()
函数,并将JSON对象传递给该函数调用。
nodes()
函数执行所有图形计算,以将流行度圆圈放入包布局中。 计算结果采用节点数组的形式,每个节点代表一个圆的大小和绘制位置。 整个节点数组包含所有数据,以绘制整个流行圈子。
简而言之:将JavaScript数组转换为JSON,然后将JSON移交给D3的包布局。 pack布局执行图形计算,并将计算结果放入另一个数组(这次是节点数组)中,在清单7中 ,该数组名为packCalculations
。
对于packCalculations
数组中的每个节点,D3提供几个属性,包括r
, x
和y
。 现在,您将在清单1中看到如何使用这些属性来编写SVG代码。
步骤3:使用包装布局计算生成SVG
步骤2之后, packCalculations
数组将具有绘制圆包所需的所有图形数据。 步骤3(在清单8中重复)使用这些计算来生成SVG:
清单8.使用图形计算绘制圆
//Step 3: Use calculations to generate SVG.
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.selectAll("g").data(packCalculations).enter();
var g = svg.append("g");
g.append("circle")
.attr("r", function(d){return d.r})
.style("fill", function(d, i){return colors[i%6];})
.attr("transform", function(d,i)
{return "translate(" + d.x + "," + d.y + ")"});
g.append("text")
.attr("x", -10)
.attr("fill", "grey")
.text(function(d){return d.value})
.attr("transform", function(d,i)
{return "translate(" + d.x + "," + d.y + ")"});
在清单8中看到的大部分内容都是第1部分所熟悉的。 唯一的区别是这次的数据来自packCalculations
数组。 不必担心从阵列中取出每个节点并一一画出每个圆圈。 您只需将packCalculations
数组packCalculations
给神奇的.data(packCalculations)
D3函数即可处理大部分工作。
注意清单8中 dr
, dx
和dy
属性的使用。 要确定每个圆的大小和位置,您需要这些r
, x
和y
属性。 dr
属性用于创作圆的r
属性(半径),而dx
和dy
属性用于创作dy
每个圆的transform
属性的值。
如果运行清单2中JavaScript,您将看到图2所示的图像。
将外部计算与包装布局相结合
现在,我将解释如何将包布局与您自己的图形计算结合起来以绘制图3中的图形。 您可以看到, 图3使用圈子的包布局三次(每周一次),从而得到三组,每组五个圈子,其中一组代表一周的受欢迎程度数据。
清单9显示了图3的SVG代码:
清单9.显示三周受欢迎程度数据的SVG代码
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
width="1000" height="1000">
<g transform="translate(0,143)">
<text x="60">Week 1</text>
<!-- resource-popularity tags omitted.-->
</g>
<g transform="translate(320,143)">
<text x="60">Week 2</text>
<!-- resource-popularity tags omitted.-->
</g>
<g transform="translate(640,143)">
<text x="60">Week 3</text>
<!-- resource-popularity tags omitted.-->
</g>
</svg>
清单9显示了根<svg>
标记的三个<g>
子标记。 我将这三个<g>
标记称为Week-wrapper标记,因为每个标记代表一周的数据。
每个包装器标签都有六个<g>
子标签(较大的圆圈加上五个受欢迎的圆圈); 我将它们称为资源受欢迎度标签。 我省略了resource-popularity标签,因为从清单1中您知道它们的外观。
现在,查看week-wrapper标签的transform
属性。 它们的值放置第一组以点0、143为中心的圆; 第二个在320、143左右; 第三个是640、143。这个位置是我自己的简单图形计算的结果,该图形计算包装了整个圆圈。 现在,我将向您展示如何。
清单9中JavaScript生成SVG代码
为了生成清单9中的SVG, 清单10中JavaScript代码将D3的pack布局与我自己的一些图形计算结合在一起:
清单10.结合了包布局和更多图形计算JavaScript
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<!--
<script src="https://github.com/douglascrockford/JSON-js/blob/master/json2.js"></script>
-->
<script src="d3.v3.js"></script>
<script>
var views = [
[7057, 7483, 3749, 3846, 4598],
[ 2371, 7397, 4589, 2861, 8249],
[ 5972, 5672, 9152, 9725, 8983],
];
var colors = [
"white",
"blue",
"gold",
"green",
"red",
"maroon" ];
var width = 1500, height = 1000;
var widthOfOnePack = 300, heightOfOnePack = 300;
var spaceBetweenPacks = 20;
var packCalculations = [];
for (var count=0; count<3; count++){
var pack = d3.layout.pack()
.size([widthOfOnePack, heightOfOnePack])
.value(function(d) {
return d.popularity; });
packCalculations[count] =
pack.nodes(
getJSONForOneWeekOfPopularityData(count)
);
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g1 = svg.selectAll("g")
.data(packCalculations).enter().append("g")
.attr("transform", function(d,i){
return "translate(" +
(widthOfOnePack + spaceBetweenPacks )*i +
"," + height / 7 + ")" });
g1.append("text")
.text(function(d, i){return "Week " + (i+1)})
.attr("x", 60);
var g2 = g1.selectAll("g")
.data(function(d){return d;})
.enter()
.append("g");
g2.append("circle")
.attr("r", function(d, i){return d.r})
.style("fill", function(d, i){return colors[i%6];})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
g2.append("text")
.attr("x", -10)
.attr("fill", "grey")
.text(function(d, i){return d.value})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
function getJSONForOneWeekOfPopularityData(week){
var viewsJSONString =
"{ \"name\": \"week" + week + "\", \"children\": [" ;
for (var count=0;
count<5;
count++ ){
viewsJSONString +=
"{ \"name\": \"" + colors[count+1] + " \", \"popularity\": "
+ views[week][count] + " } ";
if (count<4) viewsJSONString += ",";
}
viewsJSONString += "]}"
return JSON.parse(viewsJSONString);
}
</script>
</body>
我在清单10中强调了packCalculations
和transform
增强功能,以帮助您将此代码与清单2进行比较。
在清单2中,我将packCalculations
声明为一个简单变量,而在清单10中,我将其声明为数组。 这是因为清单10处理了三包圆,并且每包都有自己的图形计算。
在声明packCalculations
数组之后,我运行一个简单的循环,在该循环中,我使用D3的pack布局执行计算以将每个圆圈定位在pack中。 D3每周返回一个节点数组。 我将每组D3的计算存储在packCalculations
数组中。 所以packCalculations
现在是节点数组的数组。
在这里注意一个有趣的观点。 D3的所有计算都是针对一个包装进行的,而与其他包装无关,就好像只处理一个包装一样。 这些计算不考虑其他包可能在同一SVG画布上。 但是,当transform
属性转换周包装标签的位置时,周包装内的所有内容都相对于包装定位-因此,您可以轻松地定位多包圆。
现在看一下清单10中以粗体显示的week-wrapper标签的transform
属性值的计算。 我将一包的宽度加上一点空白乘以索引i
。 此调整将第一个周包装器( i=0
)放置成没有任何水平位移。第二个周包装器( i=1
)被第一个周包装器的宽度加上少量空白水平移动。 类似地,第三周包装纸被前两个周包装纸的宽度和额外的空白所取代。
清单10的其余部分与清单2相同,在清单2中 ,您绘制了每个包的各个圆圈。 显然,您可以:
- 通过设计
<g>
标签的父子排列来处理布局。 - 使用每个
<g>
标记的transform
属性值将单个组件或完整的组件布局放置在所需位置。
圈子内圈子的打包布局
现在,我将向您展示一些有趣的增强功能,您可以对清单10进行一些改进,以在图3的布局内添加用户交互数据,从而形成如图4所示的一圈内圆。
清单11显示了绘制图4JavaScript:
清单11.绘制图4JavaScript
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<!--
<script src="https://github.com/douglascrockford/JSON-js/blob/master/json2.js"></script>
-->
<script src="d3.v3.js"></script>
<script>
var viewsAndInteraction = [
[ [7057, 2052], [7483, 2089], [3749, 1586], [3846, 1426], [4598, 2632] ],
[ [5972, 2071], [5672, 2190], [9152, 7214], [9725, 3782], [8983, 2721] ],
[ [8749, 3076], [4768, 3190], [6738, 4532], [9546, 3825], [6983, 4831] ]
];
var viewColors = [ "white", "blue", "gold", "green", "red", "maroon" ];
var interactionColors = [ "white", "lightblue", "yellow", "lightgreen",
"lightcoral", "indianred" ];
var width = 1500, height = 1000;
var widthOfOnePack = 300, heightOfOnePack = 300;
var spaceBetweenPacks = 20;
var packCalculations = [];
for (var count=0;
count<3;
count++ ){
var pack = d3.layout.pack()
.size([widthOfOnePack, heightOfOnePack])
.value(function(d) { return d.size; });
packCalculations[count] = pack.nodes(
getJSONForOneWeekOfPopularityAndInteractionData(
count));
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g1 = svg.selectAll("g")
.data(packCalculations).enter().append("g")
.attr("transform", function(d,i){
return "translate(" +
(widthOfOnePack + spaceBetweenPacks)*i + "," +
height / 7 + ")" }
);
g1.append("text")
.text(function(d, i){return "Week " + (i+1)})
.attr("x", 60);
var g2 = g1.selectAll("g")
.data(function(d){return d;})
.enter()
.append("g");
g2.append("circle")
.attr("r", function(d, i){return d.r})
.style("fill", function(d, i){return viewColors[i%6];})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
g2.append("circle")
.attr("r", function(d, i){
return ((d.interaction*d.r)/d.value);})
.style("fill", function(d, i){return interactionColors[i%6];})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
g2.append("text")
.attr("x", -15)
.attr("y", 35)
.attr("fill", "white")
.text(function(d, i){return d.value})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
g2.append("text")
.attr("x", -15)
.attr("y", 5)
//.attr("fill", "grey")
.text(function(d, i){return d.interaction})
.attr("transform", function(d,i){
return "translate(" + d.x + "," + d.y + ")"});
function getJSONForOneWeekOfPopularityAndInteractionData(week){
var viewsAndInteractionJSON =
"{ \"name\": \"week" + week + "\", \"children\": [" ;
for (var count=0; count<5; count++){
viewsAndInteractionJSON +=
"{ \"name\": \"" + viewColors[count+1] +
" \", \"size\": \"" + viewsAndInteraction[week][count][0] +
"\", \"interaction\": \"" +
viewsAndInteraction[week][count][1] + "\" } ";
if (count<4) viewsAndInteractionJSON += ",";
}
viewsAndInteractionJSON += "]}";
return JSON.parse(viewsAndInteractionJSON) ;
}
</script>
</body>
清单11中的增强功能(以粗体显示)为:
- 数组到JSON的转换逻辑更加复杂,因为这一次您必须生成清单5的JSON,其中每个叶节点都有两段数据:流行度和用户交互。
- 包布局对象的
value()
函数仍然使用d.popularity
属性,而完全忽略了user-interaction
属性。 包装布局的图形计算仍基于受欢迎程度数据。 用户交互数据进入图片仅是在外部受欢迎程度圈子内绘制一个内部圈子。 注意内部用户交互圈的r
属性值(半径)的编写, 清单11以粗体显示。 这就是用户交互数据起作用的地方。 为了计算内部圆的半径,我将用户交互数据乘以外部(受欢迎度)圆的半径与实际受欢迎程度数据的比率。 这种小的图形操作按比例缩放了内圆,该比例与D3进行内部计算所使用的比例成比例。
条形图表示受欢迎程度和用户互动数据
我讨论了通过使用不同布局的圆圈表示社交数据。 现在,我添加了一些变化。 例如,图5显示了除了圆圈以外的条形图,它们代表相同的社交数据:
图5.社会数据的条形图表示
您可以看到, 图5中的条形图也代表了相同的三周受欢迎程度和用户互动数据。 条形图使用与绘制内圈和外圈相同的颜色和阴影。 每个条的实线部分表示受欢迎程度,阴影部分较浅的部分表示用户交互。
清单12显示了绘制图5中的条形图的SVG代码(省略了圆圈的代码):
清单12.条形图的SVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
width="1500" height="1000">
<g>
<!--Tags for circles omitted.-->
</g>
<g>
<g transform="translate(100,250)">
<g transform="translate(0,200)">
<rect height="141.14" width="20" fill="blue"></rect>
<rect height="41.04" width="20" fill="lightblue"></rect>
</g>
<g transform="translate(22,200)">
<rect height="149.66" width="20" fill="gold"></rect>
<rect height="41.78" width="20" fill="yellow"></rect>
</g>
<g transform="translate(44,200)">
<rect height="74.98" width="20" fill="green"></rect>
<rect height="31.72" width="20" fill="lightgreen"></rect>
</g>
<g transform="translate(66,200)">
<rect height="76.92" width="20" fill="red"></rect>
<rect height="28.52" width="20" fill="lightcoral"></rect>
</g>
<g transform="translate(88,200)">
<rect height="91.96" width="20" fill="maroon"></rect>
<rect height="52.64" width="20" fill="indianred"></rect>
</g>
</g>
<g transform="translate(420,250)">
<g transform="translate(0,200)">
<rect height="119.44" width="20" fill="blue"></rect>
<rect height="41.42" width="20" fill="lightblue"></rect>
</g>
<g transform="translate(22,200)">
<rect height="113.44" width="20" fill="gold"></rect>
<rect height="43.8" width="20" fill="yellow"></rect>
</g>
<g transform="translate(44,200)">
<rect height="183.04" width="20" fill="green"></rect>
<rect height="144.28" width="20" fill="lightgreen"></rect>
</g>
<g transform="translate(66,200)">
<rect height="194.5" width="20" fill="red"></rect>
<rect height="75.64" width="20" fill="lightcoral"></rect>
</g>
<g transform="translate(88,200)">
<rect height="179.66" width="20" fill="maroon"></rect>
<rect height="54.42" width="20" fill="indianred"></rect>
</g>
</g>
<g transform="translate(740,250)">
<g transform="translate(0,200)">
<rect height="174.98" width="20" fill="blue"></rect>
<rect height="61.52" width="20" fill="lightblue"></rect>
</g>
<g transform="translate(22,200)">
<rect height="95.36" width="20" fill="gold"></rect>
<rect height="63.8" width="20" fill="yellow"></rect>
</g>
<g transform="translate(44,200)">
<rect height="134.76" width="20" fill="green"></rect>
<rect height="90.64" width="20" fill="lightgreen"></rect>
</g>
<g transform="translate(66,200)">
<rect height="190.92" width="20" fill="red"></rect>
<rect height="76.5" width="20" fill="lightcoral"></rect>
</g>
<g transform="translate(88,200)">
<rect height="139.66" width="20" fill="maroon"></rect>
<rect height="96.62" width="20" fill="indianred"></rect>
</g>
</g>
</g>
</svg>
您可以在清单12中看到<g>
标记的熟悉排列。 三个<g>
标签每个包装一周的数据。 然后,每个包装器标签都有五个子<g>
标签,每个子标签又包装了一对<rect>
标签以表示实际的受欢迎程度和交互数据。 <rect>
标签绘制矩形条,就像<circle>
标签绘制圆形一样。
每个<rect>
标签都有width
和height
属性。 width
属性值对于所有<rect>
标签都是固定的, height
属性值与矩形条表示的社交数据成比例。 条形图的固定宽度和可变高度给人以条形图的印象。
再次, <g>
标记的transform
属性将每个条形放置在它们的正确位置。 换句话说,圆形和条形的SVG代码之间没有根本区别。
本文的源代码下载中包括用于生成图5中条形图的基于D3JavaScript代码。
添加导航数据
通常,您需要在一页上显示各种图形组件,因此,我将所有内容放在一张SVG画布上作为结束。 图6使用您在第1部分中看到的弦图添加了三周的导航数据:
图6.单个图形中的圆,圆弧,和弦,布局和条形图
结语
在本系列的简短内容中,您已经进行了很长的数据可视化旅程-从第1部分的图1的谦逊圆圈到本文的复杂安排。 我希望这些文章能激发您的胃口,以了解D3的其他功能,您可以通过浏览D3网站来发现这些功能。
D3的一大优点是它基于JavaScript,这是顶级业务应用程序的首选前端技术。 如果使用支持JavaScript的业务应用程序平台(例如IBM Business Process Manager),则可以将本系列文章中的图形功能直接构建到业务应用程序中。 要了解IBM Business Process Manager及其对外部JSON和JavaScript库的支持,请参阅参考资料,以获得指向IBM Business Process Manager信息中心的链接。
翻译自: https://www.ibm.com/developerworks/opensource/library/os-dataviz2/index.html
饿了么布局组件