一、第一个程序
选择集: 使用d3.select()或者 d3.selectAll()选择元素后返回的对象,就是选择集
d3能够连续不断地调用函数,形如:d3.select().selctAll().text()这称为链式语法
二、选择元素和绑定数据
在D3中,用于选择元素的函数有两个:
 d3.select():是选择所有指定元素的第一个
   d3.selectAll():是选择指定元素的全部
这两个函数返回的结果称为选择集,选择集合绑定数据通常是一起使用的
 
   D3 中通过以下两个函数来绑定数据:
   1)datum():绑定一个数据到选择集上
   2)data():绑定一个数组到选择集上,数组的各项值分别于选择集的各元素绑定
      var str="China";
        var body=d3.select(‘body‘);
        var p=body.selectAll("p");
        p.datum(str);
        p.text(function(d,i){
            return "第"+i+"个元素绑定的数据是"+d;
        });
	上面代码中,用到了一个无名函数 function(d,i),当选择集需要使用被绑定的数据时,
常需要这么使用,其包含两个参数,其中:
     d 代表数据,也就是与某元素绑定的数据
     i 代表索引,代表数据的索引号,从0开始
data()
有一个数组,接下来要分别将数组的各元素绑定到三个段落元素上
 var dataset = [‘I like dog‘,‘I like cat‘,‘I like snake‘];
        var body = d3.select("body");
        var p = body.selectAll("p");
       p.data(dataset)
               .text(function(d,i){
                   return d;
               });
三个段落元素与数组dataset 的三个字符串是一一对应
三、选择、插入、删除元素
 
  select  和selectAll的用法
假设 body中有三个段落元素:
  <p>Apple</p>
  <p>Pear</p>
  <p>Banana</p>
 
(1) 选择元素
  
现在,要分别完成以下四种选择元素的任务
1)选择第一个p元素
使用select,参数传入p即可
   var p1=d3.select("p");
        p1.style("color","red");
 2)选择三个 p 元素
  var body=d3.select("body");
        var p= body.selectAll("p");
       p.style("color","green");
3)选择第二个p 元素
   一种比较简单的是给第二个元素添加一个id号,然后使用select 选择元素,注意参数中
  id 名称前要加 # 号
     var p2= body.select("#myid");
        p2.style("color",‘red‘);
4)选择后两个p元素
   给后两个元素添加class
 
     var p=body.selectAll(".myclass");
        p.style("color","red");
  关于 select 和selectAll 的参数,其实是符合css 选择器的条件的,
  此外,对于已经绑定了数据的选择集,还有一种选择元素的方法,那就是灵活运用
  function(d,i),我们已经知道参数 i 是代表索引号的,于是便可以用条件判定语句来指定执行的元素
(2)插入元素
插入元素涉及两个函数:
      1)append():在选择集末尾插入元素
      2)insert():在选择集前面插入元素
   
  在body末尾添加一个p元素
  body.append("p")
  .text("append p element");
在 body 中 id 为 myid 的元素前添加一个段落元素。
    body.insert("p","#myid")
     .text("insert a element");
(3)删除元素
删除一个元素时,对于选择的元素,使用remove即可,例如:
   var p=body.select("#myid");
        p.remove();
四、做一个简单的图表
  柱形图是一种最简单的可视化图标,主要有 矩形、文字标签、坐标轴组成。
  
如何使用D3 在SVG画布中绘图
 
   1)画布
      要绘图,首要需要的是一块绘图的“画布”;
      HTML5 提供两种强有力的画布:SVG 和Canvas
   SVG 是什么:指可缩放矢量图形,用于描述二维矢量图形的一种图形格式。SVG使用XML
 格式来定义图形。绝大部分浏览器都支持SVG,可将SVG文本直接嵌入HTML中显示
SVG 有如下特点:
     1)SVG绘制的是矢量图,因此对图象进行放大不会失真
     2)基于XMl,可以为每个元素添加javaScript 事件处理器
     3)每个图形均视为对象,更改对象的属性,图形也会改变
     4)不适合游戏应用
 
  D3虽然没有明文规定一定要在SVG只绘图,但是D3提供了众多的 SVG 图形的生成器,它们都是
 只支持SVG的,因此建议使用SVG 画布
    var width = 300;
    var height = 300;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
    var dataset =[250 ,210 ,170 ,130,90];
    var rectHeight = 25;
    svg.selectAll("rect")
            .data(dataset)
            .enter()    //这个不能少
            .append("rect")  //添加rect
            .attr("x",20)    //设置x
            .attr("y",function(d,i){  //设置y
                return i* rectHeight;
            })
            .attr("width",function(d,i){ //设置宽度
                return d;
            })
            .attr("height",rectHeight-2) //设置高度
            .attr("fill","steelblue");
  这段代码添加了与dataset数组的长度相同数量的矩形,所使用的语句是
   svg.selectAll("rect")   //选择svg内所有的矩形
    .data(dataset)  //绑定数组
    .enter()        //指定选择集的enter部分
    .append("rect") //添加足够数量的矩形元素
   这段代码以后会常常出现在D3 的代码中,请务必牢记。当:有数据,而没有足够图形元素的时候
,使用此方法可以添加足够的元素
  添加了元素后,就需要分别给各元素的属性赋值。咋这里用到了function(d,i),前面已经讲过,
 d代表与当前元素绑定的数据,i代表索引号。给属性赋值的时候,是需要用到被绑定的数据,已经索引号的
五、比例尺的使用
 比例尺是D3中很重要的一个概念,上一章里曾经提到过直接用数值的大小来代表像素不是一种好办大
 本章正是要解决此问题。  
上面制作的柱形图,绘图时,直接使用数组中的值给矩形的宽度赋值,即矩形的宽度就是 250 个像素
此方式非常有局限性,如果数值过大或过小就不合理了。
于是,我们需要一种计算关系,能够 将某一区域的值映射到另一区域,其大小关系不变
这就是比例尺,那么有哪些比例尺呢
在数学中,x范围被称为定义域,y的范围被称为值域。
  D3中的比例尺,也有定义域和值域,分别被称为 domain 和range
   
  D3提供了多种比例尺,下面介绍最常用的两种
线性比例尺:
线性比例尺,能将一个连续的区间,映射到另一个区间。要解决柱形图宽度的问题,就需要线性比例尺。
   假设有以下数组:
     var dataset = [1.2,2.3,0.9,1.5,3.3];
   要求如下: 将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])
            .range([0,300]);
    console.log(linear(0.9)); //0
    console.log( linear(2.3)); //175
    console.log(linear(3.3));  //300
  其中,d3.scale.linear()返回一个线性比例尺。domain()和range()分别设定比例尺的
 定义域和值域。在这里还用到了两个函数,它们经常与比例尺一起出现:
	d3.max()
	d3.min()
   这两个函数能够求数组的最大值和最小值,是D3提供的。
   d3.scale.linear()的返回值,是可以当做函数来使用的。因此才有这样的用法:linear(0.9)
  
   序数比例尺
有时候,定义域和值域不一定是连续的。例如:
   	var index = [0,1,2,3,4];
        var color = ["red","blue","green","yellow","black"];
    我们希望0 对应颜色 red ,1对应blue,但是这些值都是离散的,线性比例尺不合适
  ,需要用到序数比例尺
     var index = [0,1,2,3,4];
    var color = ["red","blue","green","yellow","black"];
    var ordinal = d3.scale.ordinal()
            .domain(index)
            .range(color);
    console.log(ordinal(0));
    console.log(ordinal(2));
    console.log(ordinal(4));
   
  用法和线性比例尺是类似的。
给柱形图添加比例尺
    var width = 300;
    var height = 300;
    var svg = d3.select("body")
            .append("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")  //添加rect
            .attr("x",20)    //设置x
            .attr("y",function(d,i){  //设置y
                return i* rectHeight;
            })
            .attr("width",function(d,i){ //设置宽度
                return linear(d);
            })
            .attr("height",rectHeight-2) //设置高度
            .attr("fill","steelblue");
  六、 坐标轴
   坐标轴是可视化图表中经常出现的一种图形,由一些列线和刻度组成。坐标轴在SVG中
  是没有现成的图形元素的,需要用其他的元素组合构成。D3提供了坐标轴的组件。
坐标轴由什么构成
在SVG 画布的预定义元素里,有六种基本图形:
     1)矩形
     2)圆形
     3)椭圆
     4)线段
     5)折线
     6)多边形
另外,还有一种比较特殊,也是功能最强的元素:
   路径
 画布中的所有图形,都是由以上七种元素组成的
  
 D3提供了一个组件:d3.svg.axis(),它为我们完成了坐标轴的工作
  var axis = d3.svg.axis()
            .scale(linear)  //指定比例尺
            .orient("bottom")  //指定刻度的方向
            .ticks(7);  //指定刻度的数量
    
  定义了坐标轴之后,只需要在svg中添加一个分组元素,再将坐标轴的其他元素添加到
   这个分组元素中即可。代码如下:
       svg.append("g")
		.call(axis);
  上面有一个call()函数,其参数是前面定义的坐标轴axis
   
 在D3中,call()的参数是一个函数。调用之后,将当前的选择集作为参数传递给此函数。
设定坐标轴的样式和位置
  提供了一个常见的样式:
 <style>
        .axis path,.axis line{
            fill:none;
            stroke:black;
            shape-rendering: crispEdges;
        }
        .axis text{
            font-family:sans-serif;
            font-size:11px;
        }
    </style>
  
带刻度的柱状图
    var width = 300;
    var height = 300;
    var svg = d3.select("body")
            .append("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 axis = d3.svg.axis()
            .scale(linear)  //指定比例尺
            .orient("bottom")  //指定刻度的方向
            .ticks(7);  //指定刻度的数量
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate(20,130)")
            .call(axis);
    var rectHeight = 25;
    svg.selectAll("rect")
            .data(dataset)
            .enter()    //这个不能少
            .append("rect")  //添加rect
            .attr("x",20)    //设置x
            .attr("y",function(d,i){  //设置y
                return i* rectHeight;
            })
            .attr("width",function(d,i){ //设置宽度
                return linear(d);
            })
            .attr("height",rectHeight-2) //设置高度
            .attr("fill","steelblue");
  七、完整的柱形图
一个完整的柱形图包含三部分:矩形、文字、坐标轴。一下是完整柱形图:内容包括:选择集、数据绑定、比例尺、坐标轴等内容
    
  	 //添加SVG画布
    var width = 400;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
//画布周边的空白
var padding = {left:30,right:30,top:20,bottom:20};
    //定义数据和比例尺
    var dataset = [10,20,30,40,33,24,12,5];
    //x轴的比例尺
    var xScale = d3.scale.ordinal()
            .domain(d3.range(dataset.length))
            .rangeRoundBands([0,width - padding.left -padding.right]);
    //y轴比例尺
    var yScale = d3.scale.linear()
            .domain([0,d3.max(dataset)])
            .range([height - padding.top - padding.bottom,0]) //因为y轴默认是越往下越大,所以反着写的
//定义坐标轴
    //定义x轴
    var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom");
    //定义y轴
    var yAxis = d3.svg.axis()
            .scale(yScale)
            .orient("left");
 //添加矩形和文字元素
    //矩形之间的空白
    var rectPadding = 4;
    //添加矩形元素
    //添加矩形元素
    var rects = svg.selectAll(".MyRect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("class","MyRect")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                return yScale(d);
            })
            .attr("width", xScale.rangeBand() - rectPadding )
            .attr("height", function(d){
                return height - padding.top - padding.bottom - yScale(d);
            })
            .attr("fill",‘#e4393c‘);
    //添加文字元素
    var texts = svg.selectAll(".MyText")
            .data(dataset)
            .enter()
            .append("text")
            .attr("class","MyText")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                return yScale(d);
            })
            .attr("dx",function(){
                return (xScale.rangeBand() - rectPadding)/2;
            })
            .attr("dy",function(d){
                return 20;
            })
            .text(function(d){
                return d;
            });
   //添加坐标轴的元素  +padding.left+","+"+(height-padding.bottom)+"
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+(height-padding.bottom)+")")
            .call(xAxis);
     //添加y轴
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+padding.top+")")
            .call(yAxis);
  
八、 让图表动起来
   D3中制作动态图表称为 过渡
 
   实现动态的方法:
D3 提供了4个方法用于实现图形的过渡: 从状态A到状态 B
1)transition()
启动过渡效果
其前后是图形变化前后的状态(形状、位置、颜色等等) 例如:
   	.attr("fill","red")         //初始颜色为红色
	.transition()               //启动过渡
	.attr("fill","steelblue")   //终止颜色为铁蓝色
    D3会自动对两种颜色(红色和铁蓝色)之间的颜色值进行插值计算,得到过渡用的颜色值。我们无需
   知道中间是怎么计算的,只需要享受结果即可
   duration()
  
   指定过渡的持续时间 ,单位为毫秒
如 duration(2000)
   ease()
指定过渡方式,常用的有:
    linear:普通的线性变化
    circle:慢慢地到达变换的最终状态
    elastic:带有弹跳的到达最终状态
    bounce:在最终状态处弹跳几次
调用时,形如;ease("bounce")
delay()
   指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。此函数可以对整体指定延迟,
  也可以对个别指定延迟
例如:对整体指定时:
     .transition()
     .duration(1000)
     .delay(500)
又如,对一个个的图形进行指定:
   .transition()
   .duration(1000)
   .delay(function(d,i){return 200*i;})
  实现简单的动态效果
 
    var width = 400;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
    var circle1 = svg.append("circle")
            .attr("cx", 100)
            .attr("cy", 100)
            .attr("r", 45)
            .style("fill","green");
    //在1秒(1000毫秒)内将圆心坐标由100变为300
    circle1.transition()
            .duration(1000)
            .attr("cx", 300);
    var circle2 = svg.append("circle")
            .attr("cx", 100)
            .attr("cy", 200)
            .attr("r", 45)
            .style("fill","green");//与第一个圆一样,省略部分代码
    //在1.5秒(1500毫秒)内将圆心坐标由100变为300,
    //将颜色从绿色变为红色
    circle2.transition()
            .duration(1500)
            .attr("cx", 300)
            .style("fill","red");
    var circle3 = svg.append("circle")
            .attr("cx", 100)
            .attr("cy", 300)
            .attr("r", 45)
            .style("fill","green");//与第一个圆一样,省略部分代码
    circle3.transition()
            .duration(2000)
            .ease("bounce")
            .attr("cx", 300)
            .style("fill","red")
            .attr("r", 25);
动起来的柱状图
   <style>
        .axis path,.axis line{
            fill:none;
            stroke:black;
            shape-rendering: crispEdges;
        }
        .axis text{
            font-family:sans-serif;
            font-size:11px;
        }
    </style>
<script>
    //添加SVG画布
    var width = 400;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
//画布周边的空白
var padding = {left:30,right:30,top:20,bottom:20};
    //定义数据和比例尺
    var dataset = [10,20,30,40,33,24,12,5];
    //x轴的比例尺
    var xScale = d3.scale.ordinal()
            .domain(d3.range(dataset.length))
            .rangeRoundBands([0,width - padding.left -padding.right]);
    //y轴比例尺
    var yScale = d3.scale.linear()
            .domain([0,d3.max(dataset)])
            .range([height - padding.top - padding.bottom,0]) //因为y轴默认是越往下越大,所以反着写的
    //定义坐标轴
    //定义x轴
    var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom");
    //定义y轴
    var yAxis = d3.svg.axis()
            .scale(yScale)
            .orient("left");
    //添加矩形和文字元素
    //矩形之间的空白
    var rectPadding = 4;
    //添加矩形元素
    //添加矩形元素
    var rects = svg.selectAll(".MyRect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("class","MyRect")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .transition()
            .delay(function(d,i){
                return i*200;
            })
            .duration(2000)
            .ease("bounce")
            .attr("y",function(d){
                return yScale(d);
            })
            .attr("width", xScale.rangeBand() - rectPadding )
            .attr("height", function(d){
                return height - padding.top - padding.bottom - yScale(d);
            })
            .attr("fill",‘#e4393c‘);
    //添加文字元素
    var texts = svg.selectAll(".MyText")
            .data(dataset)
            .enter()
            .append("text")
            .attr("class","MyText")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                var min = yScale.domain()[0];
                return yScale(min);
            })
            .transition()
            .delay(function(d,i){
                return i*200;
            })
            .duration(2000)
            .ease("bounce")
            .attr("y",function(d){return yScale(d)})
            .attr("dx",function(){
                return (xScale.rangeBand() - rectPadding)/2;
            })
            .attr("dy",function(d){
                return 20;
            })
            .style("stroke","#fff")
            .text(function(d){
                return d;
            });
    //添加坐标轴的元素  +padding.left+","+"+(height-padding.bottom)+"
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+(height-padding.bottom)+")")
            .call(xAxis);
    //添加y轴
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+padding.top+")")
            .call(yAxis);
</script>
  九、理解 Update 、Enter 、Exit
    Update 、Enter 、Exit 是D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系
  不确定的情况。
   svg.selectAll("rect")   //选择svg内所有的矩形
    .data(dataset)      //绑定数组
    .enter()            //指定选择集的enter部分
    .append("rect")     //添加足够数量的矩形元素
 
	这段代码使用的情况是当以下情况出现的时候:
有数据,而没有足够图形元素的时候,使用此方法可以添加足够的元素。
   假设,在 body 中有三个 p 元素,有一数组 [3, 6, 9],则可以将数组中的每一
项分别与一个 p 元素绑定在一起。但是,有一个问题:当数组的长度与元素数量不
一致(数组长度 > 元素数量 or 数组长度 < 元素数量)时呢?这时候就需要理解
 Update、Enter、Exit 的概念。
如果数组为 [3, 6, 9, 12, 15],将此数组绑定到三个 p 元素的选择集上。可以想
象,会有两个数据没有元素与之对应,这时候 D3 会建立两个空的元素与数据对应,
这一部分就称为 Enter。而有元素与数据对应的部分称为 Update。如果数组为 [3],
则会有两个元素没有数据绑定,那么没有数据绑定的部分被称为 Exit。示意图如下
所示。
  
此时 SVG 里没有 rect 元素,即元素数量为 0。有一数组 dataset,将数组与元素
数量为 0 的选择集绑定后,选择其 Enter 部分(请仔细看上图),然后添加
(append)元素,也就是添加足够的元素,使得每一个数据都有元素与之对应。
Update 和 Enter 的使用
当对应的元素不足时 ( 绑定数据数量 > 对应元素 ),需要添加元素(append)。
现在 body 中有三个 p 元素,要绑定一个长度大于 3 的数组到 p 的选择集上,然后分别处理 update 和 enter 两部分。
var dataset = [ 3 , 6 , 9 , 12 , 15 ];
//选择body中的p元素
var p = d3.select("body").selectAll("p");
//获取update部分
var update = p.data(dataset);
//获取enter部分
var enter = update.enter();
//update部分的处理:更新属性值
update.text(function(d){
    return "update " + d;
});
//enter部分的处理:添加元素后赋予属性值
enter.append("p")
    .text(function(d){
        return "enter " + d;
    });
结果如下图,update 部分和 enter 部分被绑定的数据很清晰地表示了出来。
update和enter
请大家记住:
update 部分的处理办法一般是:更新属性值
enter 部分的处理办法一般是:添加元素后,赋予属性值
Update 和 Exit 的使用
当对应的元素过多时 ( 绑定数据数量 < 对应元素 ),需要删掉多余的元素。
现在 body 中有三个 p 元素,要绑定一个长度小于 3 的数组到 p 的选择集上,然后分别处理 update 和 exit 两部分。
var dataset = [ 3 ];
//选择body中的p元素
var p = d3.select("body").selectAll("p");
//获取update部分
var update = p.data(dataset);
//获取exit部分
var exit = update.exit();
//update部分的处理:更新属性值
update.text(function(d){
    return "update " + d;
});
//exit部分的处理:修改p元素的属性
exit.text(function(d){
        return "exit";
    });
//exit部分的处理通常是删除元素
// exit.remove();
结果如下,请大家区分好 update 部分和 exit 部分。这里为了表明哪一部分是 exit,并没有删除掉多余的元素,但实际上 exit 部分的绝大部分操作是删除。
update和exit
请大家记住:
exit 部分的处理办法一般是:删除元素(remove)
十、交互式操作
用户用于交互的工具一般有三种:鼠标、键盘、触屏。
var circle = svg.append("circle");
circle.on("click", function(){
    //在这里添加交互内容
});
这段代码在 SVG 中添加了一个圆,然后添加了一个监听器,是通过 on() 添加的。在 D3 中,每一个选择集都有 on() 函数,用于添加事件监听器。
on() 的第一个参数是监听的事件,第二个参数是监听到事件后响应的内容,第二个参数是一个函数。
鼠标常用的事件有:
click:鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起。
mouseover:光标放在某元素上。
mouseout:光标从某元素上移出来时。
mousemove:鼠标被移动的时候。
mousedown:鼠标按钮被按下。
mouseup:鼠标按钮被松开。
dblclick:鼠标双击。
键盘常用的事件有三个:
keydown:当用户按下任意键时触发,按住不放会重复触发此事件。该事件不会区分字母的大小写,例如“A”和“a”被视为一致。
keypress:当用户按下字符键(大小写字母、数字、加号、等号、回车等)时触发,按住不放会重复触发此事件。该事件区分字母的大小写。
keyup:当用户释放键时触发,不区分字母的大小写。 触屏常用的事件有三个:
touchstart:当触摸点被放在触摸屏上时。
touchmove:当触摸点在触摸屏上移动时。
touchend:当触摸点从触摸屏上拿开时。 当某个事件被监听到时,D3 会把当前的事件存到 d3.event 对象,里面保存了当前事件的各种参数
  //添加SVG画布
    var width = 400;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
//画布周边的空白
var padding = {left:30,right:30,top:20,bottom:20};
    //定义数据和比例尺
    var dataset = [10,20,30,40,33,24,12,5];
    //x轴的比例尺
    var xScale = d3.scale.ordinal()
            .domain(d3.range(dataset.length))
            .rangeRoundBands([0,width - padding.left -padding.right]);
    //y轴比例尺
    var yScale = d3.scale.linear()
            .domain([0,d3.max(dataset)])
            .range([height - padding.top - padding.bottom,0]) //因为y轴默认是越往下越大,所以反着写的
    //定义坐标轴
    //定义x轴
    var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient("bottom");
    //定义y轴
    var yAxis = d3.svg.axis()
            .scale(yScale)
            .orient("left");
    //添加矩形和文字元素
    //矩形之间的空白
    var rectPadding = 4;
    //添加矩形元素
    //添加矩形元素
    var rects = svg.selectAll(".MyRect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("class","MyRect")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                return yScale(d);
            })
            .attr("width", xScale.rangeBand() - rectPadding )
            .attr("height", function(d){
                return height - padding.top - padding.bottom - yScale(d);
            })
            .attr("fill",‘#e4393c‘)
            .on("mouseover",function(d,i){
                d3.select(this)
                        .attr("fill","yellow");
            })
            .on("mouseout",function(d,i){
                d3.select(this)
                        .transition()
                        .duration(500)
                        .attr("fill","#e4393c");
            });
    //添加文字元素
    var texts = svg.selectAll(".MyText")
            .data(dataset)
            .enter()
            .append("text")
            .attr("class","MyText")
            .attr("transform","translate(" + padding.left + "," + padding.top + ")")
            .attr("x", function(d,i){
                return xScale(i) + rectPadding/2;
            } )
            .attr("y",function(d){
                var min = yScale.domain()[0];
                return yScale(min);
            })
            .transition()
            .delay(function(d,i){
                return i*200;
            })
            .duration(2000)
            .ease("bounce")
            .attr("y",function(d){return yScale(d)})
            .attr("dx",function(){
                return (xScale.rangeBand() - rectPadding)/2;
            })
            .attr("dy",function(d){
                return 20;
            })
            .attr("stroke","#fff")
            .text(function(d){
                return d;
            });
    //添加坐标轴的元素  +padding.left+","+"+(height-padding.bottom)+"
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+(height-padding.bottom)+")")
            .call(xAxis);
    //添加y轴
    svg.append("g")
            .attr("class","axis")
            .attr("transform","translate("+padding.left+","+padding.top+")")
            .call(yAxis);
这段代码添加了鼠标移入(mouseover),鼠标移出(mouseout)两个事件的监听器。
监听器函数中都使用了 d3.select(this),表示选择当前的元素,this 是当前的
元素,要改变响应事件的元素时这么写就好。
mouseover 监听器函数的内容为:将当前元素变为黄色
mouseout 监听器函数的内容为:缓慢地将元素变为原来的颜色(蓝色)
十一、布局
我们可以据此定义什么时候选择 D3 比较好:
选择 D3:如果希望开发脑海中任意想象到的图表。
选择 Highcharts、Echarts 等:如果希望开发几种固定种类的、十分大众化的图表。
如何理解布局
从上面的图可以看到,布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。
因此,为了便于初学者理解,本站的教程叫布局的作用解释成:数据转换。
布局有哪些
D3 总共提供了 12 个布局:饼状图(Pie)、力导向图(Force)、弦图(Chord)、
树状图(Tree)、集群图(Cluster)、捆图(Bundle)、打包图(Pack)、
直方图(Histogram)、分区图(Partition)、堆栈图(Stack)、
矩阵树图(Treemap)、层级图(Hierarchy)。
1)饼图
利用布局绘制饼图:
  var width = 400;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
    //用D3进行数据可视化
     var dataset = [30,10,43,55,13];
     //定义一个布局
    var pie = d3.layout.pie();
    //返回值赋给变量pie,此时 pie 可以当做函数使用
    var piedata = pie(dataset);
    //将数组dataset作为pie()的参数,返回值给piedata,这样 piedata 就是转换后的数据
    // console.log(piedata);   //转换后为一个包含5个对象的数组
    //布局不是要绘图而是为了得到绘图所需的数据
   //为了根据转换后的数据 piedata 来作图,还需要一样工具:生成器。
   //SVG 有一个元素,叫做路径 path,是 SVG 中功能最强的元素,
    // 它可以表示其它任意的图形。顾名思义,路径元素就是通过定义
    // 一个段“路径”,来绘制出各种图形。但是,路径是很难计算的,
    // 通过布局转换后的数据 piedat
    // a 仍然很难手动计算得到路径值。为我们完成这项任务的,就是生成器。
    //这里要用到的叫做弧生成器,能够生成弧的路径,因为饼图的每一部分都是一段弧。
    var outerRadius = 150;
    var innerRadius = 0;
    var arc = d3.svg.arc()
            .innerRadius(innerRadius)
            .outerRadius(outerRadius);
//弧生成器返回的结果赋值给 arc。此时 arc可以当做一个函数使用,把piedata
    //作为参数传入,即可得到路径值
    //接下来,可以在 SVG 中添加图形元素了。先在 svg 里添加足够数量(5个)个分组元素(g),
    // 每一个分组用于存放一段弧的相关元素。
    var arcs = svg.selectAll("g")
            .data(piedata)
            .enter()
            .append("g")
            .attr("transform","translate("+(width/2)+","+(width/2)+")");
//接下来对每个g元素,添加path
    //color是一个颜色比例尺,它能根据传入的索引号获取相应的颜色值
    var color = d3.scale.category10();
    var selectColor = "";
    arcs.append("path")
            .attr("fill",function(d,i){
                return color(i);
            })
            .attr("d",function(d){
                return arc(d);  //调用弧生成器,得到路径值
            });
//在每一个弧线中心添加文本
    arcs.append("text")
            .attr("transform",function(d){
                return "translate("+arc.centroid(d)+")";
            })
            .attr("text-anchor","middle")
            .text(function(d){
                return d.data;
            });
    //arc.centroid(d) 能算出弧线的中心。要注意,text() 里返回的是 d.data ,
    // 而不是 d 。因为被绑定的数据是对象,里面有 d.startAngle、d.endAngle、
    // d.data 等,其中 d.data 才是转换前的整数的值。
2)力导向图
  
力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置
节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。
节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。
根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,
最终达到一种能量很低的安定状态。
力导向图能表示节点之间的多对多的关系
布局(数据转换) d3.layout.force()。
    var width = 800;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
    //用D3进行数据可视化
    var nodes = [{name:"桂林"},{name:"广州"},{name:"厦门"},{name:"杭州"},{name:"上海"},{name:"青岛"},{name:"天津"}];
    var edges = [{source:0,target:1},{source:0,target:2},{source:0,target:3},
        {source:1,target:4},{source:1,target:5},{source:1,target:6},];
    //定义一个力导向图的布局如下
      var force = d3.layout.force()
              .nodes(nodes) //指定节点数组
              .links(edges)  //指定连线数组
              .size([width,height])  //指定作用域范围
              .linkDistance(150)   //指定连线长度
              .charge([-400]);  //相互之间的作用力
    //使力学作用生效
     force.start();  //开始作用
    console.log(nodes);
    console.log(edges);
//转换后,节点对象里多了一些变量。其意义如下:
   // index:节点的索引号
    //px, py:节点上一个时刻的坐标
    //x, y:节点的当前坐标
    //weight:节点的权重
     //有了转换后的数据就可以作图了。分别绘制三种图形元素
     //line 线段 ,表示连线
     //circle 圆,表示节点
    //text 文字,描述节点
    //添加连线
 var svg_edges = svg.selectAll("line")
                .data(edges)
         .enter()
         .append("line")
         .style("stroke","#ccc")
         .style("stroke-width",1);
    var color = d3.scale.category20();
//添加节点
    var svg_nodes = svg.selectAll("circle")
            .data(nodes)
            .enter()
            .append("circle")
            .attr("r",20)
            .style("fill",function(d,i){
                return color(i);
            })
            .call(force.drag);  //使节点能够拖动
  //添加描述节点的文字
    var svg_texts = svg.selectAll("text")
            .data(nodes)
            .enter()
            .append("text")
            .style("fill","black")
            .attr("dx",20)
            .attr("dy",8)
            .text(function(d){
                return d.name;
            });
  
   // 调用 call( force.drag ) 后节点可被拖动。force.drag() 是一个函数,将其作为 call() 的参数,相当于将当前选择的元素传到 force.drag() 函数中。
    //最后,还有一段最重要的代码。由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。
  //力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好。
    force.on("tick", function(){ //对于每一个时间间隔
        //更新连线坐标
        svg_edges.attr("x1",function(d){ return d.source.x; })
                .attr("y1",function(d){ return d.source.y; })
                .attr("x2",function(d){ return d.target.x; })
                .attr("y2",function(d){ return d.target.y; });
        //更新节点坐标
        svg_nodes.attr("cx",function(d){ return d.x; })
                .attr("cy",function(d){ return d.y; });
        //更新文字坐标
        svg_texts.attr("x", function(d){ return d.x; })
                .attr("y", function(d){ return d.y; });
    });
   var width = 800;
    var height = 400;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height);
    //用D3进行数据可视化
    var nodes = [{name:"桂林"},{name:"广州"},{name:"厦门"},{name:"杭州"},{name:"上海"},{name:"青岛"},{name:"天津"}];
    var edges = [{source:0,target:1},{source:0,target:2},{source:0,target:3},
        {source:1,target:4},{source:1,target:5},{source:1,target:6},];
    //定义一个力导向图的布局如下
      var force = d3.layout.force()
              .nodes(nodes) //指定节点数组
              .links(edges)  //指定连线数组
              .size([width,height])  //指定作用域范围
              .linkDistance(150)   //指定连线长度
              .charge([-400]);  //相互之间的作用力
    //使力学作用生效
     force.start();  //开始作用
    console.log(nodes);
    console.log(edges);
//转换后,节点对象里多了一些变量。其意义如下:
   // index:节点的索引号
    //px, py:节点上一个时刻的坐标
    //x, y:节点的当前坐标
    //weight:节点的权重
     //有了转换后的数据就可以作图了。分别绘制三种图形元素
     //line 线段 ,表示连线
     //circle 圆,表示节点
    //text 文字,描述节点
    //添加连线
 var svg_edges = svg.selectAll("line")
                .data(edges)
         .enter()
         .append("line")
         .style("stroke","#ccc")
         .style("stroke-width",1);
    var color = d3.scale.category20();
//添加节点
    var svg_nodes = svg.selectAll("circle")
            .data(nodes)
            .enter()
            .append("circle")
            .attr("r",20)
            .style("fill",function(d,i){
                return color(i);
            })
            .call(force.drag);  //使节点能够拖动
  //添加描述节点的文字
    var svg_texts = svg.selectAll("text")
            .data(nodes)
            .enter()
            .append("text")
            .style("fill","black")
            .attr("dx",20)
            .attr("dy",8)
            .text(function(d){
                return d.name;
            });
// 调用 call( force.drag ) 后节点可被拖动。force.drag() 是一个函数,将其作为 call() 的参数,相当于将当前选择的元素传到 force.drag() 函数中。
    //最后,还有一段最重要的代码。由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。
  //力导向图布局 force 有一个事件 tick,每进行到一个时刻,都要调用它,更新的内容就写在它的监听器里就好。
    force.on("tick", function(){ //对于每一个时间间隔
        //更新连线坐标
        svg_edges.attr("x1",function(d){ return d.source.x; })
                .attr("y1",function(d){ return d.source.y; })
                .attr("x2",function(d){ return d.target.x; })
                .attr("y2",function(d){ return d.target.y; });
        //更新节点坐标
        svg_nodes.attr("cx",function(d){ return d.x; })
                .attr("cy",function(d){ return d.y; });
        //更新文字坐标
        svg_texts.attr("x", function(d){ return d.x; })
                .attr("y", function(d){ return d.y; });
    });
  
   3)树状图
树状图,可表示节点之间的包含与被包含关系
数据
初始数据先写在一个JSON文件中,再用D3来读取
JSON 是一种轻量级的数据交换格式。
  {
"name":"中国",
"children":
[
    { 
      "name":"浙江" , 
      "children":
      [
            {"name":"杭州" },
            {"name":"宁波" },
            {"name":"温州" },
            {"name":"绍兴" }
      ] 
    },
    { 
        "name":"广西" , 
        "children":
        [
            {
            "name":"桂林",
            "children":
            [
                {"name":"秀峰区"},
                {"name":"叠彩区"},
                {"name":"象山区"},
                {"name":"七星区"}
            ]
            },
            {"name":"南宁"},
            {"name":"柳州"},
            {"name":"防城港"}
        ] 
    },
    { 
        "name":"黑龙江",
        "children":
        [
            {"name":"哈尔滨"},
            {"name":"齐齐哈尔"},
            {"name":"牡丹江"},
            {"name":"大庆"}
        ] 
    },
    { 
        "name":"新疆" , 
        "children":
        [
            {"name":"乌鲁木齐"},
            {"name":"克拉玛依"},
            {"name":"吐鲁番"},
            {"name":"哈密"}
        ]
    }
]
}
这段数据表示:“中国”“省份名”“城市名”的包含与被包含关系
布局(数据转换)
 <style>
        .node circle{
            fill:#fff;
            stroke:steelblue;
            stroke-width: 1.5px;
        }
        .node{
            font: 12px sans-serif;
        }
        .link{
            fill:none;
            stroke:#ccc;
            stroke-width: 1.5px;
        }
    </style>
 var width = 800;
    var height = 800;
    var svg = d3.select("body")
            .append("svg")
            .attr("width",width)
            .attr("height",height)
            .style("padding-top","50px");
    var tree = d3.layout.tree()
            .size([width,height-500])
            .separation(function(a,b){return (a.parent == b.parent ?1:2);})
    //布局保存在变量tree中
    //size():设定尺寸,即转换后的各节点的坐标在哪一个范围内
    //separation():设定节点之间的间隔
    //转换数据
    d3.json("data/data.json",function(error,root){
        //root是读入的数据
        var nodes = tree.nodes(root);
        var links = tree.links(nodes);
       // console.log(nodes);
        //console.log(links);
        //画点
        var node = svg.selectAll(".node")
                .data(nodes)
                .enter()
                .append("g")
                .attr("class","node")
                .attr("transform",function(d){return "translate("+ d.x+","+ d.y+")"})
        //加圈圈
        node.append("circle")
                .attr("r",4.5)
        //加文字
        var text = node.append("text")
                .attr("dy",[15,15,15,15])
                .attr("dx",[-11,-11,-11,-11])
                .style("text-anchor", function (d) {return d.children?"end":"start"})
                .text(function(d){
                    return d.name
                })
        var diagonal = d3.svg.diagonal()
                .projection(function(d){return [d.x, d.y];})
        //画线
        var line = svg.selectAll("link")
                .data(links)
                .enter()
                .append("path")
                .attr("class","link")
                .attr("d",diagonal)
    });
//    //d3.json() 是用来向服务器请求JSON文件的
//    //d3.json()函数后面跟一个无名函数function(error),参数root是读取的数据
//
//    //后两行代码调用tree转换数据,保存到变量nodes和links中。然后输出转换后的数据
//    //nodes 中有各个节点的子节点(children)、深度(depth)、名称(name)、位置(x,y)信息,其中名称(name)是 json 文件中就有的属性。
//
//    //links 中有连线两端( source , target )的节点信息。
//   //绘制
//    //D3已经基本上我们准备好了绘制的函数,d3.svg.diagonal()。这是一个对角线
//    //生成器,只需要输入两个顶点坐标,即可生成一条贝塞尔曲线
//
//    //创建一个对角线生成器:
//    var diagonal = d3.svg.diagonal()
//            .projection(function(d){return [d.y, d.x];})
//    //  projection()是一个点变换器,默认是[d.x , d.y],即保持原坐标不变,
//  //如果写成[d.y ,d.x] 即是说对任意输入的顶点,都交换 x 和y坐标
//    //绘制连线时,方法如下:
//     var link = svg.selectAll(".link")
//            .data(links)
//            .enter()
//             .append("path")
//             .attr("class","link")
//             .attr("d",diagonal);  //使用对角线生成器
 
原文:http://www.cnblogs.com/WEI-web/p/7209246.html