d3 Sunburst在点击时减少父节点内外半径

时间:2016-10-28 15:10:47

标签: javascript d3.js svg data-visualization

当我点击其中一个子节点时,我正在尝试减小父级内外半径的大小。您可以在此处查看我当前的图表:https://jsfiddle.net/2heLd2b1/。如您所见,当单击子节点并且扭曲以显示所选节点及其路径时,父层占用太多空间。我正在寻找关于如何减少或缩小父节点宽度的任何建议。

ar width = 960,
    height = 750,
    radius = (Math.min(width, height) / 2) - 10;

var color = d3.scale.category20c();

var x = d3.scale.linear()
    .range([0, 2 * Math.PI]);

var y = d3.scale.linear()
    .range([0, radius]);

function percent(d) {
    var percentage = (d.value / 956129) * 100;
    return percentage.toFixed(2);
}

// var tip = d3.tip()
//   .attr('class', 'd3-tip')
//   .offset([-10, 0])
//   .html(function(d) {
//     return "<strong>" + d.name + "</strong> <span style='color:red'>" + percent(d) + "%</span>";
//   })

var partition = d3.layout.partition()
    // .value(function(d) { return d.size; });
    .value(function(d) { return 1; });

var arc = d3.svg.arc()
    .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
    .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
    .innerRadius(function(d) { return Math.max(0, y(d.y)) })
    .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)) })
    .cornerRadius(function(d) { return 5;});


var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
    .append("g")
    .classed("inner", true);

// svg.call(tip);

d3.json("flare.json", function(error, root) {
    if (error) throw error;

    var g = svg.selectAll("g")
        .data(partition.nodes(root))
        .enter().append("g");

    path = g.append("path")
        .attr("d", arc)
        .attr('stroke', 'white')
        .attr("fill", function(d) { return color((d.children ? d : d.parent).name); })
        .on("click", magnify)
        // .on('mouseover', tip.show)
        // .on('mouseout', tip.hide)
        .each(stash);

    var text = g.append("text")
        .attr("x", function(d) { return d.x; })
        .attr("dx", "6") // margin
        .attr("dy", ".35em") // vertical-align
        .text(function(d) {
            return d.name;
        })
        .attr('font-size', function(d) {
            return '10px';
        })
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
            if (d.depth > 0) {
                return "translate(" + arc.centroid(d) + ")" +
                       "rotate(" + getStartAngle(d) + ")";
            } else {
                return null;
            }
        })
        .on("click", magnify);

    var innerG = d3.selectAll("g.inner");



    // Distort the specified node to 80% of its parent.
    function magnify(node) {

        // get and store parent sequence
        var parentSequence = getAncestors(node)

        text.transition().attr("opacity", 0);
        spin(node);

        // check if node has a parent. If so, iterate throught parentSequence and update the size of each node in the sequence
        if (node.parent) {
            for (var p = 0; p < parentSequence.length; p++) {
                if (parent = parentSequence[p].parent) {
                    var parent,
                        x = parent.x,
                        k = 0.95;
                    parent.children.forEach(function(sibling) {
                        x += reposition(sibling, x, sibling === parentSequence[p]
                          ? parent.dx * k / parentSequence[p].value
                          : parent.dx * (1 - k) / (parent.value - parentSequence[p].value));
                    });
                } else {
                    reposition(parentSequence[p], 0, parentSequence[p].dx / parentSequence[p].value);
                }
            }
        // if node does not have parent (center node) reset all values to original
        } else {
            if (parent = node.parent) {
                var parent,
                    x = parent.x,
                    k = 0.95;
                parent.children.forEach(function(sibling) {
                    x += reposition(sibling, x, sibling === node
                      ? parent.dx * k / node.value
                      : parent.dx * (1 - k) / (parent.value - node.value));
                });
            } else {
                reposition(node, 0, node.dx / node.value);
            }
        }

        path.transition()
        .duration(750)
        .attrTween("d", arcTween)
        .each("end", function(e, i) {

          // check if the animated element's data e lies within the visible angle span given in node
            if (e.x >= node.x && e.x < (node.x + node.dx)) {
            // get a selection of the associated text element
                var arcText = d3.select(this.parentNode).select("text");
            // fade in the text element and recalculate positions
                arcText.transition().duration(750)
                .attr("opacity", 1)
                .attr("x", function(d) {
                    return d.x;
                })
                .attr("transform", function(d) {
                    if (d.depth > 0) {
                        return "translate(" + arc.centroid(d) + ")" +
                               "rotate(" + getNewAngle(d) + ")";
                    }  else {
                        return null;
                    }
                });
            }
        });
    }

    function spin(d) {
        var spin1 = new Promise (function(resolve, reject) {
            var newAngle = - x(d.x + d.dx / 2);
            // console.log('newAngle', newAngle)

            innerG
                .transition()
                .duration(1500)
                .attr("transform", "rotate(" + ((180 / Math.PI * newAngle)) + ")");
                resolve("Success!");
        });

        spin1.then(function() {
            var newerAngle = - x(d.x + d.dx / 2);
            // console.log('newerAngle', newerAngle)

            innerG
                .transition()
                .duration(1500)
                .attr("transform", "rotate(" + ((180 / Math.PI * newerAngle)) + ")");
        })

        path
            .classed("selected", function (x) { return d.name == x.name; });
    }

    // Recursively reposition the node at position x with scale k.
    function reposition(node, x, k) {
        // console.log(k)
        node.x = x;
        if (node.children && (n = node.children.length)) {
            var i = -1, n;
            while (++i < n) x += reposition(node.children[i], x, k);
        }
        return node.dx = node.value * k;
    }

    // Stash the old values for transition.
    function stash(d) {
        d.x0 = d.x;
        d.dx0 = d.dx;
    }

    // Interpolate the arcs in data space.
    function arcTween(a) {
        var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
        return function(t) {
            var b = i(t);
            a.x0 = b.x;
            a.dx0 = b.dx;
            return arc(b);
        };
    };
});

function getStartAngle(d) {
    // Offset the angle by 90 deg since the '0' degree axis for arc is Y axis, while
    // for text it is the X axis.
    var thetaDeg = (180 / Math.PI * (arc.startAngle()(d) + arc.endAngle()(d)) / 2 - 90);
    // If we are rotating the text by more than 90 deg, then "flip" it.
    // This is why "text-anchor", "middle" is important, otherwise, this "flip" would
    // a little harder.
    return (thetaDeg > 90) ? thetaDeg - 180 : thetaDeg;
}

function getNewAngle(d) {
    var thetaDeg = (180 / Math.PI * (arc.startAngle()(d) + arc.endAngle()(d)) / 2 - 90);
    return (thetaDeg < 90) ? thetaDeg - 180 : thetaDeg;
}

function getAncestors(node) {
  var path = [];
  var current = node;
  while (current.parent) {
    path.unshift(current);
    current = current.parent;
  }
  return path;
}

1 个答案:

答案 0 :(得分:0)

我设法通过将我原来的jsFiddle链接https://jsfiddle.net/2heLd2b1/中的补间方法与可缩放的sunburst使用的传统补间相结合来解决这个问题。你可以在这里看到实现:https://jsfiddle.net/6e4y0s11/

我改变了我的innerRadius和outerRadius:

var arc = d3.svg.arc()
    .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
    .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
    .innerRadius(function(d) { return Math.max(0, y(d.y)) })
    .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)) })
    .cornerRadius(function(d) { return 5;});

为:

var arc = d3.svg.arc()
    .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
    .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
    .innerRadius(function(d) { return Math.max(d.depth * 20, y(d.y)) })
    .outerRadius(function(d) { return Math.max(100, y(d.y + d.dy)) })
    .cornerRadius(function(d) { return 5;});

我还补充说:

function arcTweenZoom(d) {
    var yd = d3.interpolate(y.domain(), [d.y, 1]),
        yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
  return function(d, i) {
    return i
        ? function(t) { return arc(d); }
        : function(t) {
            y.domain(yd(t)).range(yr(t));
            return arc(d);

        };
  };
}

这样我就可以插入音阶。最终结果允许内部父级节点收缩而不会实际消失。根据d.y值保持最小半径。