如何将缩放添加到 d3 可折叠树示例? [英] How can I add zooming to the d3 collapsible tree example?

查看:26
本文介绍了如何将缩放添加到 d3 可折叠树示例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试向 d3 可折叠树添加缩放功能示例,但无法让它工作.我知道在这里有一个问题即使看起来相当彻底,我的更改也不起作用.我唯一得到的是一个白页.可能与我对 javascript/d3 比较陌生的事实有关.这是我的代码.

I am trying to add a zooming feature to the d3 collapsible tree example, but can't get it to work. I know there's been a question about this here and even though it seems rather thorough my changes don't work. The only thing I get is a white page. Probably has to do with the fact that I'm rather new with javascript/d3. Here is my code.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

svg {
    pointer-events: all;
}

.node {
  cursor: pointer;
}

.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}

tree {
pointer-events: all;
}

.node text {
  font: 12px sans-serif;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;
}



</style>
<body>
<script src="d3.v3.min.js"></script>
<script>

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 1500 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

var i = 0,
    duration = 750,
    root;

var tree = d3.layout.tree()
    .size([height, width]);

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

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

d3.json("flare.json", function(error, flare) {
  root = flare;
  root.x0 = height / 2;
  root.y0 = 0;

  function collapse(d) {
    if (d.children) {
      d._children = d.children;
      d._children.forEach(collapse);
      d.children = null;
    }
  }

  root.children.forEach(collapse);
  update(root);
});

d3.select(self.frameElement).style("height", "800px");

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Update the nodes…
  var node = svg.selectAll("g.node")
      .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .on("click", click);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeEnter.append("text")
      .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
      .text(function(d) { return d.name; })
      .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

  nodeUpdate.select("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

  nodeUpdate.select("text")
      .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .remove();

  nodeExit.select("circle")
      .attr("r", 1e-6);

  nodeExit.select("text")
      .style("fill-opacity", 1e-6);

  // Update the links…
  var link = svg.selectAll("path.link")
      .data(links, function(d) { return d.target.id; });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
      .attr("class", "link")
      .attr("d", function(d) {
        var o = {x: source.x0, y: source.y0};
        return diagonal({source: o, target: o});
      });

  // Transition links to their new position.
  link.transition()
      .duration(duration)
      .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {x: source.x, y: source.y};
        return diagonal({source: o, target: o});
      })
      .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });

    d3.select("svg")
    .call(d3.behavior.zoom()
          .scaleExtent([0.5, 5])
          .on("zoom", zoom));


}

// Toggle children on click.
function click(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
  update(d);


function zoom() {
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -h * scale,
        bbound = h * scale,
        lbound = (-w + m[1]) * scale,
        rbound = (w - m[3]) * scale;
    // limit translation to thresholds
    translation = [
        Math.max(Math.min(translation[0], rbound), lbound),
        Math.max(Math.min(translation[1], bbound), tbound)
    ];
    d3.select(".drawarea")
        .attr("transform", "translate(" + translation + ")" +
              " scale(" + scale + ")");
}




}

</script>

为什么缩放实现不起作用?

Why does the zoom implementation not work?

推荐答案

我玩了一下你的代码,发现了几个潜在的问题:

I played with your code a bit and found several potential issues:

根据您在哪里/如何运行它,加载flare.json 文件可能会导致CORS 错误(例如,如果您尝试链接到bostock 的github 上的那个).您可以将 json 对象直接嵌入到flare"变量中,然后将 d3.json() 方法中的代码移动到脚本底部并将根变量分配给flare.

Depending on where/how you are running this, loading the flare.json file could cause a CORS error (eg. if you try to link to the one on bostock's github). You can embed the json object directly in a 'flare' variable and then just move the code you have in the d3.json() method to the bottom of the script and assign the root variable to flare.

var flare =  ...paste contents of flare.json here... ;

您的 javascript 方法是按照您在定义之前引用方法的顺序定义的(您应该在更新方法之前移动缩放和点击方法):

Your javascript methods are defined in an order where you reference methods before they are defined (you should move the zoom and click methods before the update method):

collapse(d){...}
zoom(){...}
click(){...}
update(){...}

您的缩放方法引用了h"、w"和m",它们没有在任何地方定义.我假设它们应该是宽度、高度和边距:

Your zoom method references 'h', 'w', and 'm', which aren't defined anywhere. I assume they are supposed to be width, height, and margin:

var zoom = function() {
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -height * scale,
        bbound = height * scale,
        lbound = (-width + margin.right) * scale,
        rbound = (width - margin.left) * scale;

最后,要修复缩放问题,您应该像这样修复缩放方法:

Finally, to fix the zoom thing, you should fix the zoom method like so:

var zoom = function() {
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -height * scale,
        bbound = height * scale,
        lbound = (-width + margin.right) * scale,
        rbound = (width - margin.left) * scale;
    // limit translation to thresholds
    translation = [
        Math.max(Math.min(translation[0], rbound), lbound),
        Math.max(Math.min(translation[1], bbound), tbound)
    ];
    svg.attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")");
}

请注意,我用svg"替换了select(".drawArea")".因为您想转换顶部的 g 元素(这是svg"变量中的内容).

Note that I replaced the "select(".drawArea")" with "svg." because you want to transform the top g element (which is what is in the "svg" variable).

仅供参考 - 我真的无法在 plnkr 或 jsFiddle 中使用它,因为在 plnkr 和 jsFiddle 中处理变量/函数作用域的方式导致了奇怪的 d3 错误(请参阅 此处).这可能是其他人可以解决的问题.但这是完整的代码(减去嵌入的flare.json 变量).我在自己的 Tomcat 容器中运行了这段代码,可以双击树并放大它.您可能想尝试缩放和平移,以便树不会绘制到可见窗口的边界之外(现在如果你双击它会放大,但居中,所以树的左侧不在浏览器窗口的一侧).

Just FYI - I couldn't really get this working in plnkr or jsFiddle because there are weird d3 errors due to the way variables/function scope are handled in plnkr and jsFiddle (see here). That may be an issue someone else can work out. But here's the code in it's entirety (minus the embedded flare.json variable). I ran this code in my own Tomcat container and can double click on the tree and it zooms in. You may want to play around with zooming and panning so that the tree doesn't draw outside of the boundaries of the visibile window (right now if you double click it will zoom, but recenter, so the left side of the tree is off the side of the browser window).

<!DOCTYPE html>
<meta charset="utf-8">
<style>

svg {  pointer-events: all;  }
.node { cursor: pointer;  }
.node circle {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 1.5px;
}
tree {  pointer-events: all;  }
.node text {  font: 12px sans-serif;  }
.link {
    fill: none;
    stroke: #ccc;
    stroke-width: 1.5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 1500 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

var i = 0,
    duration = 750,
    root;

var tree = d3.layout.tree()
    .size([height, width]);

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

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

d3.select(self.frameElement).style("height", "800px");

var collapse = function(d) {
    if (d.children) {
        d._children = d.children;
        d._children.forEach(collapse);
        d.children = null;
    }
}

var zoom = function() {
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -height * scale,
        bbound = height * scale,
        lbound = (-width + margin.right) * scale,
        rbound = (width - margin.left) * scale;
    // limit translation to thresholds
    translation = [
        Math.max(Math.min(translation[0], rbound), lbound),
        Math.max(Math.min(translation[1], bbound), tbound)
    ];
    svg.attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")");
}

// Toggle children on click.
var click = function(d) {
    if (d.children) {
        d._children = d.children;
        d.children = null;
    } else {
        d.children = d._children;
        d._children = null;
    }
    update(d);
};

var update = function(source) {
    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
        links = tree.links(nodes);

    // Normalize for fixed-depth.
    nodes.forEach(function(d) { d.y = d.depth * 180; });

    // Update the nodes…
    var node = svg.selectAll("g.node")
        .data(nodes, function(d) { return d.id || (d.id = ++i); });

    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
        .on("click", click);

    nodeEnter.append("circle")
        .attr("r", 1e-6)
        .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

    nodeEnter.append("text")
        .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
        .attr("dy", ".35em")
        .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
        .text(function(d) { return d.name; })
        .style("fill-opacity", 1e-6);

    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

    nodeUpdate.select("circle")
        .attr("r", 4.5)
        .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

    nodeUpdate.select("text")
        .style("fill-opacity", 1);

    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
        .duration(duration)
        .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
        .remove();

    nodeExit.select("circle")
        .attr("r", 1e-6);

    nodeExit.select("text")
        .style("fill-opacity", 1e-6);

    // Update the links…
    var link = svg.selectAll("path.link")
        .data(links, function(d) { return d.target.id; });

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", function(d) {
            var o = {x: source.x0, y: source.y0};
            return diagonal({source: o, target: o});
         });

    // Transition links to their new position.
    link.transition()
        .duration(duration)
        .attr("d", diagonal);

    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
        .duration(duration)
        .attr("d", function(d) {
            var o = {x: source.x, y: source.y};
            return diagonal({source: o, target: o});
         })
        .remove();

    // Stash the old positions for transition.
    nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
    });

    d3.select("svg")
        .call(d3.behavior.zoom()
        .scaleExtent([0.5, 5])
        .on("zoom", zoom));
};

d3.json("flare.json", function(json){
    root = json;
    root.x0 = height / 2;
    root.y0 = 0;
    root.children.forEach(collapse);
    update(root);
});

</script>

这篇关于如何将缩放添加到 d3 可折叠树示例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆