hjzheng / CUF_meeting_knowledge_share

Record CUF team meeting knowledge share
121 stars 49 forks source link

2015-1-13 D3(Data-Driven Documents) #23

Open hjzheng opened 9 years ago

hjzheng commented 9 years ago

D3(Data-Driven Documents)

什么是D3?

这里的D3,首先不是尼康相机也不是暗黑破坏神3更不是维生素。

摘自数据可视化实战:使用D3设计交互式图表

D3(有时候也叫D3或d3.js)是一个JavaScript库,用于创建数据可视化图形。 事实上,D3是一个缩写,它的全称叫Data-Driven Documents(数据驱动的文档)。 数据来源于你,而文档就是基于Web的文档(或者网页),代表可以在浏览器中展现的一切, 比如HTML、SVG。D3扮演的是一个驱动程序的角色,因为它联系着数据和文档。

D3优点

  • 资料丰富,案例非常多。
  • SVG矢量图形的特点是无损缩放,这个优势在显示2D图形式会有非常好的效果,并且兼容各种分辨率。
  • SVG图形的节点可以像dom元素一样控制,这就让自主创作图形变得更容易。相对于canvas这也是非常大的优势。

    D3缺点

  • SVG是2D矢量图,不能画3D图形。(用2D矢量可以画很多带透视效果的伪3D图,那并不是真正的3D图!) d3.
  • 不支持IE6,7,8。如果想要IE8使用d3,请用r2d3.js(一个结合了 Raphael.js的扩展库)。Raphael.js是一个跨浏览器的矢量图形库,它实现IE6,7,8兼容的方法是:在IE6,7,8中使用VML,在其他浏览器中使用SVG。另外,如果图形复杂,就不要指望用Raphael.js在IE上能跟D3画出一样酷炫的效果。
  • SVG的节点都是对象,非常占用内存。例如论坛里一个朋友使用d3绘制超过12000个节点的图,直接导致每个试图打开它的浏览器都崩溃了。这个时候如果不愿意做简化那么应该试试canvas绘图。

    一个例子, 基于AngularJS (http://projects.delimited.io/experiments/last-fm/)

angular.module('viz').directive('toptagChart', ['lastfm', 

  function (lastfm) {

    var link = function ($scope, $el, $attrs) {
      //定义大小
      var diameter = 500;

      //使用pack布局,关于pack你可以查看D3 API
      var bubble = d3.layout.pack()
        .sort(null)
        .size([diameter, diameter])
        .padding(2.5);

      //创建svg元素,并定义大小 
      var svg = d3.select($el[0]).append("svg")
        .attr({width: diameter, height: diameter})
        .attr("viewBox", "0 0 " + diameter + " " + diameter);

      //追加g元素 
      var chart = svg.append("g");

      //追加text元素,显示loading
      chart.append("text").attr("id", "loading")
        .text("Loading...")
        .attr("transform", "translate(200,250)");

      var update = function () {
        var data = $scope.toptags.map(function (d) {
          d.value = d[$scope.tagsize];
          return d;
        });

        //将数据塞入pack布局中,pack布局会
        //自动帮我们计算坐标和bubble的半径大小根据value
        bubble.nodes({children: data});

        //data如果有数据,删除text元素
        if (data.length) chart.select("#loading").remove();

        //选取所有class为node元素
        //很多人很奇怪,为啥元素没有创建,却要去选取
        //这就是D3的特点,通过下面的enter()方法先占位
        //enter(), 这个方法会分析当前选择的DOM 元素和传给它的数据,
        //如果数据值比对应的DOM 元素多,就创建一个新的占位元素。
        //然后把这个新占位元素的引用交给链中的下一个方法
        var selection = chart.selectAll(".node")
          .data(data);

        //enter方法占位,追加g元素,并添加node class
        var enter = selection.enter()
          .append("g").attr("class", "node")
          .attr("transform", function (d) { 
            return "translate(" + d.x + "," + d.y + ")"; 
          });

        //在g元素里面添加圆,利用pack布局计算好的圆心坐标和半径
        enter.append("circle")
          .attr("r", function (d) { return d.r; })
          .style("fill", '#FFCB72')
          .on("click", function (d) {
            svg.selectAll("circle").style("fill", '#FFCB72');
            d3.select(this).style("fill", "#19314A");

            lastfm.topArtists(d.name)
              .success(function (res) {
                if (res.error) {
                  throw new Error(res.message);
                } else {
                 $scope.currtag = d.name;
                  var artists = res.topartists.artist.map(function (a) {
                    a.genre = d.name;
                    a.arank = +a['@attr'].rank;
                    return a;
                  });
                  $scope.artists = artists;
                }
              });
          });

        //在g元素里追加文本
        enter.append("text")
          .attr("dy", ".3em")
          .style("text-anchor", "middle")
          .text(function (d) { return d.name; });

        //当圆心坐标改变时,增加translation效果
        selection.transition().duration(2000)
          .attr("transform", function (d) { 
            return "translate(" + d.x + "," + d.y + ")";
          });

        //当圆的半径改变时,增加translation效果
        selection.selectAll("circle").transition().duration(3000)
          .attr("r", function (d) { return d.r; });

        resize();
      };

      function resize() {
        svg.attr("width", $el[0].clientWidth);
        svg.attr("height", $el[0].clientWidth); //It's a square
      }

      $scope.$on('windowResize',resize);
      //tagsize如果有变化,调用update方法
      $scope.$watch('tagsize', update);
      $scope.$watch('toptags', update);

    };
    return {
      template: '<div class="chart col-sm-12 col-md-12 col-lg-12 col-xl-12"></div>',
      replace: true,
      link: link, 
      restrict: 'E' 
    };
}]);

另一个例子,是我自己写的(http://get-set.cn/front-end-chart/)

代码: https://github.com/hjzheng/front-end-collect

参考资料

我涉及的数据可视化的实现技术和工具 数据可视化实战:使用D3设计交互式图表 Creating Custom D3 Directives in AngularJS

hjzheng commented 9 years ago

2015年 第一篇,祝福CUF Team越来越强!