增加D3路径链路的伪区域以实现更轻松的D3-Tip事件触发

时间:2016-06-16 11:29:51

标签: javascript d3.js svg tooltip

我是D3.js的新手,正在使用它来构建一个树形图,就像在JSFiddle中一样(注意这不是我的,这正是我的样子)。

我正在尝试使用D3-tip创建工具提示,这些工具提示会在将鼠标悬停在树中的路径链接上时触发。但是,由于路径元素的实际大小非常小,因此很难触发。

有没有办法透明地增加D3路径元素的区域和Javascript,以便更容易触发这些事件?我已经看到其他SO示例,例如herehere,但我无法在Javascript中正确实现它们。我显然试图增加路径元素的行程,但这看起来很傻。

谢谢。

我的代码如下:

export function createTree(json) {
    var width = 600;
    var height = 300;
    var maxLabel = 120;
    var duration = 200;
    var radius = 8;

    var i = 0;
    var root;

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

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

    var tip = d3.tip()
        .attr('class', 'd3-tip')
        .html(function(d) {
            var html = "<div id='popover-permission' align='center'> <div class='col-md-12'>" +
                "<div class='row text-center'><span style='color:#444444;'>Distribution Contract:</span><br>" +
            "<span class='textSmall' style='color:#444444;'>" + "3473247xxx78728347" + "</span></div></div></div>";

        return html;
    })

var svg = d3.select("#tree")
    .append("div")
    .classed("svg-container", true) //container class to make it responsive
    .append("svg")
    //responsive SVG needs these 2 attributes and no width and height attr
    .attr("preserveAspectRatio", "xMinYMin meet")
    .attr("viewBox", "0 0 " + width + " " + height)
    //class to make it responsive
    .classed("svg-content-responsive", true)
    .append("g")
    .attr("transform", "translate(" + maxLabel + ",0)");

svg.call(tip);

root = json;
root.x0 = height / 2;
root.y0 = 0;

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

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

    // 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", 0)
        .style("fill", function (d) {
            return d._children ? "lightsteelblue" : "white";
        });

    nodeEnter.append("text")
        .attr("text-anchor", "middle")
        .attr('x', 0)
        .attr('y', 30)
        .attr("dy", "-30")
        .attr("class", "node-text textSmall")
        .append('tspan')
        .attr('x', 0)
        .attr('dy', 10)
        .text(function(d) { return d.name; })
        .append('tspan')
        .attr('x', 0)
        .attr('dy', 20)
        .attr("class", "textSmall tree-balance")
        .text(function(d) {
            return "(" + d.value + " ETH)";
        })

    // 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", function (d) {
            return computeRadius(d);
        })
        .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", 0);
    nodeExit.select("text").style("fill-opacity", 0);

    // 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("id", function(d) {
            return d.target.address;
        })
        .attr("d", function (d) {
            //console.log(d.source.name + d.target.name + " ");
            var o = {x: source.x0, y: source.y0};
            return diagonal({source: o, target: o});
        })
        .on('mouseover', tip.show)
        .on('mouseleave', tip.hide);

    // 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;
    });
}

function computeRadius(d) {
    if (d.children || d._children) return radius + (radius * nbEndNodes(d) / 10);
    else return radius;
}

function nbEndNodes(n) {
    nb = 0;
    if (n.children) {
        n.children.forEach(function (c) {
            nb += nbEndNodes(c);
        });
    }
    else if (n._children) {
        n._children.forEach(function (c) {
            nb += nbEndNodes(c);
        });
    }
    else nb++;

    return nb;
}

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

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

update(root);

}

编辑:我还找到了this解决方案并为每条路径添加了一条透明线,如下所示:

lines = gEnter
    .selectAll('.path').data(['visible', 'invisible'])
 lines.enter()
    .append('line')
    .attr('class', 'path')
    .attr('marker-end', function(d, i, j) {
        // j is the parent's i
        if (j === 2) {
            return 'url(#arrow)';
        } else {
            return null;
        }
    })
.attr({
    // returning null from these functions simply defaults to whatever the
    // .path class's CSS props are doing
    'stroke-width': function(d, i) { return d == 'invisible' ? 10 : null },
    'stroke': function(d, i) { return d == 'invisible' ? 'transparent' : null }
})

问题是,即使绘制了线条,它也不会在浏览器中显示为元素,因此根本不会影响该区域。有没有办法改变这个?

1 个答案:

答案 0 :(得分:1)

这就是我所做的:我将link复制为link2,具有所有相同的属性,但使用不同的类,我在CSS中设置如下:

.link2{
  fill: none;
  stroke: lightgray;
  stroke-width: 20px;
  opacity: 0;
}

将鼠标悬停在路径旁边以查看标题。这是小提琴:http://jsfiddle.net/gerardofurtado/JnNwu/1025/