zoukankan      html  css  js  c++  java
  • 【 D3.js 高级系列 — 3.0 】 堆栈图

    堆栈图布局(Stack Layout)能够计算二维数组每一数据层的基线,以方便将各数据层叠加起来。本文讲解堆栈图的制作方法。

    301

    先说说什么是堆栈图。

    例如,有如下情况:

    某公司,销售三种产品:个人电脑、智能手机、软件。

    2005年,三种产品的利润分别为3000、2000、1100万。

    2006年,三种产品的利润分别为1300、4000、1700万。

    计算可得,2005年总利润为6100万,2006年为7000万。

    如果要将2005年的利润用柱形表示,那么应该画三个矩形,三个矩形堆叠在一起。这时候就有一个问题:每一个矩形的起始y坐标是多少,高应该是多少。

    输入数组,直接计算出上述问题的,就是堆栈图布局。


    1. 数据

    某公司销售个人电脑、智能手机、软件的数据如下:

    var dataset = [
    { name: "PC" , 
    		  sales: [	{ year:2005, profit: 3000 },
    					{ year:2006, profit: 1300 },
    					{ year:2007, profit: 3700 },
    					{ year:2008, profit: 4900 },
    					{ year:2009, profit: 700 }] },
    		{ name: "SmartPhone" , 
    		  sales: [	{ year:2005, profit: 2000 },
    					{ year:2006, profit: 4000 },
    					{ year:2007, profit: 1810 },
    					{ year:2008, profit: 6540 },
    					{ year:2009, profit: 2820 }] },
    		{ name: "Software" , 
    		  sales: [	{ year:2005, profit: 1100 },
    					{ year:2006, profit: 1700 },
    					{ year:2007, profit: 1680 },
    					{ year:2008, profit: 4000 },
    					{ year:2009, profit: 4900 }] }
        ];

    dataset是一个数组,数组的每一项是一个对象,对象里含有name和sales。name是产品名、sales是销售情况。sales也是一个数组,每一项也是对象,对象里包含有year表示年份、profit表示利润。

    现要将此数据绘制成堆栈图。


    2. 布局(数据转换)

    首先创建堆栈图布局。

    var stack = d3.layout.stack()
    					.values(function(d){ return d.sales; })
    					.x(function(d){ return d.year; })
    					.y(function(d){ return d.profit; });

    values访问器指定的是d.sales,表示接下来接收的数组,要计算的数据在数组每一项的变量sales中。x访问器指定的是d.year,y访问器指定的是d.profit,都是相对于values访问器指定的对象说的,即sales数组每一项的变量year和profit。

    以dataset为stack参数,结果保存在data中:

    	var data = stack(dataset);
    	console.log(data);

    要注意,转换之后原数据也会改变,因此dataset和data的值是一样的。data的输出值如图1所示。可以看到,sales的每一项都多了两个值:y0和y。y0即该层起始坐标,y是高度。x坐标有就是year。

    302

    图1


    3. 绘制

    首先要创建x轴和y轴比例尺,在添加图形元素和坐标轴的时候都要用到。要绘制坐标轴,要立刻想到要给坐标轴的刻度留出一部分空白。先定义一个外边框对象。

    var padding = { left:50, right:100, top:30, bottom:30 };

    右边部分留出的空白较多,是为了在后面添加标签的。x轴比例尺的定义如下:

    var xRangeWidth = width - padding.left - padding.right;
    		
    	var xScale = d3.scale.ordinal()
    					.domain( data[0].sales.map(function(d){ 
    return d.year; 
    }))
    					.rangeBands([0, xRangeWidth],0.3);

    本例中x轴代表年份,2005年、2006年、2007年等,是离散的,也就是说比例尺的定义域是离散的。从第5章的内容可知,序数比例尺d3.scale.ordinal的定义域是离散的。上面代码将定义域设定成:

    [2005, 2006, 2007, 2008, 2009]

    值域是根据rangeBands()计算的,实际是:

    [31, 134, 238, 342, 446]		//(省略了小数点)

    因此,在2005年处堆叠的矩形的x坐标为31。

    然后再创建y轴的比例尺。

            //最大利润(定义域的最大值)
    	var maxProfit = d3.max(data[data.length-1].sales, function(d){ 
    							return d.y0 + d.y; 
    					});
    	
    	//最大高度(值域的最大值)
    	var yRangeWidth = height - padding.top - padding.bottom;
    	
    	var yScale = d3.scale.linear()
    					.domain([0, maxProfit])		//定义域
    					.range([0, yRangeWidth]);		//值域

    这段代码中,求最大利润时,是对data数组中的最后一项data[2]求取sales的最大值。这是因为data[2]代表着最高的层,如图2所示,data[2].sales中的各项y0+y,必定比data[1]和data[0]的大。因此,只要用d3.max()求取data[2].sales中的最大值即可。值域是SVG的高度减去外边框的上下宽度。然后为d3.scale.linear()设定定义域和值域即可。

    303

    图2

    有了比例尺后,需要添加足够数量的分组元素<g>,每一个分组代表一种产品,每一种产品都用一种颜色来标识。

            //颜色比例尺
    	var color = d3.scale.category10();
    	
    	//添加分组元素
    	var groups = svg.selectAll("g")
    					.data(data)
    					.enter()
    					.append("g")
    					.style("fill",function(d,i){ return color(i); });

    现在添加了3个分组,分别代表PC、SmartPhone、Software,且每一个分组元素的fill都设定了颜色。接下来为每个分组添加矩形元素。

            //添加矩形
    	var rects = groups.selectAll("rect")
    					.data(function(d){ return d.sales; })
    					.enter()
    					.append("rect")
    					.attr("x",function(d){ return xScale(d.year); })
    					.attr("y",function(d){ 
    return yRangeWidth - yScale( d.y0 + d.y ); 
    })
    					.attr("width",function(d){ 
    return xScale.rangeBand(); 
    })
    					.attr("height",function(d){ return yScale(d.y); })
    					.attr("transform","translate(" + padding.left + "," 
       + padding.top + ")");

    每一个分组元素里还要绑定数组sales,以添加足够数量(每个分组5个)的矩形。然后再使用比例尺为矩形的x、y、width、height属性赋值。再添加上坐标轴后,结果如图3所示。

    304

    图3

    但是,什么颜色代表什么产品从图里看不出来。解决此问题最常用的方法是,在图表旁边添加几个图形标志,旁边加上文字,告诉用户某种颜色对应的是什么。在分组里继续添加图形元素,代码如下。

            var labHeight = 50;
    	var labRadius = 10;
    	
    	var labelCircle = groups.append("circle")
    							.attr("cx",function(d){ 
    return width - padding.right*0.98; 
    })
    							.attr("cy",function(d,i){ 
    return padding.top * 2 + labHeight * i; 
    })
    							.attr("r",labRadius);
    					
    	var labelText = groups.append("text")
    						.attr("x",function(d){ 
    return width - padding.right*0.8; 
    })
    						.attr("y",function(d,i){ 
    return padding.top * 2 + labHeight * i; 
    })
    						.attr("dy",labRadius/2)
    						.text(function(d){ return d.name; });

    用圆和文字作为标签,添加到图表的右边。最终结果如图4所示。

    301

    图4

    完整代码请点击以下网址,再右键查看源代码:

    http://www.ourd3js.com/demo/G-3.0/stack.html

    谢谢阅读。

    文档信息

  • 相关阅读:
    发布SpringBoot项目到Docker容器
    Docker网络
    Docker镜像发布到阿里云
    Docker制作Tomcat镜像
    DockerFile指令
    Docker命令
    kafka安装与使用
    刷题第2天
    刷题第1天
    UVA 11107 Life Forms
  • 原文地址:https://www.cnblogs.com/new0801/p/6176782.html
Copyright © 2011-2022 走看看