d3.js学习5----比例尺和坐标轴

【 D3.js 入门系列 — 4 】 比例尺的使用
发表于2014/06/20
比例尺是 D3 中很重要的一个概念,上一章里曾经提到过直接用数值的大小来代表像素不是一种好方法,本章正是要解决此问题。


1. 为什么需要比例尺


上一章制作了一个柱形图,当时有一个数组:




var dataset = [ 250 , 210 , 170 , 130 , 90 ];


绘图时,直接使用 250 给矩形的宽度赋值,即矩形的宽度就是 250 个像素。
此方式非常具有局限性,如果数值过大或过小,例如:




var dataset_1 = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var dataset_2 = [ 2500, 2100, 1700, 1300, 900 ];
对以上两个数组,绝不可能用 2.5 个像素来代表矩形的宽度,那样根本看不见;也不可能用 2500 个像素来代表矩形的宽度,因为画布没有那么长。


于是,我们需要一种计算关系,能够:


将某一区域的值映射到另一区域,其大小关系不变。


这就是比例尺(Scale)。


2. 有哪些比例尺


比例尺,很像数学中的函数。例如,对于一个一元二次函数,有 x 和 y 两个未知数,当 x 的值确定时,y 的值也就确定了。


在数学中,x 的范围被称为定义域,y 的范围被称为值域。


D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。


D3 提供了多种比例尺,下面介绍最常用的两种。


2.1. 线性比例尺


线性比例尺,能将一个连续的区间,映射到另一区间。要解决柱形图宽度的问题,就需要线性比例尺。


假设有以下数组:
var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
 
现有要求如下:
将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。


代码如下:
var min = d3.min(dataset);
var max = d3.max(dataset);


var linear = d3.scale.linear()
        .domain([min, max])
        .range([0, 300]);


linear(0.9);    //返回 0
linear(2.3);    //返回 175
linear(3.3);    //返回 300
   


 
其中,d3.scale.linear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域。在这里还用到了两个函数,它们经常与比例尺一起出现:


d3.max()
d3.min()
这两个函数能够求数组的最大值和最小值,是 D3 提供的。按照以上代码,


比例尺的定义域 domain 为:[0.9, 3.3]


比例尺的值域 range 为:[0, 300]


因此,当输入 0.9 时,返回 0;当输入 3.3 时,返回 300。当输入 2.3 时呢?返回 175,这是按照线性函数的规则计算的。


有一点请大家记住:


d3.scale.linear() 的返回值,是可以当做函数来使用的。因此,才有这样的用法:linear(0.9)。


2.2. 序数比例尺


有时候,定义域和值域不一定是连续的。例如,有两个数组:
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];
 
我们希望 0 对应颜色 red,1 对应 blue,依次类推。


但是,这些值都是离散的,线性比例尺不适合,需要用到序数比例尺。




var ordinal = d3.scale.ordinal()
        .domain(index)
        .range(color);


ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black


用法与线性比例尺是类似的。


3. 给柱形图添加比例尺


在上一章的基础上,修改一下数组,再定义一个线性比例尺。


var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];


var linear = d3.scale.linear()
        .domain([0, d3.max(dataset)])
        .range([0, 250]);




其后,按照上一章的方法添加矩形,在给矩形设置宽度的时候,应用比例尺。




var rectHeight = 25;   //每个矩形所占的像素高度(包括空白)


svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",20)
    .attr("y",function(d,i){
         return i * rectHeight;
    })
    .attr("width",function(d){
         return linear(d);   //在这里用比例尺
    })
    .attr("height",rectHeight-2)
    .attr("fill","steelblue");
var rectHeight = 25;   //每个矩形所占的像素高度(包括空白)
 
svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",20)
    .attr("y",function(d,i){
         return i * rectHeight;
    })
    .attr("width",function(d){
         return linear(d);   //在这里用比例尺
    })
    .attr("height",rectHeight-2)
    .attr("fill","steelblue");
如此一来,所有的数值,都按照同一个线性比例尺的关系来计算宽度,因此数值之间的大小关系不变。




代码:
<html>  
<head>  
<meta charset="utf-8">  
<title>给柱形图添加比例尺</title>  
</head>


<body>  


<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
<script>


var width = 300; //画布的宽度
var height = 300; //画布的高度


var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度

var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];


var linear = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, 250]);


var rectHeight = 25; //每个矩形所占的像素高度(包括空白)


svg.selectAll("rect")
 .data(dataset)
 .enter()
 .append("rect")
 .attr("x",20)
 .attr("y",function(d,i){
return i * rectHeight;
 })
 .attr("width",function(d){
  return linear(d);
 })
 .attr("height",rectHeight-2)
 .attr("fill","steelblue");

</script>  

</body>  

</html>  





坐标轴:


坐标轴,是可视化图表中经常出现的一种图形,由一些列线段和刻度组成。坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成。D3 提供了坐标轴的组件,如此在 SVG 画布中绘制坐标轴变得像添加一个普通元素一样简单。
 
1. 坐标轴由什么构成


在 SVG 画布的预定义元素里,有六种基本图形:


矩形 <rect>
圆形 <circle>
椭圆 <ellipse>
线段 <line>
折线 <polyline>
多边形 <polygon>
另外,还有一种比较特殊,也是功能最强的元素:
路径 <path>


画布中的所有图形,都是由以上七种元素组成。


显然,这里面没有坐标轴 <axis> 这种元素。如果有的话,我们可以采用类似以下的方式定义:


<axis x1="" x2="" ...></axis>
 
很可惜,没有这种元素。但是,这种设计是合理的:不可能为每一种图形都配备一个单独的元素,那样 SVG 就会过于庞大。


因此,我们需要用其他元素来组合成坐标轴,最终使其变为类似以下的形式:




<g>
<!-- 第一个刻度 -->
<g>
<line></line>   <!-- 第一个刻度的直线 -->
<text></text>   <!-- 第一个刻度的文字 -->
</g>
<!-- 第二个刻度 -->
<g>
<line></line>   <!-- 第二个刻度的直线 -->
<text></text>   <!-- 第二个刻度的文字 -->
</g> 
...
<!-- 坐标轴的轴线 -->
<path></path>
</g>
 
分组元素 <g>,是 SVG 画布中的元素,意思是 group。此元素是将其他元素进行组合的容器,在这里是用于将坐标轴的其他元素分组存放。


如果需要手动添加这些元素就太麻烦了,为此,D3 提供了一个组件:d3.svg.axis()。它为我们完成了以上工作。


2. 定义坐标轴


上一章提到了比例尺的概念,要生成坐标轴,需要用到比例尺,它们二者经常是一起使用的。下面,在上一章的数据和比例尺的基础上,添加一个坐标轴的组件。




//数据
var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
//定义比例尺
var linear = d3.scale.linear()
      .domain([0, d3.max(dataset)])
      .range([0, 250]);


var axis = d3.svg.axis()
     .scale(linear)      //指定比例尺
     .orient("bottom")   //指定刻度的方向
     .ticks(7);          //指定刻度的数量
 
第 1 – 2 行:定义数组。


第 4 – 7 行:定义比例尺,其中使用了数组 dataset。


第 9 – 12 行:定义坐标轴,其中使用了线性比例尺 linear。其中:


d3.svg.axis():D3 中坐标轴的组件,能够在 SVG 中生成组成坐标轴的元素。
scale():指定比例尺。
orient():指定刻度的朝向,bottom 表示在坐标轴的下方显示。
ticks():指定刻度的数量。
3. 在 SVG 中添加坐标轴


定义了坐标轴之后,只需要在 SVG 中添加一个分组元素 <g>,再将坐标轴的其他元素添加到这个 <g> 里即可。代码如下:




svg.append("g")
   .call(axis);
 
上面有一个 call() 函数,其参数是前面定义的坐标轴 axis。


在 D3 中,call() 的参数是一个函数。调用之后,将当前的选择集作为参数传递给此函数。也就是说,以下两段代码是相等的。


function foo(selection) {
  selection
      .attr("name1", "value1")
      .attr("name2", "value2");
}
foo(d3.selectAll("div"))
 

d3.selectAll("div").call(foo);
 
因此,
svg.append("g").call(axis);

axis(svg.append(g));
 
是相等的。


4. 设定坐标轴的样式和位置


默认的坐标轴样式不太美观,下面提供一个常见的样式:


<style>
.axis path,
.axis line{
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}


.axis text {
    font-family: sans-serif;
    font-size: 11px;
}
</style>
分别定义了类 axis 下的 path、line、text 元素的样式。接下来,只需要将坐标轴的类设定为 axis 即可。


坐标轴的位置,可以通过 transform 属性来设定。


通常在添加元素的时候就一并设定,写成如下形式:


svg.append("g")
  .attr("class","axis")
  .attr("transform","translate(20,130)")
  .call(axis);
 
代码:
<html>  
<head>  
<meta charset="utf-8">  
<title>坐标轴</title>  
</head> 


<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}


.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>


<body>  
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>  
<script>


        var width = 300; //画布的宽度
var height = 300; //画布的高度


var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度


var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];


var linear = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, 250]);


var rectHeight = 25; //每个矩形所占的像素高度(包括空白)


svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linear(d);
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");


var axis = d3.svg.axis()
.scale(linear) //指定比例尺
.orient("bottom") //指定刻度的方向
.ticks(7); //指定刻度的数量


svg.append("g")
.attr("class","axis")
.attr("transform","translate(20,130)")
.call(axis);


</script>  
</body>  
</html>  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值