如何将鼠标悬停在 D3 中的线图交互上
How to as mouseover to Line Graph Interactive in D3
我是 D3 的新手,正在尝试制作一个简单的交互式线图。我当前的代码:http://jsfiddle.net/9xske0m1/
基于在 bl.ocks.org
上找到的一个
我正在考虑添加一条垂直线,如下所示:http://bl.ocks.org/benjchristensen/2657838 但线的值显示为悬停时的图例,而不是将它们显示在底部图表,当我将鼠标悬停在一条线上时。
所以我想知道我将如何去做。
我假设我已经在这里添加了一些东西:
data = data.map( function (d) {
return {
CAUSES: d.CAUSES,
YEAR: parseDate(d.YEAR.toString()),
VALUE: +d.VALUE };
});
// then we need to nest the data on CAUSES since we want to only draw one
// line per CAUSES
data = d3.nest().key(function(d) { return d.CAUSES; }).entries(data);
x.domain([d3.min(data, function(d) { return d3.min(d.values, function (d) { return d.YEAR; }); }),
d3.max(data, function(d) { return d3.max(d.values, function (d) { return d.YEAR; }); })]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.values, function (d) { return d.VALUE; }); })]);
// var path1 = svg.append("g").append("path").data([data1]).attr("class", "line1");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var causation = svg.selectAll(".CAUSES")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "CAUSES");
causation.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.key); });
编辑
对于通过搜索找到此答案的人,另请参阅 。这是一个非常相似的问题,实现更清晰。
我没有查看链接示例的实现,但重新创建了效果。我玩这个有点过头了,所以在我解释之前,这里是更新的 fiddle。
基本上我添加了一条垂直线来跟随鼠标。我还为每条绘制线添加了 g
的 circle
和 text
元素。当您将鼠标悬停在图上时,我将垂直线更新为鼠标位置,找出垂直线在 x 轴上的位置并确定它与每个绘制路径相交的位置。最后,我更新了圆的位置并使用内插的 x,y 位置更新了文本元素。
这是添加代码的注释说明:
svg.append("path") // this is the black vertical line to follow mouse
.attr("class","mouseLine")
.style("stroke","black")
.style("stroke-width", "1px")
.style("opacity", "0");
var mouseCircle = causation.append("g") // for each line, add group to hold text and circle
.attr("class","mouseCircle");
mouseCircle.append("circle") // add a circle to follow along path
.attr("r", 7)
.style("stroke", function(d) { console.log(d); return color(d.key); })
.style("fill","none")
.style("stroke-width", "1px");
mouseCircle.append("text")
.attr("transform", "translate(10,3)"); // text to hold coordinates
var bisect = d3.bisector(function(d) { return d.YEAR; }).right; // reusable bisect to find points before/after line
svg.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function(){ // on mouse out hide line, circles and text
d3.select(".mouseLine")
.style("opacity", "0");
d3.selectAll(".mouseCircle circle")
.style("opacity", "0");
d3.selectAll(".mouseCircle text")
.style("opacity", "0");
})
.on('mouseover', function(){ // on mouse in show line, circles and text
d3.select(".mouseLine")
.style("opacity", "1");
d3.selectAll(".mouseCircle circle")
.style("opacity", "1");
d3.selectAll(".mouseCircle text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
d3.select(".mouseLine")
.attr("d", function(){
yRange = y.range(); // range of y axis
var xCoor = d3.mouse(this)[0]; // mouse position in x
var xDate = x.invert(xCoor); // date corresponding to mouse x
d3.selectAll('.mouseCircle') // for each circle group
.each(function(d,i){
var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse
var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line
yRange[0],
xCoor,
yRange[1],
x(data[i].values[rightIdx-1].YEAR),
y(data[i].values[rightIdx-1].VALUE),
x(data[i].values[rightIdx].YEAR),
y(data[i].values[rightIdx].VALUE));
d3.select(this) // move the circle to intersection
.attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')');
d3.select(this.children[1]) // write coordinates out
.text(xDate.toLocaleDateString() + "," + y.invert(interSect.y).toFixed(0));
});
return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line
});
});
// from here:
function get_line_intersection(p0_x, p0_y, p1_x, p1_y,
p2_x, p2_y, p3_x, p3_y)
{
var rV = {};
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
var s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
// Collision detected
rV.x = p0_x + (t * s1_x);
rV.y = p0_y + (t * s1_y);
}
return rV;
}
截图:
我是 D3 的新手,正在尝试制作一个简单的交互式线图。我当前的代码:http://jsfiddle.net/9xske0m1/ 基于在 bl.ocks.org
上找到的一个我正在考虑添加一条垂直线,如下所示:http://bl.ocks.org/benjchristensen/2657838 但线的值显示为悬停时的图例,而不是将它们显示在底部图表,当我将鼠标悬停在一条线上时。 所以我想知道我将如何去做。 我假设我已经在这里添加了一些东西:
data = data.map( function (d) {
return {
CAUSES: d.CAUSES,
YEAR: parseDate(d.YEAR.toString()),
VALUE: +d.VALUE };
});
// then we need to nest the data on CAUSES since we want to only draw one
// line per CAUSES
data = d3.nest().key(function(d) { return d.CAUSES; }).entries(data);
x.domain([d3.min(data, function(d) { return d3.min(d.values, function (d) { return d.YEAR; }); }),
d3.max(data, function(d) { return d3.max(d.values, function (d) { return d.YEAR; }); })]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.values, function (d) { return d.VALUE; }); })]);
// var path1 = svg.append("g").append("path").data([data1]).attr("class", "line1");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var causation = svg.selectAll(".CAUSES")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "CAUSES");
causation.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.key); });
编辑
对于通过搜索找到此答案的人,另请参阅
我没有查看链接示例的实现,但重新创建了效果。我玩这个有点过头了,所以在我解释之前,这里是更新的 fiddle。
基本上我添加了一条垂直线来跟随鼠标。我还为每条绘制线添加了 g
的 circle
和 text
元素。当您将鼠标悬停在图上时,我将垂直线更新为鼠标位置,找出垂直线在 x 轴上的位置并确定它与每个绘制路径相交的位置。最后,我更新了圆的位置并使用内插的 x,y 位置更新了文本元素。
这是添加代码的注释说明:
svg.append("path") // this is the black vertical line to follow mouse
.attr("class","mouseLine")
.style("stroke","black")
.style("stroke-width", "1px")
.style("opacity", "0");
var mouseCircle = causation.append("g") // for each line, add group to hold text and circle
.attr("class","mouseCircle");
mouseCircle.append("circle") // add a circle to follow along path
.attr("r", 7)
.style("stroke", function(d) { console.log(d); return color(d.key); })
.style("fill","none")
.style("stroke-width", "1px");
mouseCircle.append("text")
.attr("transform", "translate(10,3)"); // text to hold coordinates
var bisect = d3.bisector(function(d) { return d.YEAR; }).right; // reusable bisect to find points before/after line
svg.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function(){ // on mouse out hide line, circles and text
d3.select(".mouseLine")
.style("opacity", "0");
d3.selectAll(".mouseCircle circle")
.style("opacity", "0");
d3.selectAll(".mouseCircle text")
.style("opacity", "0");
})
.on('mouseover', function(){ // on mouse in show line, circles and text
d3.select(".mouseLine")
.style("opacity", "1");
d3.selectAll(".mouseCircle circle")
.style("opacity", "1");
d3.selectAll(".mouseCircle text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
d3.select(".mouseLine")
.attr("d", function(){
yRange = y.range(); // range of y axis
var xCoor = d3.mouse(this)[0]; // mouse position in x
var xDate = x.invert(xCoor); // date corresponding to mouse x
d3.selectAll('.mouseCircle') // for each circle group
.each(function(d,i){
var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse
var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line
yRange[0],
xCoor,
yRange[1],
x(data[i].values[rightIdx-1].YEAR),
y(data[i].values[rightIdx-1].VALUE),
x(data[i].values[rightIdx].YEAR),
y(data[i].values[rightIdx].VALUE));
d3.select(this) // move the circle to intersection
.attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')');
d3.select(this.children[1]) // write coordinates out
.text(xDate.toLocaleDateString() + "," + y.invert(interSect.y).toFixed(0));
});
return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line
});
});
// from here:
function get_line_intersection(p0_x, p0_y, p1_x, p1_y,
p2_x, p2_y, p3_x, p3_y)
{
var rV = {};
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
var s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
// Collision detected
rV.x = p0_x + (t * s1_x);
rV.y = p0_y + (t * s1_y);
}
return rV;
}
截图: