D3图形工具提示应显示悬停时的最近点

时间:2020-06-10 08:45:27

标签: javascript d3.js

我已经使用D3制作了一个图形,但是问题是我希望其工具提示根据图形中最近的鼠标指针出现在图形上。我还在此链接的D3: Get nearest value from ordinal axis on mouseover上看到了StackOverflow上的一个示例。这样做确实符合我的要求,但是当我在图形上实现相同的代码时,它就无法正常工作。请查看我的代码并建议我进行更改。

重要!:我也想当某人双击图形时,使工具提示的x'轴和y'轴保持一致。非常感谢您宝贵的时间。请帮忙!!!

<div id='chartdiv'></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function getData(){ 
data1 = [{x:1,y:15},{x:2,y:26},{x:3,y:17},{x:4,y:21},];
return data1};

function drawChart(data) {
var coreheight = 720
var corewidth = 1280
var margin = {top: 10, right: 30, bottom: 100, left: 60}
, width = corewidth - margin.left - margin.right
, height = coreheight - margin.top - margin.bottom;

var xExtent = d3.extent(data, function(d) { return d.x; }),
xRange = xExtent[1] - xExtent[0],
yExtent = d3.extent(data, function(d) { return d.y; }).reverse(),
yRange = yExtent[1] - yExtent[0];

var xScale = d3.scaleLinear().range([50, width]).domain([xExtent[0] - (xRange * .05), xExtent[1] + (xRange * .05)]);;
var yScale = d3.scaleLinear().range([0, height]).domain([yExtent[0] - (yRange * .1), yExtent[1] + (yRange * .05)]);;

var line = d3.line()
 .x(function(d, x) { return xScale(d.x); })
 .y(function(d, y) { return yScale(d.y); })
 .curve(d3.curveMonotoneX);

d3.select('#chartdiv')
 .append('svg')
 .attr('class','graph')
 .style('background-color','#fff')
 .attr("viewBox", "0 0 "+ corewidth +" "+ coreheight +"");

var svg = d3.select(".graph")
 .append("g")
 .attr("class", "dchart")
 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append("g")
 .attr("class", "y axis")
 .call(d3.axisLeft(yScale).ticks(7))
 .attr("transform", "translate(50, 0)")
 .attr('font-size','25px');

svg.append("g")
 .attr("class", "x axis")
 .attr("transform", "translate(0," + height + ")")
 .call(d3.axisBottom(xScale).ticks(7))
 .attr('font-size','25px');

svg.append("path")
 .datum(data)
 .attr("class", "line")
 .attr("d", line);

svg.selectAll(".dot")
 .data(data)
 .enter()
 .append("circle")
 .attr("class", "dot")
 .attr("cx", function(d) { return xScale(d.x) })
 .attr("cy", function(d) { return yScale(d.y) })
 .attr("r", 8)
 .on("mouseover", mousehover)
 .on("mouseout", mousehoverout) 

function mousehover(d) { 
   svg.append("text")
    .attr('class','annot')
    .attr('x', xScale(d.x) - 20)
    .attr('y', yScale(d.y) - 23)
    .text(d.x+','+d.y)
    .style('font-size','30px')
   svg.append('line')
    .attr('class','annot')
    .style("stroke", "black")
    .attr('stroke-width','3')
    .attr("x1", xScale(d.x))
   .attr("y1", yScale(d.y))
    .attr("x2", xScale(d.x))
    .attr("y2", height);
    }

function mousehoverout() {  
    d3.selectAll('.annot').remove()
  }

}drawChart(getData());
</script>

<style>
.line {fill: none;stroke: darkblue;stroke-width: 3}
.dot {fill: darkblue;stroke-width:0}
</style>

1 个答案:

答案 0 :(得分:1)

您需要跟踪鼠标在图表上的位置,当前仅检查鼠标是否悬停在点上。

我在整个图表中添加了几个事件处理程序:

 .on("mousemove", mousemove)
 .on("mouseout", mousehoverout);

您已经实现了一个mousehoverout,另一个已经实现了mousemove,它可以跟踪鼠标相对于点的位置。

var lastIndex = -1;

function mousemove()
{
  let x = d3.mouse(this)[0];
  let closest = data.reduce((best, value, i) => 
    {
      let absx = Math.abs(xScale(value.x) - x) 
      if(absx < best.value) 
      {
        return {index: i, value:absx};
      }
      else
      {
        return best;
      }
    }, {index:0, value:Number.MAX_SAFE_INTEGER});

  if(lastIndex != closest.index)
  {
    d3.selectAll('.annot').remove();
    lastIndex = closest.index;
  }
  mousehover(data[closest.index]);
}

然后添加双击功能,您可以按照w3schools链接中所示的方法进行操作,但是将处理程序添加到图表中:

.on('dblclick', function(){/* your code in here */});

function getData(){ 
data1 = [{x:1,y:15},{x:2,y:26},{x:3,y:17},{x:4,y:21},];
return data1};

function drawChart(data) {
var coreheight = 720
var corewidth = 1280
var margin = {top: 10, right: 30, bottom: 100, left: 60}
, width = corewidth - margin.left - margin.right
, height = coreheight - margin.top - margin.bottom;

var xExtent = d3.extent(data, function(d) { return d.x; }),
xRange = xExtent[1] - xExtent[0],
yExtent = d3.extent(data, function(d) { return d.y; }).reverse(),
yRange = yExtent[1] - yExtent[0];

var xScale = d3.scaleLinear().range([50, width]).domain([xExtent[0] - (xRange * .05), xExtent[1] + (xRange * .05)]);;
var yScale = d3.scaleLinear().range([0, height]).domain([yExtent[0] - (yRange * .1), yExtent[1] + (yRange * .05)]);;

var line = d3.line()
 .x(function(d, x) { return xScale(d.x); })
 .y(function(d, y) { return yScale(d.y); })
 .curve(d3.curveMonotoneX);

d3.select('#chartdiv')
 .append('svg')
 .attr('class','graph')
 .style('background-color','#fff')
 .attr("viewBox", "0 0 "+ corewidth +" "+ coreheight +"")
 .on('dblclick', function(){if(lastIndex > -1) { createpeak(data[lastIndex]); } })
 .on("mousemove", mousemove)
 .on("mouseout", mousehoverout);

var svg = d3.select(".graph")
 .append("g")
 .attr("class", "dchart")
 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append("g")
 .attr("class", "y axis")
 .call(d3.axisLeft(yScale).ticks(7))
 .attr("transform", "translate(50, 0)")
 .attr('font-size','25px');

svg.append("g")
 .attr("class", "x axis")
 .attr("transform", "translate(0," + height + ")")
 .call(d3.axisBottom(xScale).ticks(7))
 .attr('font-size','25px');

svg.append("path")
 .datum(data)
 .attr("class", "line")
 .attr("d", line);

svg.selectAll(".dot")
 .data(data)
 .enter()
 .append("circle")
 .attr("class", "dot")
 .attr("cx", function(d) { return xScale(d.x) })
 .attr("cy", function(d) { return yScale(d.y) })
 .attr("r", 8);

var lastIndex = -1;

function mousemove()
{
  let x = d3.mouse(this)[0];
  let closest = data.reduce((best, value, i) => 
    {
      let absx = Math.abs(xScale(value.x) - x) 
      if(absx < best.value) 
      {
        return {index: i, value:absx};
      }
      else
      {
        return best;
      }
    }, {index:0, value:Number.MAX_SAFE_INTEGER});
    
  d3.selectAll('.annot').remove();
  lastIndex = closest.index;
  mousehover(data[closest.index]);
}

function mousehover(d) { 
   svg.append("text")
    .attr('class','annot')
    .attr('x', xScale(d.x) - 20)
    .attr('y', yScale(d.y) - 23)
    .text(d.x+','+d.y)
    .style('font-size','30px')
   svg.append('line')
    .attr('class','annot')
    .style("stroke", "black")
    .attr('stroke-width','3')
    .attr("x1", xScale(d.x))
    .attr("y1", yScale(d.y))
    .attr("x2", xScale(d.x))
    .attr("y2", height);
    }

var i = 1;
var m = 1;
function createpeak(d) {
console.log('redacted');
         }

function dragstarted(d,e){
d3.select(this).raise().classed("active", true);
var current = d3.select(this);
deltaX = current.attr("x") - d3.event.x;
deltaY = current.attr("y") - d3.event.y;
}
    
function dragged(d,e){
d3.select(this).attr("x",d3.event.x + deltaX)
d3.select(this).attr("y",d3.event.y + deltaY)
for (var i=1; i <= 100; i++){
d3.select('.line'+i).attr("x2", function (d) {
return d3.select('.peak'+i).attr("x") - deltaX
});
d3.select('.line'+i).attr("y2", function (d) {
return d3.select('.peak'+i).attr("y") - deltaY
})}}

function dragended(d,e){}

function mousehoverout() {

        
  }

}drawChart(getData());
.line {fill: none;stroke: darkblue;stroke-width: 3}
.dot {fill: darkblue;stroke-width:0}
<div id='chartdiv'></div>
<script src="https://d3js.org/d3.v5.min.js"></script>