在 d3js 中显示关于力定向图链接的工具提示
Show tool-tip on links of force directed graph in d3js
我正在研究 This link 中给出的简单力导向图。它运行良好,但我想在边缘上显示工具提示,就像它在节点上显示的那样。每当我将鼠标移到边上时,它都会在 link 上显示一些工具提示(可以从 JSON 文件中检索数据)。
是否有内置方法或者我必须在鼠标悬停时显示 div(在这种情况下如何获取鼠标的位置,div 将显示在何处)
您可以将 <title>
元素用于 line
元素,就像链接演示用于 circle
元素一样。例如,
link.append("title")
.text(function(d) { return "This is my title"; });
将在链接的演示中创建此类工具提示。请注意,according to the spec,您应确保标题元素是 parent 的第一个 child。
另请注意,这可能不是与用户沟通的有效方式;将鼠标悬停在一条线上是相当困难的,尤其是在移动图形中。一个可能的解决方案是用一条宽度更大的不可见线将每一行包裹在一个更大的组中,并将标题添加为该组的第一个 child,因为标题元素也适用于 <g>
元素。
- 是否有内置方法?
回答:是的。有一个内置的方式。大多数 HTML 元素都支持 title 属性。当您将鼠标指针移到该元素上时,会显示一个小工具提示,持续一段时间或直到您离开该元素。
演示:
var w = 500,
h = 200
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var graph = {
nodes: [{
name: 'A'
}, {
name: 'B'
}],
links: [{
source: 0,
target: 1
}]
};
var force = d3.layout.force()
.nodes(graph.nodes)
.links(graph.links)
.gravity(.05)
.distance(100)
.charge(-100)
.size([w, h])
.start();
var link = vis.selectAll("line.link")
.data(graph.links)
.enter().append("svg:line")
.attr("class", "link");
link.append("title").text(function(d) {
return d.source.name + " -> " + d.target.name
});
var node = vis.selectAll("g.node")
.data(graph.nodes)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
node.append("circle").attr("r", 5);
node.append("title").text(function(d) {
return d.name
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
line {
stroke: red;
stroke-width: 3;
}
cicrle {
fill: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
- 或者我必须在鼠标悬停时显示 div?如何获取鼠标的位置? div 会显示在哪里?
答案:
如果您想显示包含 html 内容的工具提示,您可以在鼠标悬停时显示 div。您可以使用 d3.mouse
方法获取鼠标位置。
演示:
var width = 960;
var height = 500;
var margin = 20;
var pad = margin / 2;
var color = d3.scale.category20();
var graph = {
"nodes": [{
"name": "Myriel",
"group": 1
}, {
"name": "Napoleon",
"group": 1
}, {
"name": "Mlle.Baptistine",
"group": 1
}, {
"name": "Mme.Magloire",
"group": 1
}, {
"name": "CountessdeLo",
"group": 1
}, {
"name": "Geborand",
"group": 1
}, {
"name": "Champtercier",
"group": 1
}, {
"name": "Cravatte",
"group": 1
}, {
"name": "Count",
"group": 1
}, {
"name": "OldMan",
"group": 1
}, {
"name": "Labarre",
"group": 2
}, {
"name": "Valjean",
"group": 2
}, {
"name": "Marguerite",
"group": 3
}, {
"name": "Mme.deR",
"group": 2
}, {
"name": "Isabeau",
"group": 2
}, {
"name": "Gervais",
"group": 2
}, {
"name": "Tholomyes",
"group": 3
}, {
"name": "Listolier",
"group": 3
}, {
"name": "Fameuil",
"group": 3
}, {
"name": "Blacheville",
"group": 3
}, {
"name": "Favourite",
"group": 3
}, {
"name": "Dahlia",
"group": 3
}, {
"name": "Zephine",
"group": 3
}, {
"name": "Fantine",
"group": 3
}, {
"name": "Mme.Thenardier",
"group": 4
}, {
"name": "Thenardier",
"group": 4
}, ],
"links": [{
"source": 1,
"target": 0,
"value": 1
}, {
"source": 2,
"target": 0,
"value": 8
}, {
"source": 3,
"target": 0,
"value": 10
}, {
"source": 3,
"target": 2,
"value": 6
}, {
"source": 4,
"target": 0,
"value": 1
}, {
"source": 5,
"target": 0,
"value": 1
}, {
"source": 6,
"target": 0,
"value": 1
}]
};
drawGraph(graph);
function drawGraph(graph) {
var svg = d3.select("#force").append("svg")
.attr("width", width)
.attr("height", height);
// draw plot background
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "#eeeeee");
// create an area within svg for plotting graph
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-force
var layout = d3.layout.force()
.size([width - margin, height - margin])
.charge(-120)
.linkDistance(function(d, i) {
return (d.source.group == d.target.group) ? 50 : 100;
})
.nodes(graph.nodes)
.links(graph.links)
.start();
drawLinks(graph.links);
drawNodes(graph.nodes);
// add ability to drag and update layout
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-drag
d3.selectAll(".node").call(layout.drag);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-on
layout.on("tick", function() {
d3.selectAll(".link")
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
d3.selectAll(".node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
}
// Draws nodes on plot
function drawNodes(nodes) {
// used to assign nodes color by group
var color = d3.scale.category20();
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-nodes
d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", function(d, i) {
return d.name;
})
.attr("cx", function(d, i) {
return d.x;
})
.attr("cy", function(d, i) {
return d.y;
})
.attr("r", function(d, i) {
return 4;
})
.style("fill", function(d, i) {
return color(d.group);
})
.on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
// Draws edges between nodes
function drawLinks(links) {
var scale = d3.scale.linear()
.domain(d3.extent(links, function(d, i) {
return d.value;
}))
.range([1, 6]);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-links
d3.select("#plot").selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
.style("stroke-width", function(d, i) {
return scale(d.value) + "px";
})
.style("stroke-dasharray", function(d, i) {
return (d.value <= 1) ? "2, 2" : "none";
}).on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.source.name + " -> " + d.target.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
b {
font-weight: 900;
}
.outline {
fill: none;
stroke: #888888;
stroke-width: 1px;
}
#tooltip {
font-size: 10pt;
font-weight: 900;
fill: #000000;
stroke: #ffffff;
stroke-width: 0.25px;
}
.node {
stroke: #ffffff;
stroke-weight: 1px;
}
.link {
fill: none;
stroke: #888888;
stroke-weight: 1px;
stroke-opacity: 0.5;
}
.highlight {
stroke: red;
stroke-weight: 4px;
stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>
我正在研究 This link 中给出的简单力导向图。它运行良好,但我想在边缘上显示工具提示,就像它在节点上显示的那样。每当我将鼠标移到边上时,它都会在 link 上显示一些工具提示(可以从 JSON 文件中检索数据)。 是否有内置方法或者我必须在鼠标悬停时显示 div(在这种情况下如何获取鼠标的位置,div 将显示在何处)
您可以将 <title>
元素用于 line
元素,就像链接演示用于 circle
元素一样。例如,
link.append("title")
.text(function(d) { return "This is my title"; });
将在链接的演示中创建此类工具提示。请注意,according to the spec,您应确保标题元素是 parent 的第一个 child。
另请注意,这可能不是与用户沟通的有效方式;将鼠标悬停在一条线上是相当困难的,尤其是在移动图形中。一个可能的解决方案是用一条宽度更大的不可见线将每一行包裹在一个更大的组中,并将标题添加为该组的第一个 child,因为标题元素也适用于 <g>
元素。
- 是否有内置方法?
回答:是的。有一个内置的方式。大多数 HTML 元素都支持 title 属性。当您将鼠标指针移到该元素上时,会显示一个小工具提示,持续一段时间或直到您离开该元素。
演示:
var w = 500,
h = 200
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var graph = {
nodes: [{
name: 'A'
}, {
name: 'B'
}],
links: [{
source: 0,
target: 1
}]
};
var force = d3.layout.force()
.nodes(graph.nodes)
.links(graph.links)
.gravity(.05)
.distance(100)
.charge(-100)
.size([w, h])
.start();
var link = vis.selectAll("line.link")
.data(graph.links)
.enter().append("svg:line")
.attr("class", "link");
link.append("title").text(function(d) {
return d.source.name + " -> " + d.target.name
});
var node = vis.selectAll("g.node")
.data(graph.nodes)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
node.append("circle").attr("r", 5);
node.append("title").text(function(d) {
return d.name
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
line {
stroke: red;
stroke-width: 3;
}
cicrle {
fill: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
- 或者我必须在鼠标悬停时显示 div?如何获取鼠标的位置? div 会显示在哪里?
答案:
如果您想显示包含 html 内容的工具提示,您可以在鼠标悬停时显示 div。您可以使用 d3.mouse
方法获取鼠标位置。
演示:
var width = 960;
var height = 500;
var margin = 20;
var pad = margin / 2;
var color = d3.scale.category20();
var graph = {
"nodes": [{
"name": "Myriel",
"group": 1
}, {
"name": "Napoleon",
"group": 1
}, {
"name": "Mlle.Baptistine",
"group": 1
}, {
"name": "Mme.Magloire",
"group": 1
}, {
"name": "CountessdeLo",
"group": 1
}, {
"name": "Geborand",
"group": 1
}, {
"name": "Champtercier",
"group": 1
}, {
"name": "Cravatte",
"group": 1
}, {
"name": "Count",
"group": 1
}, {
"name": "OldMan",
"group": 1
}, {
"name": "Labarre",
"group": 2
}, {
"name": "Valjean",
"group": 2
}, {
"name": "Marguerite",
"group": 3
}, {
"name": "Mme.deR",
"group": 2
}, {
"name": "Isabeau",
"group": 2
}, {
"name": "Gervais",
"group": 2
}, {
"name": "Tholomyes",
"group": 3
}, {
"name": "Listolier",
"group": 3
}, {
"name": "Fameuil",
"group": 3
}, {
"name": "Blacheville",
"group": 3
}, {
"name": "Favourite",
"group": 3
}, {
"name": "Dahlia",
"group": 3
}, {
"name": "Zephine",
"group": 3
}, {
"name": "Fantine",
"group": 3
}, {
"name": "Mme.Thenardier",
"group": 4
}, {
"name": "Thenardier",
"group": 4
}, ],
"links": [{
"source": 1,
"target": 0,
"value": 1
}, {
"source": 2,
"target": 0,
"value": 8
}, {
"source": 3,
"target": 0,
"value": 10
}, {
"source": 3,
"target": 2,
"value": 6
}, {
"source": 4,
"target": 0,
"value": 1
}, {
"source": 5,
"target": 0,
"value": 1
}, {
"source": 6,
"target": 0,
"value": 1
}]
};
drawGraph(graph);
function drawGraph(graph) {
var svg = d3.select("#force").append("svg")
.attr("width", width)
.attr("height", height);
// draw plot background
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "#eeeeee");
// create an area within svg for plotting graph
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-force
var layout = d3.layout.force()
.size([width - margin, height - margin])
.charge(-120)
.linkDistance(function(d, i) {
return (d.source.group == d.target.group) ? 50 : 100;
})
.nodes(graph.nodes)
.links(graph.links)
.start();
drawLinks(graph.links);
drawNodes(graph.nodes);
// add ability to drag and update layout
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-drag
d3.selectAll(".node").call(layout.drag);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-on
layout.on("tick", function() {
d3.selectAll(".link")
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
d3.selectAll(".node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
}
// Draws nodes on plot
function drawNodes(nodes) {
// used to assign nodes color by group
var color = d3.scale.category20();
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-nodes
d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", function(d, i) {
return d.name;
})
.attr("cx", function(d, i) {
return d.x;
})
.attr("cy", function(d, i) {
return d.y;
})
.attr("r", function(d, i) {
return 4;
})
.style("fill", function(d, i) {
return color(d.group);
})
.on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
// Draws edges between nodes
function drawLinks(links) {
var scale = d3.scale.linear()
.domain(d3.extent(links, function(d, i) {
return d.value;
}))
.range([1, 6]);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-links
d3.select("#plot").selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
.style("stroke-width", function(d, i) {
return scale(d.value) + "px";
})
.style("stroke-dasharray", function(d, i) {
return (d.value <= 1) ? "2, 2" : "none";
}).on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.source.name + " -> " + d.target.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
b {
font-weight: 900;
}
.outline {
fill: none;
stroke: #888888;
stroke-width: 1px;
}
#tooltip {
font-size: 10pt;
font-weight: 900;
fill: #000000;
stroke: #ffffff;
stroke-width: 0.25px;
}
.node {
stroke: #ffffff;
stroke-weight: 1px;
}
.link {
fill: none;
stroke: #888888;
stroke-weight: 1px;
stroke-opacity: 0.5;
}
.highlight {
stroke: red;
stroke-weight: 4px;
stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>