D3.js 如何在D3中实现鼠标悬停到线图的交互

D3.js 如何在D3中实现鼠标悬停到线图的交互,d3.js,D3.js,我是D3新手,正在尝试制作一个简单的交互式线条图。我当前的代码: 基于在bl.ocks.org上找到的 我正在考虑添加一条像这里这样的垂直线:但是这些线的值在悬停时显示为图例,而不是在图表的底部显示,当我悬停在一条线上时,我会这样做。 所以我想知道我该怎么做。 我假设我已经在这里添加了一些东西: data = data.map( function (d) { return { CAUSES: d.CAUSES, YEAR: parseDate(d.YE

我是D3新手,正在尝试制作一个简单的交互式线条图。我当前的代码: 基于在bl.ocks.org上找到的

我正在考虑添加一条像这里这样的垂直线:但是这些线的值在悬停时显示为图例,而不是在图表的底部显示,当我悬停在一条线上时,我会这样做。 所以我想知道我该怎么做。 我假设我已经在这里添加了一些东西:

    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); });
编辑

对于通过搜索找到此答案的用户,请参见。这是一个非常类似的问题,它的实现更加简洁


我没有看链接示例的实现,而是重新创建了效果。我玩这个有点过头了,所以在我解释之前,这里是最新的

基本上,我添加了一条垂直线来跟随鼠标。我还为每个绘制线添加了一个
g
文本
元素。当您将鼠标悬停在绘图上时,我会将垂直线更新为鼠标位置,找出垂直线在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: https://stackoverflow.com/a/1968345/16363
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;
}
svg.append(“path”)//这是鼠标后面的黑色垂直线
.attr(“类”、“鼠线”)
.style(“笔划”、“黑色”)
.style(“笔划宽度”、“1px”)
.样式(“不透明度”、“0”);
var mouseCircle=causion.append(“g”)//对于每一行,添加组以容纳文本和圆圈
.attr(“类”、“鼠标圈”);
mouseCircle.append(“circle”)//添加一个沿路径的圆
.attr(“r”,7)
.style(“stroke”,函数(d){console.log(d);返回颜色(d.key);})
.style(“填充”、“无”)
.样式(“笔划宽度”、“1px”);
mouseCircle.append(“文本”)
.attr(“转换”、“翻译(10,3)”);//保存坐标的文本
var bisect=d3.bisector(函数(d){return d.YEAR;})。右;//可重复使用的二分法,用于查找线前/线后的点
append('svg:rect')//追加一个rect以捕捉画布上的鼠标移动
.attr('width',width)//无法捕获g元素上的鼠标事件
.attr('height',height)
.attr('填充','无')
.attr('pointer-events','all')
.on('mouseout',function(){//on mouseout隐藏线、圆和文本
d3.选择(“鼠线”)
.样式(“不透明度”、“0”);
d3.全选(“鼠标圆圈”)
.样式(“不透明度”、“0”);
d3.全选(“.mouseCill text”)
.样式(“不透明度”、“0”);
})
.on('mouseover',function(){//在鼠标上显示线、圆和文本
d3.选择(“鼠线”)
.样式(“不透明度”、“1”);
d3.全选(“鼠标圆圈”)
.样式(“不透明度”、“1”);
d3.全选(“.mouseCill text”)
.样式(“不透明度”、“1”);
})
.on('mousemove',function(){//鼠标在画布上移动
d3.选择(“鼠线”)
.attr(“d”,函数(){
yRange=y.range();//y轴的范围
var xCoor=d3.mouse(this)[0];//鼠标在x中的位置
var xDate=x.invert(xCoor);//与鼠标x对应的日期
d3.为每个圆圈组选择All('.mouseCircle')/
.每个(功能(d,i){
var rightIdx=bisect(数据[1]。值,xDate);//立即在数据中查找日期
var interSect=get_line_intersection(xCoor,//获取垂直线和数据线的交点
yRange[0],
xCoor,
yRange[1],
x(数据[i]。值[rightIdx-1]。年),
y(数据[i]。值[rightIdx-1]。值),
x(数据[i]。值[rightIdx]。年),
y(数据[i].值[rightIdx].值));
d3.选择(此)//将圆移动到交点处
.attr('transform','translate('+interSect.x+','+interSect.y+'));
d3.select(this.children[1])//写出坐标
.text(xDate.toLocaleDateString()+”,“+y.invert(interSect.y).toFixed(0));
});
返回“M”+xCoor+”、“+yRange[0]+“L”+xCoor+”、“+yRange[1];//定位垂直线
});
});
//从这里开始:https://stackoverflow.com/a/1968345/16363
函数get_line_intersection(p0_x,p0_y,p1_x,p1_y,
p2_x,p2_y,p3_x,p3_y)
{
var rV={};
变量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*s2y);
t=(s2_x*(p0_y-p2_y)-s2_y*(p0_x-p2_x))/(-s2_x*s1_y+s1_x*s2_y);

如果(s>=0&&s=0&&t)感谢您的回复!我想知道如何让它只显示年份以及鼠标悬停时CSV中的确切值?@Eddicheung,看看这个。我已经更新了它来回答您的问题。