修改可折叠缩进树中的条宽或显示大小

时间:2018-07-31 15:31:06

标签: d3.js

下面的可折叠缩进树代码片段显示了叶子节点的值,但对于非叶子节点,它显示了“未定义”。有没有办法让barwidth(非叶节点)反映该节点的值(在flare.json示例中为size),而非叶节点有一种方法反映其基础子值的总和? Final Output I am Looking to achieve

<script src="https://d3js.org/d3.v4.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node rect {
  cursor: pointer;
  fill: #fff;
  fill-opacity: 0.5;
  stroke: #3182bd;
  stroke-width: 1.5px;
}
.node text {
  font: 10px sans-serif;
  pointer-events: none;
}
.link {
  fill: none;
  stroke: #9ecae1;
  stroke-width: 1.5px;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 20},
    width = 960,
    barHeight = 20,
    barWidth = (width - margin.left - margin.right) * 0.8;
var i = 0,
    duration = 400,
    root;
var diagonal = d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; });
var svg = d3.select("body").append("svg")
    .attr("width", width) // + margin.left + margin.right)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("https://gist.githubusercontent.com/mbostock/1093025/raw/b40b9fc5b53b40836ead8aa4b4a17d948b491126/flare.json", function(error, flare) {
  if (error) throw error;
  root = d3.hierarchy(flare);
  root.x0 = 0;
  root.y0 = 0;
  root.children.forEach(function(d){
    d._children = d.children;
    d.children = null;
  });
  update(root);
});
function update(source) {
  // Compute the flattened node list.
  var nodes = root.descendants();
  var height = Math.max(500, nodes.length * barHeight + margin.top + margin.bottom);
  d3.select("svg").transition()
      .duration(duration)
      .attr("height", height);
  d3.select(self.frameElement).transition()
      .duration(duration)
      .style("height", height + "px");
  // Compute the "layout". TODO https://github.com/d3/d3-hierarchy/issues/67
  var index = -1;
  root.eachBefore(function(n) {
    n.x = ++index * barHeight;
    n.y = n.depth * 20;
  });
  // Update the nodes…
  var node = svg.selectAll(".node")
    .data(nodes, function(d) { return d.id || (d.id = ++i); });
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .style("opacity", 0);
  // Enter any new nodes at the parent's previous position.
  nodeEnter.append("rect")
      .attr("y", -barHeight / 2)
      .attr("height", barHeight)
      .attr("width", barWidth)
      .style("fill", color)
      .on("click", click);
  nodeEnter.append("text")
      .attr("dy", 3.5)
      .attr("dx", 5.5)
      .text(function(d) { return d.data.name + " value:" + d.data.size; });
  // Transition nodes to their new position.
  nodeEnter.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1);
  node.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1)
    .select("rect")
      .style("fill", color);
  // Transition exiting nodes to the parent's new position.
  node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .style("opacity", 0)
      .remove();
  // Update the links…
  var link = svg.selectAll(".link")
    .data(root.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()
      .duration(duration)
      .attr("d", diagonal);
  // 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.
  root.each(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}
// 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 color(d) {
  return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}
</script>

1 个答案:

答案 0 :(得分:1)

使用d3.sumd3.sum)计算大小,这是一个基于children长度的递归函数:

function getSize(d) {
   var children = d.children || d._children;
   return children ? d3.sum(children, function(row) { return (row.children || row._children) ? getSize(row) : row.data.size; }) : d.data.size;
}

它基于d.children函数中分配的d._childrenclick。这是一个简单的递归函数,可以求和大小。

在节点文本分配中,这是我所做的更改:

.text(function(d) { 
  return d.data.name + " value:" + getSize(d); 
});

以下是使用d3.sum计算大小之和的代码段:

var margin = {top: 30, right: 20, bottom: 30, left: 20},
    width = 960,
    barHeight = 20,
    barWidth = (width - margin.left - margin.right) * 0.8;
var i = 0,
    duration = 400,
    root;
var diagonal = d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; });
var svg = d3.select("body").append("svg")
    .attr("width", width) // + margin.left + margin.right)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("https://gist.githubusercontent.com/mbostock/1093025/raw/b40b9fc5b53b40836ead8aa4b4a17d948b491126/flare.json", function(error, flare) {
  if (error) throw error;
  root = d3.hierarchy(flare);
  root.x0 = 0;
  root.y0 = 0;
  root.children.forEach(function(d){
    d._children = d.children;
    d.children = null;
  });
  update(root);
});
function update(source) {
  // Compute the flattened node list.
  var nodes = root.descendants();
  var height = Math.max(500, nodes.length * barHeight + margin.top + margin.bottom);
  d3.select("svg").transition()
      .duration(duration)
      .attr("height", height);
  d3.select(self.frameElement).transition()
      .duration(duration)
      .style("height", height + "px");
  // Compute the "layout". TODO https://github.com/d3/d3-hierarchy/issues/67
  var index = -1;
  root.eachBefore(function(n) {
    n.x = ++index * barHeight;
    n.y = n.depth * 20;
  });
  // Update the nodes…
  var node = svg.selectAll(".node")
    .data(nodes, function(d) { return d.id || (d.id = ++i); });
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .style("opacity", 0);
  // Enter any new nodes at the parent's previous position.
  nodeEnter.append("rect")
      .attr("y", -barHeight / 2)
      .attr("height", barHeight)
      .attr("width", barWidth)
      .style("fill", color)
      .on("click", click);
  nodeEnter.append("text")
      .attr("dy", 3.5)
      .attr("dx", 5.5)
      .text(function(d) { 
      	return d.data.name + " value:" + getSize(d); 
  		});
  // Transition nodes to their new position.
  nodeEnter.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1);
  node.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1)
    .select("rect")
      .style("fill", color);
  // Transition exiting nodes to the parent's new position.
  node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .style("opacity", 0)
      .remove();
  // Update the links…
  var link = svg.selectAll(".link")
    .data(root.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()
      .duration(duration)
      .attr("d", diagonal);
  // 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.
  root.each(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}
// 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 color(d) {
  return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}

function getSize(d) {
	var children = d.children || d._children;
  return children ? d3.sum(children, function(row) { return (row.children || row._children) ? getSize(row) : row.data.size; }) : d.data.size;
}
.node rect {
  cursor: pointer;
  fill: #fff;
  fill-opacity: 0.5;
  stroke: #3182bd;
  stroke-width: 1.5px;
}
.node text {
  font: 10px sans-serif;
  pointer-events: none;
}
.link {
  fill: none;
  stroke: #9ecae1;
  stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

编辑:根据尺寸更改barWidth

为此,我创建了一个线性标度,范围从0到barWidth,并且将根据当前节点的大小来设置域。 (当然,这将包含对叶子/非叶子节点的检查)。相关代码:

nodeEnter.append("rect")
  .attr("y", -barHeight / 2)
  .attr("height", barHeight)
  .attr("width", function(d) {
    if(d.children || d._children) {
        return barWidth;
    } else {
      xScale.domain([0, d3.max(d.parent.data.children, function (child) { return child.size; })]).nice();
      d.barWidth = xScale(d.data.size);
      return xScale(d.data.size);
    }
  })

使用此宽度,可以text进行如下移位:

nodeEnter.append("text").classed('value', true)
  .attr("dy", 3.5)
  .attr("dx", 5.5)
.attr('x', function (d) { 
    var labelWidth = d3.select(this.parentNode).select('text.name').node().getBBox().width;
    return ((d.children || d._children) ? labelWidth : (d.barWidth < labelWidth ? labelWidth : d.barWidth))+5;
    })      
  .text(function(d) { 
    return (d.children || d._children) ? "Size: " + getSize(d) : getSize(d); 
    }); 
  

请注意,我已经将value文本与name文本分开并进行了相应的分类。

放在一起,这是新的片段:

var margin = {top: 30, right: 20, bottom: 30, left: 20},
    width = 960,
    barHeight = 20,
    barWidth = (width - margin.left - margin.right) * 0.8;
var i = 0,
    duration = 400,
    root;
    
// x scale
var xScale = d3.scaleLinear().range([0, barWidth]);

var diagonal = d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; });
var svg = d3.select("body").append("svg")
    .attr("width", width) // + margin.left + margin.right)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("https://gist.githubusercontent.com/mbostock/1093025/raw/b40b9fc5b53b40836ead8aa4b4a17d948b491126/flare.json", function(error, flare) {
  if (error) throw error;
  root = d3.hierarchy(flare);
  root.x0 = 0;
  root.y0 = 0;
  root.children.forEach(function(d){
    d._children = d.children;
    d.children = null;
  });
  update(root);
});
function update(source) {
  // Compute the flattened node list.
  var nodes = root.descendants();
  var height = Math.max(500, nodes.length * barHeight + margin.top + margin.bottom);
  d3.select("svg").transition()
      .duration(duration)
      .attr("height", height);
  d3.select(self.frameElement).transition()
      .duration(duration)
      .style("height", height + "px");
  // Compute the "layout". TODO https://github.com/d3/d3-hierarchy/issues/67
  var index = -1;
  root.eachBefore(function(n) {
    n.x = ++index * barHeight;
    n.y = n.depth * 20;
  });
  // Update the nodes…
  var node = svg.selectAll(".node")
    .data(nodes, function(d) { return d.id || (d.id = ++i); });
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
      .style("opacity", 0);
  // Enter any new nodes at the parent's previous position.
  nodeEnter.append("rect")
      .attr("y", -barHeight / 2)
      .attr("height", barHeight)
      .attr("width", function(d) {
      	if(d.children || d._children) {
        	return barWidth;
        } else {
          xScale.domain([0, d3.max(d.parent.data.children, function (child) { return child.size; })]).nice();
          d.barWidth = xScale(d.data.size);
          return xScale(d.data.size);
        }
      })
      .style("fill", color)
      .on("click", click);
  nodeEnter.append("text").classed('name', true)
      .attr("dy", 3.5)
      .attr("dx", 5.5)
      .text(function(d) { 
      	return d.data.name; 
  		});
  nodeEnter.append("text").classed('value', true)
      .attr("dy", 3.5)
      .attr("dx", 5.5)
      .attr('x', function (d) { 
      	var labelWidth = d3.select(this.parentNode).select('text.name').node().getBBox().width;
      	return ((d.children || d._children) ? labelWidth : (d.barWidth < labelWidth ? labelWidth : d.barWidth))+5;
  		})      
      .text(function(d) { 
      	return (d.children || d._children) ? "Size: " + getSize(d) : getSize(d); 
  		});      
  // Transition nodes to their new position.
  nodeEnter.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1);
  node.transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
      .style("opacity", 1)
    .select("rect")
      .style("fill", color);
  // Transition exiting nodes to the parent's new position.
  node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
      .style("opacity", 0)
      .remove();
  // Update the links…
  var link = svg.selectAll(".link")
    .data(root.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()
      .duration(duration)
      .attr("d", diagonal);
  // 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.
  root.each(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}
// 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 color(d) {
  return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
}

function getSize(d) {
	var children = d.children || d._children;
  return children ? d3.sum(children, function(row) { return (row.children || row._children) ? getSize(row) : row.data.size; }) : d.data.size;
}
.node rect {
  cursor: pointer;
  fill: #fff;
  fill-opacity: 0.5;
  stroke: #3182bd;
  stroke-width: 1.5px;
}
.node text {
  font: 10px sans-serif;
  pointer-events: none;
}
.link {
  fill: none;
  stroke: #9ecae1;
  stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

另一个重要说明:我在栏上的名称/值标签几乎没有重叠,我也添加了一个修复程序(用于值)。您可以相应地使用文本的dxx值。

希望这会有所帮助。