我创建了一个差异图表,现在我想为该图表添加一个工具提示,我创建了一条在图表上移动的线,现在我想将图表的值显示为工具提示,即x和y值,这里是(日期,打开,关闭)在鼠标移动线上的图表上,任何参考或指导将非常有帮助 .

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.area.above {
  fill:rgb(252,141,89);
}

.area.below {
  fill:rgb(145,207,96);
}

.line {
  fill: none;
  stroke: #000;
  stroke-width: 1.5px;
}

</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var parseDate = d3.timeParse("%Y-%m-%d");

var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear() .range([height, 0]);   

var xAxis = d3.axisBottom().scale(x);
var yAxis = d3.axisLeft() .scale(y).ticks(20);
//leg
//var c = d3.scaleOrdinal(d3.schemeCategory10);


var line = d3.area()
    .curve(d3.curveBasis)
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d["open"]); });

var area = d3.area()
    .curve(d3.curveBasis)
    .x(function(d) { return x(d.date); })
    .y1(function(d) { return y(d["open"]); });

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.csv("data/morley1.csv", function (error, data) {
  if (error) throw error;

data.forEach(function(d) {    
d.date = parseDate(d.date);
  d.open = +d.open;
  d.close=+d.close;
});
data.sort(function(a,b){
    return a.date-b.date
});

  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain([0, d3.max(data, function(d) {
	  return Math.max(d.close, d.open); })]);

  svg.datum(data);

  svg.append("clipPath")
      .attr("id", "clip-below")
    .append("path")
      .attr("d", area.y0(height));

  svg.append("clipPath")
      .attr("id", "clip-above")
    .append("path")
      .attr("d", area.y0(0));

  svg.append("path")
      .attr("class", "area above")
      .attr("clip-path", "url(#clip-above)")
      .attr("d", area.y0(function(d) { return y(d["close"]); }));

  svg.append("path")
      .attr("class", "area below")
      .attr("clip-path", "url(#clip-below)")
      .attr("d", area);

  svg.append("path")
      .attr("class", "line")
      .attr("d", line);
      svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
    
      svg.append("g")
.attr("class","y axis")
.call(yAxis)
});

var mouseG = svg.append("g") // this the black vertical line to folow mouse
.attr("class","mouse-over-effects");

mouseG.append("path")
.attr("class","mouse-line")
.style("stroke","black")
.style("stroke-width","1px")
.style("opacity","0");




var lines = document.getElementsByClassName("line");
var mousePerLine = mouseG.selectAll(".mouse-per-line")
//.data(data)
.enter()
.append("g")
.attr("class","mouse-per-line");                                        
mousePerLine.append("circle")
.attr("r",7)
.style("stroke",function(d){
  return color(d.name);
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform","translate(10,3)");
mouseG.append("rect")
.attr("width",width)
.attr("height",height)
.attr("fill","none")
.attr("pointer-events","all")
.on("mouseout",function(){
  d3.select(".mouse-line").style("opacity","0");
  d3.selectAll(".mouse-per-line circle").style("opacity","0");
  d3.selectAll(".mouse-per-line text").style("opacity","0")
})
.on("mouseover",function(){
  d3.select(".mouse-line").style("opacity","1");
  d3.selectAll(".mouse-per-line circle").style("opacity","1");
  d3.selectAll(".mouse-per-line text").style("opacity","1")
})
.on("mousemove",function(){
  var mouse = d3.mouse(this);
  console.log("Mouse:",mouse);
  d3.select(".mouse-line")
  .attr("d",function(){
    var d = "M" + mouse[0] +"," + height;
    d+=" " +mouse[0] + "," + 0;
    return d;
  })
  // .attr("d",function(){
  //   var d = "M" + w +"," + mouse[1];
  //   d+=" " +0 + "," + mouse[1];
  //   return d;
  // });

  d3.selectAll(".mouse-per-line")
  .attr("transform",function(d,i){
    console.log(width/(mouse[0]));
    var xDate = scaleX.invert(mouse[0]),
    bisect =d3.bisector(function(d){ return d.date;}).right;
    idx = bisect(d.values,xDate);
    console.log("xDate:",xDate);
    console.log("bisect",bisect);
    console.log("idx:",idx)
    var beginning = 0,
     end = lines[i].getTotalLength(),
    target = null;
    console.log("end",end);
    while(true){
      target = Math.floor((beginning+end)/2)
      console.log("Target:",target);
      pos = lines[i].getPointAtLength(target);
      console.log("Position",pos.y);
      console.log("What is the position here:",pos)
      if((target ===end || target == beginning) && pos.x !==mouse[0]){
        break;
      }

      if(pos.x > mouse[0]) end = target;
      else if(pos.x < mouse[0]) beginning = target;
      else break; // position found
    }
    d3.select(this).select("text")
    .text(scaleY.invert(pos.y).toFixed(1))
    .attr("fill",function(d){
      return color(d.name)
    });
    return "translate(" +mouse[0]+","+pos.y+")";

  });
});
</script>