使用动画在D3 Visualization中添加和删除元素

时间:2014-03-21 18:52:06

标签: javascript animation d3.js chord-diagram

很长时间用户第一次在这里提问(第一次我无法使用我搜索到的答案来解决问题)。我用自己的数据在JS D3中重建了超级和弦图表。我的实现可以找到here,虽然该地址可能永远不会保持良好,所以这里是代码(原谅一些错位):

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script type="text/javascript" src="d3/d3.js"></script>
    <script type="text/javascript" src="d3/d3.layout.js"></script>
    <link type="text/css" rel="stylesheet" href="style.css"/>
  </head>
  <body>
    <div id="body">
      <div id="footer">
        Purdue OIR Testing - Migration
        <div class="hint">mouseover groups to highlight</div>
      </div>
    </div>
    <div id="tooltip"></div>
    <script type="text/javascript">


//import the data and call the draw chords function 
d3.text("migrationdata.csv", function(data) {
    var matrix = d3.csv.parseRows(data).map(function(row) {
        return row.map(function(value) {
            return +value;
        });
    });
    d3.text("headersColors.csv", function(headerdata) {
    var headersColors = d3.csv.parseRows(headerdata);
    var headers = headersColors[1];
    var colors = headersColors[2];

    drawChords(matrix, headers, colors);
 });
});

//create the chord viz
function drawChords (matrix, headers, colors){

    var w = 980,
        h = 800,
        r1 = h / 2,
        r0 = r1 - 110,
        fadeOutA = 0,
        fadeInA = 0.8;

    var fill = d3.scale.category20c();

    var chord = d3.layout.chord()
        .padding(.02)
        .sortSubgroups(d3.descending)
        .sortChords(d3.descending);

    var arc = d3.svg.arc()
        .innerRadius(r0)
        .outerRadius(r0 + 20);

    var svg = d3.select("body").append("svg:svg")
        .attr("width", w)
        .attr("height", h)
      .append("svg:g")
        .attr("id", "circle")
        .attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");

    svg.append("circle")
        .attr("r", r0 + 20);
        //.attr("fill-opacity",0);

    //assign the matrix
      chord.matrix(matrix);

    //create the groups
      var g = svg.selectAll("g.group")
          .data(chord.groups)
        .enter().append("svg:g")
          .attr("class", "group")
          .on("mouseover", mouseover)
          .on("mouseout", function (d) { d3.select("#tooltip").style("visibility", "hidden") });

      g.append("svg:path")
          .style("stroke", function(d) { return colors[d.index]; })
          .style("fill", function(d) { return colors[d.index]; })
          .attr("d", arc);

      g.append("svg:text")
          .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
          .attr("dy", ".35em")
          .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
          .attr("transform", function(d) {
            return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                + "translate(" + (r0 + 26) + ")"
                + (d.angle > Math.PI ? "rotate(180)" : "");
          })
          .text(function(d) { return headers[d.index]; });

      var chordPaths = svg.selectAll("path.chord")
          .data(chord.chords)
        .enter().append("svg:path")
          .attr("class", "chord")
          .style("stroke", function(d) { return d3.rgb(colors[d.source.index]).darker(); })
          .style("fill", function(d) { return colors[d.source.index]; })
          .attr("d", d3.svg.chord().radius(r0))
          .on("mouseover", function (d) {
              d3.select("#tooltip")
                .style("visibility","visible")
                .html(chordTip(d))
                .style("left", (d3.event.pageX - 100) + "px")     
                .style("top", (d3.event.pageY - 100) + "px");  
               })
           .on("mouseout", function (d) { d3.select("#tooltip").style("visibility", "hidden") });

      function chordTip (d) {
        var p = d3.format(".1%"), q = d3.format(",.2r")
        return "Migration Info:<br/>"
          +  headers[d.source.index] + " → " + headers[d.target.index]
          + ": " + Math.round(d.source.value) + "<br/>"
          + headers[d.target.index] + " → " + headers[d.source.index]
          + ": " + Math.round(d.target.value) + "<br/>";
      }

      function groupTip (d) {
        return "College Info:<br/>"
            + headers[d.index] + " : " + Math.round(d.value) + "<br/>";
        }

      function mouseover(d, i) {
        d3.select("#tooltip")
          .style("visibility", "visible")
          .html(groupTip(d))
          .style("top", function () { return (d3.event.pageY - 80)+"px"})
          .style("left", function () { return (d3.event.pageX - 130)+"px";})

        chordPaths.classed("fade", function(p) {
          return p.source.index != i
              && p.target.index != i;
        });
  }

}



// Returns an event handler for fading a given chord group.
function fade(opacity) {
  return function(d, i) {
    svg.selectAll("path.chord")
        .filter(function(d) { return d.source.index != i && d.target.index != i; })
      .transition()
        .style("stroke-opacity", opacity)
        .style("fill-opacity", opacity);
  };
}

    </script>
  </body>
</html>

这很好用,功能齐全。它显示了2012年秋季至2013年秋季学院之间的学生运动(我计划在屏幕上做出更好的解释)。我的下一步是让这个可钻孔。因此,举例来说,如果你点击一所大学,我想把这所大学扩展到各个部门,并展示各个部门之间的和弦,以及#34;其他&#34;这将是所有其他学院的组合。此外,您将能够以相同的方式深入到部门以获得主要细节。我拥有完成此操作所需的所有原始数据,并且我非常自信地知道如何在单击某个组时动态创建新矩阵。

我的问题是,如果我只是将当前的viz转换为每次都有新数据的新视频,那么我上面描述的内容就相当简单,但这将是一个严峻的过渡。我喜欢像this这样的动画。但是,该示例仅在转换的每一端的矩阵大小相同时才有效。由于我希望如何深入研究这些数据,对我来说并非总是如此。我可以拥有比以前更少或更多的组/和弦。我的问题是,如果我使用比当前屏幕更大/更小的矩阵创建新的和弦布局,我能否以某种方式平滑地为/新的/不需要的组/和弦制作动画?如果是这样,我将如何解决这个问题,那里有任何例子吗?

我对D3仍然相当新,但我正在努力学习,因为这些类型的可视化对此有很多需求。如果有一个关于这个的教程可以随意链接它,我现在已经开启和关闭了几天,并且没有找到任何令人满意的解释如何在d3中顺利添加/删除元素布局。

1 个答案:

答案 0 :(得分:1)

为了回答这个问题,我使用了AmeliaBR对这个问题的出色答案:here

我对此解决方案做了一些更改:

首先,我更新了组出口以获得起始不透明度。它的编码方式,如果我使用属性而不是样式,那么退出的任何内容都会弹出而不是淡出,因为转换需要一个起始属性值而无法获取当前值:

        groupG.exit()
        .attr("fill-opacity", 1)
        .attr("stroke-opacity", 1)
        .transition()
            .duration(300)
            //change fill and stroke opacity to avoid CSS conflicts
            .attr("fill-opacity", 0)
            .attr("stroke-opacity", 0)
            .remove(); //remove after transitions are complete

这确实会导致褪色的和弦在动画过程中快速淡化和淡化,但它看起来比弹出更平滑,所以我保留了它。

另外,我在IE中遇到了问题,特别是因为我们在校园中安装的IE默认文件模式为IE7,而你至少需要IE9才能工作。它只需要一行代码来强制进行此更改:

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

此外,指针事件在IE11之前的IE中不起作用,我在示例中使用了div工具提示而不是工具提示,因此我需要将工具提示设置得远离鼠标,用户不会触摸它。这是一个......不太优雅的解决方案,我想找到一个更好的解决方案,但它可能不值得花时间。

我的最终解决方案可以找到here。我打算更新这个,1)解读一些不必要的和弦重叠和2)使我的数据更新功能与模板数据集一起工作,而不是我为此目的创建的一个特定数据集。