比例尺是 D3 中很重要的一个概念。绘制图形时直接用数值的大小来代表像素不是一种好方法,本章正是要解决此问题。

一、为什么需要比例尺

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

var dataset_1 = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var dataset_2 = [ , , , , ];

  对以上两个数组,绝不可能用 2.5 个像素来代表矩形的宽度,那样根本看不见;也不可能用 2500 个像素来代表矩形的宽度,因为画布没有那么长。于是,我们需要一种计算关系,能够:将某一区域的值映射到另一区域,其大小关系不变。这就是比例尺(Scale)。

二、有哪些比例尺

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

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

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

1、线性比例尺

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

  假设有以下数组,现有要求如下:将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。代码如下:

        var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];

        var min = d3.min(dataset);
var max = d3.max(dataset); var linear = d3.scale.linear()
.domain([min, max])//注意:domain()/range()里面是个数组形式哦
.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、序数比例尺

  有时候,定义域和值域不一定是连续的。例如,有两个数组:

var index = [, , , , ];
var color = ["red", "blue", "green", "yellow", "black"];

  我们希望 0 对应颜色 red,1 对应 blue,依次类推。但是,这些值都是离散的,线性比例尺不适合,需要用到序数比例尺。

var ordinal = d3.scale.ordinal()
.domain(index)
.range(color); ordinal(); //返回 red
ordinal(); //返回 green
ordinal(); //返回 black

  序数比例尺:d3.scale.ordinal();用法与线性比例尺是类似的。

三、坐标轴由什么构成

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

  在 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()。它为我们完成了以上工作。

四、定义坐标轴

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

//数据
var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
//定义比例尺
var linear = d3.scale.linear()
.domain([, d3.max(dataset)])
.range([, ]); var axis = d3.svg.axis()
.scale(linear) //指定比例尺
.orient("bottom") //指定刻度的方向
.ticks(); //指定刻度的数量

  主要看下定义坐标轴的方法,其中使用了线性比例尺 linear。其中:

  d3.svg.axis():D3 中坐标轴的组件,能够在 SVG 中生成组成坐标轴的元素。

  scale():指定比例尺。

  orient():指定刻度的朝向,bottom 表示在坐标轴的下方显示。

  ticks():指定刻度的数量。

五、在 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));

六、设定坐标轴的样式和位置

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

<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>
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
} .axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = ; //画布的宽度
var height = ; //画布的高度 var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度 var dataset = [ 2.5 , 2.1 , 1.8 , 1.3 , 0.9 ]; //数据(表示矩形的宽度)
var linear = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([0,250]);
var rectHeight = ; //每个矩形所占的像素高度(包括空白) svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linear(d); // 此处应用比例尺
})
.attr("height",rectHeight-)
.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>

最新文章

  1. 【iOS】NSNumberFormatter
  2. OC--编码建议
  3. WPF:依赖属性的应用
  4. Hadoop家族 路线图(转)
  5. 如何安装sass
  6. Android 退出app,后台推送的服务也停止了,怎么可以做到不停止后台服务呢?
  7. [转]如何:在设备上安装 SQL Server Compact 3.5
  8. NeHe OpenGL教程 第十一课:飘动的旗帜
  9. iOS设置导航栏样式(UINavigationController)
  10. Revit二次开发-BIM模型导出
  11. Share SDK分享
  12. 专门为公共部门和联邦机构所设计Microsoft Azure
  13. android之PackageManager简单介绍
  14. UI和UE有什么区别呢?
  15. SQL的case when then else end语句的用法
  16. TCP连接之未连接队列的理解[转]
  17. ANT与SVN集成
  18. XMLHttpRequest中常用的方法
  19. R-CNN,SPP-NET, Fast-R-CNN,Faster-R-CNN, YOLO, SSD, R-FCN系列深度学习检测方法梳理
  20. iOS - 记住用户登录状态保存用户名密码

热门文章

  1. POJ2495(棋盘分治,染色)
  2. PhpExcel一些使用方法
  3. pyhton mechanize 学习笔记
  4. 原创 gif png bmp jeg 显示方法
  5. docker从零开始 存储(四)tmpfs挂载
  6. django 实现自定义认证
  7. ASP.NET Core 2.2 基础知识(十四) WebAPI Action返回类型(未完待续)
  8. 使用IIFE(立即执行函数)让变量私有化
  9. RPD Volume 168 Issue 4 March 2016 评论7-end
  10. Python web 简单服务器的搭建与运行