这篇是D3的学习总结。由于Freecodecamp的D3新版教程还没有上线,D3的基础学习资料是自己从网上搜集来跟着学的,然后完成了Freecodecamp上的5个小项目。

基础学习

  1. 这里是D3官方给出的一些教程,我看了Introductions & Core Concepts部分的前10篇,全部是D3的作者Mike Bostock大神写,其中Thinking with Joins以及How Selections Work这两篇看过之后豁然开朗,大概明白了D3为何要写成这样,比如先引用不存在的节点再创造节点。D3做出来的东西真的很惊艳,是我最喜欢的一个库(虽然我也没接触几个…)。basic barletter frequenciesgeneral update 1general update 2general update 3circle这6个练习是跟着Mike Bostock大神的教程自己敲进去的代码实现。上述给出的教程链接感觉很重要,日后遗忘可以进行参考。

  2. 使用codepen写练习有时需要外部获取数据文件,比如图片、json文件、tsv文件。对于图片,使用google drive和dropbox均可以达到目的,google drive使用gdurl这个均可以生成永久链接(gdurl有时会生成http形式的链接,在codepen中无法使用)。而json文件、tsv文件存储在google drive中无法使用ajax技术获取,目前的办法是存储在dropbox,生成分享链接,稍作修改,作为最后的url。
    正确的url: https://dl.dropboxusercontent.com/s/ + hfgg7cdoedkv1he/ky-counties.json(dropbox生成的链接中包含这部分,相应替换) + ?dl=1

  3. 这里插一段题外话,这三个月的自学让自己发现,那种从非常基础的底层理论入手,全面学习后再进行各项练习的学习方法或许真的不适合自己,当然这也是对特定的学科,特定阶段而言。比如学习语法,就需要全面了解一下常用基础语法,才能顺利进行下一步。但是更重要的进一步的提升需要的是大量的练习,从目的出发,反向学习需要的东西,更有效率。目前发现对于语言(机器语言、自然语言)的学习,这种方法对自己特别有效。但对于写程序而言,这么说似乎有些耍小聪明,毕竟计算机科学是一套完整的理论的体系,还有大量的基础等着我去学习,任重道远。

下面是freecodecamp中的5个项目练习,主要记录自己遇到的一些问题,解决办法,没有解决的问题以及一些感想。

Visualize Data with a Bar Chart

这个项目是利用D3做了一张柱状图,涉及的是D3的基本的标准使用方法。

  • 引用的库: d3标准库和d3-tip库
  • 过程: 1. 准备阶段:获取json文件的url、数据转换方式、图纸大小、坐标轴的比例和转换;2.画图阶段:利用d3.json获取数据,完善坐标轴标尺,绘制图形、坐标轴、标题等。
  • 遇到的问题:
    1.时间数据的转换
    var parseTime = d3.timeParse("%Y-%m-%d");
    var timeFormat = d3.timeFormat(%Y-%m);
    利用上面两行代码将以string格式表示的时间数据和js中的时间类型相互转换,更多的转换类型点击这里。可以在使用数据前统一对数据进行预处理,如下。

    var data = json.data;
    data.forEach(function(d){
    d[0] = parseTime(d[0]);
    d[1] = +d[1];
    });
    

    2.比例尺
    这个项目使用了三种比例尺,时间、线性、序数比例尺,对应d3.scaleTime(用来设置横坐标轴)、d3.scaleLinear(用来设置纵坐标轴以及图形的y值定位)以及d3.scaleBand(用来设置图形的宽度以及图形x值定位)。同时利用d3.scaleBandd3.scaleTime来进行图形x方向定位和宽度设置时,图纸宽度的选择是关键!图纸有效宽度/数据数量 = 一个整数,否则画出的bar之间会有间距。其实,只是用d3.scaleBand可以实现目的,在后面的项目中有用法。
    3.绑定问题
    将g绑定到svg后,再绑定数据时,不用使用tag名称,会出现数据绑定不全的问题,此时应使用class名称来绑定。具体原因没有找到说法。

Visualize Data with a Scatterplot Graph

这个项目的图形是用circle表示的,和bar大同小异。

  • 遇到的问题
    1.在固定的位置用来显示tip:先创建一个固定位置,然后在具体操作代码处稍作调整。
    创建固定位置:
    chart.call(tip);
    var show = chart.append("g")
    .attr("id", "show")
    .attr("cx", "5")
    .attr("cy", "5");
    
    调整代码:
    .on("mouseover", function(d){tip.show(d,document.getElementById("show"))})

Visualize Data with a Heat Map

这个项目用来表示地表温度,画出来有些震撼,基本没有遇到难点。

Show National Contiguity with a Force Directed Graph

这张图使用了d3的force layout。说实话,对force layout的学习并不深入,很多东西还不太明白,此处只记录了force的基本实现过程,其他同上。

  • 引用的库:d3标准库
  • 参考文档:官方文档实例1实例2sprite
  • 注意:
    1.div使用left、right属性来定位
    2.为了在svg中使用css-sprite, link和node要放在同一个div内
    html:

    <div id="root" width="900px" height="600px">
      <h2>Force Directed Graph of State Contiguity</h2>
      <div id="node" ></div>
    </div>
    

    js:

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke-width", 1);
    
    var node = d3.select("#node")
    .selectAll(".flag")
    .data(graph.nodes)
    .enter().append("div")
    .attr("class", function(d){
      return "flag flag-" + d.code;
    })
    

    2.这个练习没有使用d3-tip库来实现tooltip,方法如下

    var tip = d3.select("#root")
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);  
    ...
    .on("mouseover", function(d) {
      tip.transition()
        .duration(200)
        .style("opacity", .9);
      tip.html(d.country)
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY) + "px");
    })
    .on("mouseout", function(d) {
      tip.transition()
        .style("opacity", 0);
    

    3.css的设置,涉及绝对位置显示和居中的问题

    svg{
      border: 1px black solid;
      display: block;
      margin: auto;
    }
    #node{
      position: absolute;
      margin:auto;
      width: 900px;
      left: 0;
      right: 0;
    }
    div.tooltip {
      position: absolute;
      text-align: center;
      padding: 2px;
      font: 12px sans-serif;
      color: white;
      background: steelblue;
      border: 0px;
      border-radius: 8px;
      pointer-events: none;
    }
    .flag {
        position: absolute;
        width: 16px;
        height: 11px;
        background: url('http://gdurl.com/R0x1') no-repeat;
    }
    
  • 过程:
    1.创建一个force模拟对象

    var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d){return d.index;}))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("y", d3.forceY(0))
    .force("x", d3.forceX(0));
    

    2.数据绑定

    simulation
      .nodes(graph.nodes)
      .on("tick", ticked);
    
    simulation.force("link")
      .links(graph.links);
    

    3.正常创建link和node

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke-width", 1);
    
    var node = d3.select("#node")
    .selectAll(".flag")
    .data(graph.nodes)
    .enter().append("div")
    .attr("class", function(d){
      return "flag flag-" + d.code;
    })
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended))
    .on("mouseover", function(d) {
      tip.transition()
        .duration(200)
        .style("opacity", .9);
      tip.html(d.country)
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY) + "px");
    })
    .on("mouseout", function(d) {
      tip.transition()
        .style("opacity", 0);
    

    4.对node和link的操作

      function ticked(){
      link
        .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;});
      node
        .style("left", function(d){return d.x + "px";})
        .style("top", function(d){return d.y + "px";});
    }
    

    Map data across the globe

    使用D3来绘制地图,做完这个项目,我意识到D3绘图是一层一层画的,不同的内容位于不同的图层,后画的内容显示在上层。开始做项目之前,先跟着下面的两个教程预热了一下,画了波士顿老鼠分布地图美国失业情况地图

  • 教程:地图galleryDUSPviz
  • 涉及的库:D3标准库,topojson库,D3 queue库
  • 重要概念:1.projection: 自己理解为某种模板,用来处理地理坐标和图纸坐标的关系;2.topojson:用来处理数据的一种方法,主要用了其中的topojson.feature()(用来转换数据)和topojson.mesh()(转换同时筛选数据);3.queue:用于下载完所需的全部数据再执行后续的操作。
  • 过程: 1.基本设置;2.画地图;3.添加陨石数据;4;添加zoom
  • 遇到的问题:
    1.大圆遮挡了小圆
    使用sort方法对数据进行了从大到小的排序,利用后画的图形在上层的特点。

    data.sort(function(a, b){
      if(a.properties.mass > b.properties.mass)
        return -1;
      else if(a.properties.mass < b.properties.mass)
        return 1;
      return 0;
    });  
    

    2.append数据时,遇到null的情况处理办法

    .attr("cx", function(m){return m.geometry == null ? projection([0, 0])[0] : projection(m.geometry.coordinates)[0];})
    .attr("cy", function(m){return m.geometry == null ? projection([0, 0])[0] : projection(m.geometry.coordinates)[1];})
    .attr("fill", function(m, i){return color(i);})
    .attr("r", function(m){return m.geometry == null ? 0 : size(Math.sqrt(m.properties.mass));})  
    

    3.使用zoom

    var zoom = d3.zoom()
    .scaleExtent([1, 8])
    .on("zoom", zoomed);
    
    function zoomed(){
      var transform = d3.zoomTransform(this);
      g.attr("transform", transform);
      d3.selectAll("circle").attr("transform", transform);
    }
    
    svg.call(zoom);  
    

    4.svg的居中

    svg
      border: 1px solid black
      display: block
      margin: auto