javascript - d3.js (v4) Scatter w/ Tooltip
问题描述
我正在 d3 中构建散点图可视化,但在显示点工具提示时遇到了小问题。数据以数组对象的形式存在,其中位置 0 的元素是x
坐标,位置 1 是y
坐标,位置 2 是点cluster
,位置 3 是cluster description
。
[
{
"points": [
[
-12.32,
15.74,
1,
"Cluster 5"
], ... ,
[
-15.38,
-3.97,
0,
"Cluster 2"
]
}
]
这是我的 JS:
d3.json("data.json", function(data) {
var points = data[0].points;
var svg = d3.select("#cluster"),
width = +svg.attr("width"),
height = +svg.attr("height");
var k = height / width,
x0 = [-15, 15],
y0 = [-15 * k, 15 * k],
x = d3.scaleLinear().domain(x0).range([0, width]),
y = d3.scaleLinear().domain(y0).range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisTop(x).ticks(12),
yAxis = d3.axisRight(y).ticks(12 * height / width);
var brush = d3.brush().on("end", brushended),
idleTimeout,
idleDelay = 350;
var tooltip = d3.select("#cluster")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("background", "#ffffff")
.text("a simple tooltip");
svg.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("cx", function(d) { return x(d[0]); })
.attr("cy", function(d) { return y(d[1]); })
.attr("r", 2.5)
.attr("fill", function(d) { return z(d[2]); })
.on("mouseover", function(d){tooltip.text(function(d) { return d[3]; }); return tooltip.style("visibility", "visible");})
.on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
svg.selectAll(".domain")
.style("display", "none");
svg.append("g")
.attr("class", "brush")
.call(brush);
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
x.domain(x0);
y.domain(y0);
} else {
x.domain([s[0][0], s[1][0]].map(x.invert, x));
y.domain([s[1][1], s[0][1]].map(y.invert, y));
svg.select(".brush").call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg.select(".axis--x").transition(t).call(xAxis);
svg.select(".axis--y").transition(t).call(yAxis);
svg.selectAll("circle").transition(t)
.attr("cx", function(d) { return x(d[0]); })
.attr("cy", function(d) { return y(d[1]); });
}
});
最后,html:
<body>
<div class="one">
<div class="two">
<svg id="cluster" width="700" height="500"></svg>
</div>
</div>
</body>
除了工具提示和缩放有点偏离中心之外,一切似乎都正常工作。我很感激任何指导。
解决方案
这是一个有效的 JS Fiddle:ScatterPlot with zoom and tooltips
主要变化:
当您在
brush
rect
圆圈顶部附加 时,圆圈上的mouse
事件将永远不会起作用。元素的顺序在这里很重要,你看!所以,这就是我所做的(移动画笔在圆圈上方附加代码:svg.append("g") .attr("class", "brush") .call(brush); svg.selectAll("circle") ...
我不确定为什么
mousemove
只有当您将鼠标悬停在圆圈上时才需要工具提示时才使用。我将鼠标事件更改为:.on("mouseover", function(d){ tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); }).on("mouseout", function(){return tooltip.style("visibility", "hidden");});
您
div
在 SVG 中有元素(用作工具提示)。我敢肯定,这可能是匆忙完成的。这里:我把它移到了 SVG 之外:var tooltip = d3.select(".two") .append("div")
不建议在设置轴域时使用静态数字。您拥有数据并且知道要从中获取的
x
和y
点,因此只需使用d3.extentx0 = d3.extent(points, function (d) { return d[0]; }), y0 = d3.extent(points, function (d) { return d[1]; })
小改动:
domain().nice()
在设置域时使用。当你有负值时它真的很有用。这是文档我没有只使用您提供的 2 个点,而是生成了一些随机点并将其用作数据:这也解决了缩放问题(我没有更改任何内容,因为您让它完美运行)
function generateRandomPoints () { var points = []; for(var i=0; i<10; i++) { points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]); } return points; }
这是一个将以上所有内容组合在一起的片段:
var data = [
{
"points": generateRandomPoints()
}
];
function generateRandomPoints () {
var points = [];
for(var i=0; i<10; i++) {
points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]);
}
return points;
}
var points = data[0].points;
var svg = d3.select("#cluster"),
width = +svg.attr("width"),
height = +svg.attr("height");
var k = height / width,
x0 = d3.extent(points, function (d) { return d[0]; }),
y0 = d3.extent(points, function (d) { return d[1]; }),
x = d3.scaleLinear().domain(x0).nice().range([0, width]),
y = d3.scaleLinear().domain(y0).nice().range([height, 0]),
z = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisTop(x).ticks(12),
yAxis = d3.axisRight(y).ticks(12 * height / width);
var brush = d3.brush().on("end", brushended),
idleTimeout,
idleDelay = 350;
var tooltip = d3.select(".two")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.style("background", "#ffffff")
.text("a simple tooltip");
svg.append("g")
.attr("class", "brush")
.call(brush);
svg.selectAll("circle")
.data(points)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 4)
.attr("fill", function(d) { return z(d[2]); })
.on("mouseover", function(d){
tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); })
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
svg.selectAll(".domain")
.style("display", "none");
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
x.domain(x0).nice();
y.domain(y0).nice();
} else {
x.domain([s[0][0], s[1][0]].map(x.invert, x));
y.domain([s[1][1], s[0][1]].map(y.invert, y));
svg.select(".brush").call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
svg.select(".axis--x").transition(t).call(xAxis);
svg.select(".axis--y").transition(t).call(yAxis);
svg.selectAll("circle").transition(t)
.attr("cx", function(d) { return x(d[0]); })
.attr("cy", function(d) { return y(d[1]); });
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="one">
<div class="two">
<svg id="cluster" width="700" height="500"></svg>
</div>
</div>
希望这可以帮助。
推荐阅读
- php - 在symfony4.1中使用mongodb
- django - Django2.0.7 TypeError:没有主键值的模型实例是不可散列的
- java - 如何通过类共享 SQL 连接对象?
- python - 使用 subprocess.call 后如何打印变量?
- arrays - 给定一个单词,我需要获取该单词的所有空格变体
- regex - replace CamelCase function argument with SNAKE_CASE
- apache-kafka - kafka + 从主题日志文件中读取
- php - 如何在 MySQL 的消息表中选择用户并按日期排序?
- c# - Kendo UI 和 ASP.NET MVC:服务器端过滤
- scala - 无法在 Scala HTTP Akka 中测试 POST 请求