用D3.js画树状图

做项目遇到一个需求,将具有层级关系的词语用树状图的形式展示它们之间的关系,像这样:

或者是这样:

上面的图片只是样例,跟我下面的代码里面用的数据不同

网上有很多这种数据可视化展示的js控件,我这里选择了D3.js。

首先在html页面需要包含D3的js文件,其次我们需要将数据构造成json格式,然后存入到一个d3.json文件

{
"name":"如何学习D3",
"children":
[
    { 
      "name":"预备知识" , 
        "children":
        [
                {"name":"HTML & CSS" },
                {"name":"JavaScript" },
                {"name":"DOM" },
                {"name":"SVG" }
        ] 
      },
      
    { 
        "name":"安装" , 
        "children":
        [
            {
                "name":"记事本软件",
                "children":
                [
                    {"name":"Notepad++"},
                    {"name":"EditPlus"},
                    {"name":"Sublime Text"}
                ]
            },
            {
                "name":"服务器软件",
                "children":
                [
                    {"name":"Apache Http Server"},
                    {"name":"Tomcat"}
                ]
            },
            {"name":"下载D3.js"}
        ] 
    },
    
    { 
        "name":"入门",
        "children":
        [
            {
                "name":"选择集",
                "children":
                [
                    {"name":"select"},
                    {"name":"selectAll"}
                ]
            },
            {
                "name":"绑定数据",
                "children":
                [
                    {"name":"datum"},
                    {"name":"data"}
                ]
            },
            {"name":"添加删除元素"},
            {
                "name":"简单图形",
                "children":
                [
                    {"name":"柱形图"},
                    {"name":"折线图"},
                    {"name":"散点图"}
                ]
            },
            {"name":"比例尺"},
            {"name":"生成器"},
            {"name":"过渡"}
        ] 
    },
    
    { 
        "name":"进阶" , 
        "children":
        [
            {
                "name":"布局的应用",
                "children":
                [
                    {"name":"饼状图"},
                    {"name":"树状图"},
                    {"name":"矩阵树图"}
                ]
            },
            {"name":"地图"}
        ]
    }
]
}

然后开始编写JavaScript代码:

横向树状图:

var width = 700,
    height = 700;

var cluster = d3.layout.cluster()
    .size([width, height - 200]);

var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(40,0)");
    


d3.json("d3.json", function(error, root) {
    
  var nodes = cluster.nodes(root);
  var links = cluster.links(nodes);
  
  console.log(nodes);
  console.log(links);

  var link = svg.selectAll(".link")
      .data(links)
      .enter()
      .append("path")
      .attr("class", "link")
      .attr("d", diagonal);

  var node = svg.selectAll(".node")
      .data(nodes)
      .enter()
      .append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 4.5);

  node.append("text")
      .attr("dx", function(d) { return d.children ? -8 : 8; })
      .attr("dy", 3)
      .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
      .text(function(d) { return d.name; });
});

圆形树状图(这个需要用到投影,稍微麻烦一点,不详细解释了):

//图像区域大小
    var R = 600;

    //定义一个Tree对象,定义旋转角度和最大半径
    var tree = d3.layout.tree()
        .size([360,R/2-120])
        .separation(function(a,b) { return a.parent == b.parent ? 1 : 2;});

    //定义布局方向
    var diagonal = d3.svg.diagonal()  
        .projection(function(d) { 
            var r = d.y, a = (d.x-90) / 180 * Math.PI;
              return [r * Math.cos(a), r * Math.sin(a)];
     }); 

    //新建画布,移动到圆心位置
    var svg = d3.select("#showTree").append("svg")
        .attr("width", R)
        .attr("height", R)
        .append("g")
        .attr("transform", function(d){ return "translate("+R/2+"," + R/2 + ")";});

        //根据JSON数据生成树
        d3.json("d3.json", function(error, data) {
      
      //根据数据生成nodes集合
      var nodes = tree.nodes(data);
      
      //获取node集合的关系集合
      var links = tree.links(nodes);
      
      //为关系集合设置贝塞尔曲线连接
      var link=svg.selectAll(".link")
          .data(links)
          .enter()
          .append("path")
          .attr("class", "link")
          .attr("d",diagonal);
      
      //根据node集合生成节点
      var node = svg.selectAll(".node")
          .data(nodes)
          .enter()
          .append("g")
          .attr("class", "node")
          .attr("transform",function(d){return "rotate(" + (d.x-90) + ")translate(" + d.y + ")"; });
      
      //为节点添加圆形标记,如果有子节点为红色,否则绿色
      node.append("circle")      
          .attr("fill",function(d){return d.children==null?"#0F0":"#F00";})
          .attr("r", 5);
      
      //为节点添加说明文字
      node.append("text")
        .attr("dy", ".4em")
        .text(function(d){return d.name;})
        .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
        .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; });
    });    

D3的d3.json(url,callback)方法可以读取json文件,然后构造树状图。

如果不想把数据写入json文件,直接在后台构造好json数据结构然后传到前台要怎么弄?

其实d3.json(url,callback)方法的url是发起一个get请求,返回一个json字符串,然后进入callback进行处理,本质上跟ajax差不多

所以当我们不想读取文件时,修改一下d3.json(url,callback)的url参数,发起一个get请求到控制器,然后你在控制器里面构造相应的json数据结构,然后返回过来就可以了

但是这里的url不能直接带中文参数,比如:

url = "json.html?word=关键词";

这样的请求发到后台,后台接收的是这样的一段乱码字符串:

关键è¯

那有人说先将这个中文做一次编码,然后在传到后台不就行了?

确实,我们一般在js里面如果要传中文参数到后台都会先进行编码然后再传的,所以我当时也是这么想的,心想这下应该不会出问题了吧,然并卵,后台接收然后解码得到的依然是那串乱码,然后没办法,我只能进d3.js的文件去查看源码,奈何才疏学浅,压根看不懂,也没找到它哪里对这个url做了什么编码或解码操作。后来想到以前见到过在前台进行多次编码然后再传到后台的,我也尝试了一下在js里面进行了两次编码,然后再传到后台,到后台接收然后解码一次,发现能够得到正确的中文参数

 第一行是后台接收的参数,第二行是对参数解码一次得到的结果。

问题是解决了,但是我还是不知道这是为什么,只注意到d3.json这个方法里面发送的是get请求,后来查资料还有问其他人才知道在浏览器地址栏里,浏览器认为%是个转义字符,浏览器会把%与%之间的编码,两位两位取出后进行解码,然后再传递给处理页面,然后由处理页面进行再次解码,而get请求的中文参数就是显示在浏览器地址栏,所以在js里面参数只进行一次编码的话,参数经过浏览器的解码,传到后台的就是没编码的中文,这个当然就会变成乱码,所以在js里面对中文做两次编码然后通过get请求传到后台,后台只需做一次解码就能得到正确的中文参数

posted @ 2016-08-05 18:22  风归云隐  阅读(10251)  评论(1编辑  收藏  举报