【发布时间】:2016-06-16 11:29:51
【问题描述】:
我是 D3.js 的新手,我正在使用它来构建一个树形图,就像在这个 JSFiddle 中一样(注意这不是我的,这正是我的样子)。
我正在尝试使用 D3-tip 创建在将鼠标悬停在树中的路径链接上时触发的工具提示。但是由于路径元素的实际大小很小,触发起来非常困难。
有没有什么办法可以透明地增加 D3 路径元素的面积并从 Javascript 中,以便更容易触发这些事件?我在 here 和 here 中看到过其他 SO 示例,但我无法在 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 }
})
问题是,即使绘制了线条,它也不会在浏览器中显示为元素,因此根本不会影响该区域。有什么办法可以改变吗?
【问题讨论】:
-
我会尝试在你的可见线上画一条透明线,并将鼠标事件添加到透明线上。我看到你在编辑中提到了这一点,但看起来你正在采取迂回的方式。只需以与添加可见线完全相同的方式添加透明线。如果您提供特定代码的小提琴,我们很乐意提供帮助。
标签: javascript d3.js svg tooltip