在鼠标悬停和鼠标移动时Tooltip的提示无法在D3 js图表中按预期工作

时间:2019-09-11 05:39:40

标签: javascript d3.js

在我的D3图表中,添加此功能后,我实现了“放大”和“缩小”功能。我在使用工具提示时遇到了一些问题,它没有按预期显示该点/圆的悬停数据。 (在添加放大后感觉newX和newY没有正确更新)

没有放大缩小的工具提示的工作代码here

添加了放大缩小代码here

.on("mousemove", function() {
        var mouse = d3.mouse(this);
        var mouseDate = xScale.invert(mouse[0]);
        var i = bisectDate(data, mouseDate); // returns the index to the current data item

        var d0 = data[i - 1];
        var d1 = data[i];
        let d;
        // work out which date value is closest to the mouse
        if (typeof d1 !== "undefined") {
          d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
        } else {
          d = d0;
        }

        div
          .html(
            `<span>${parseDate(d.startTime)}</span>
                         <span>Magnitude: ${d.magnitude} </span>`
          )
          .style("left", d3.event.pageX + "px")
          .style("top", d3.event.pageY - 28 + "px");
        var x = xScale(d.startTime);
        var y = yScale(d.magnitude);

        focus
          .select("#focusCircle")
          .attr("cx", x)
          .attr("cy", y);
        focus
          .select("#focusLineX")
          .attr("x1", x)
          .attr("y1", yScale(yDomain[0]))
          .attr("x2", x)
          .attr("y2", yScale(yDomain[1]));
        focus
          .select("#focusLineY")
          .attr("x1", xScale(xDomain[0]))
          .attr("y1", y)
          .attr("x2", xScale(xDomain[1]))
          .attr("y2", y);
      });

我无法区分出问题的原因。请帮我。

1 个答案:

答案 0 :(得分:1)

我很好奇您的代码,但我从未见过这样的tip函数构建,错了,但是可以正常工作

查看没有缩放版本的

如果您更改

g.selectAll("dot")
  .data(data)
  .enter()
  .append("rect")
  .attr("class", "overlay")
  .attr("width", width)
  .attr("height", height)
  .on("mouseover", function(d) {....

svg
  .attr("class", "overlay")
  .attr("width", width)
  .attr("height", height)
  .on("mouseover", function(d) {....

它将产生相同的问题

  

您制作了一个浪费的元素,看到没有缩放的元素

g.selectAll("dot")
  .data(data)
  .enter()
  .append("rect")
  .attr("class", "overlay")
  .attr("width", width)
  .attr("height", height)
  .on("mouseover", function(d) {....

您不应该这样做,为什么selectAll ("dot")append ('rect'),请阅读更多的d3.js文档,以及为什么要这样做,所以您要为所有矩形元素构建每个要覆盖的数据并保持隐藏?为了什么?您甚至都没有使用附加到其上的数据,为什么不将其设为一个rect元素

g.append("rect")
  .attr("class", "overlay")
  .attr("width", width)
  .attr("height", height)
  .on("mouseover", function(d) {....
  .on("mousemove", function(d) {....

现在,当您的鼠标指向该svg时,您的指针将指向哪个元素?到svg层的底部还是第一个?指针未指向该元素,并且您放置了诸如mousemove之类的事件,那么您的鼠标指针将永远不会触摸该元素,它将无法正常工作,我相信您知道原因,这就是为什么此代码将鼠标事件和zoom函数放置到svg上,但是它会产生一些您现在面临的问题,只需对这两个代码进行一点调整即可完成

var data = [
      {
        startTime: "1567765320049",
        magnitude: 0,
        startupMagnitude: 0,
        startupRunningStatus: "IN_SYNC"
      },
      {
        startTime: "1567851720049",
        magnitude: 0,
        startupMagnitude: 0,
        startupRunningStatus: "IN_SYNC"
      },
      {
        startTime: "1568024520049",
        magnitude: 10,
        startupMagnitude: 10,
        startupRunningStatus: "IN_SYNC"
      },
      {
        startTime: "1568283720049",
        magnitude: 10,
        startupMagnitude: 0,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1568629320049",
        magnitude: 0,
        startupMagnitude: 10,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1569061320049",
        magnitude: 0,
        startupMagnitude: 0,
        startupRunningStatus: "IN_SYNC"
      },
      {
        startTime: "1569579720049",
        magnitude: -20,
        startupMagnitude: 0,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1570184520049",
        magnitude: -20,
        startupMagnitude: -10,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1570875720049",
        magnitude: 0,
        startupMagnitude: 0,
        startupRunningStatus: "IN_SYNC"
      },
      {
        startTime: "1571653320049",
        magnitude: 10,
        startupMagnitude: -0,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1572517320049",
        magnitude: 0,
        startupMagnitude: -10,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1573467720049",
        magnitude: 0,
        startupMagnitude: -10,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1574504520049",
        magnitude: 10,
        startupMagnitude: -10,
        startupRunningStatus: "OUT_OF_SYNC"
      },
      {
        startTime: "1575627720049",
        magnitude: 10,
        startupMagnitude: -10,
        startupRunningStatus: "OUT_OF_SYNC"
      }
    ];
    
      
  var drawLineGraph = function(containerHeight, containerWidth, data, yLabel, warnLine) {
    // A function that updates the chart when the user zoom and thus new boundaries are available
    var newX = '' 
    const updateChart = () => {
      d3.select("#focusCircle").style('display', 'none')
      // recover the new scale
      newX = d3.event.transform.rescaleX(xScale);
      var newY = yScale;


      // update axes with these new boundaries
      d3.select("#axisX").call(d3.axisBottom(newX));
      //d3.select('#axisY').call(d3.axisLeft(newY));

      // update circle position
      scatter
        .selectAll("circle")
        .attr("cx", function(d) {
          if (d) {
            return newX(d.startTime);
          }
        })
        .attr("cy", function(d) {
          if (d) {
            return newY(d.magnitude);
          }
        });

      var line2 = d3
        .line()
        .x(function(d) {
          return newX(d.startTime);
        })
        .y(function(d) {
          return newY(d.startupMagnitude);
        });

      var area = d3
        .area()
        .x(function(d) {
          return newX(d.startTime);
        })
        .y0(function(d) {
          return yScale(d.startupMagnitude);
        })
        .y1(height);

      var line = d3
        .line()
        .x(function(d) {
          return newX(d.startTime);
        })
        .y(function(d) {
          return newY(d.magnitude);
        });

        g.select('#firstLayer').on("mousemove", function() {
        d3.select("#focusCircle").style('display', 'block')
         focus.style('display', 'none')
          var mouse = d3.mouse(this);
          var mouseDate = newX.invert(mouse[0]);
          var i = bisectDate(data, mouseDate); // returns the index to the current data item
  
          var d0 = data[i - 1];
          var d1 = data[i];
          let d;
          // work out which date value is closest to the mouse
          if ( typeof d1 !== "undefined" && typeof d0 !== "undefined"){
                    focus.style('display', 'block')
          div.style('display', 'block')
            if (typeof d1 !== "undefined" ) {
                d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
            } else {
              d = d0;
            }
            
                      div
            .html(
              `<span>${parseDate(d.startTime)}</span>
          <span>Magnitude: ${d.magnitude} </span>`
            )
            .style("left", d3.event.pageX + "px")
            .style("top", d3.event.pageY - 28 + "px");
          var x = newX(d.startTime);
          var y = yScale(d.magnitude);
  
          focus
            .select("#focusCircle")
            .attr("cx", x)
            .attr("cy", y);
          focus
            .select("#focusLineX")
            .attr("x1", x)
            .attr("y1", yScale(yDomain[0]))
            .attr("x2", x)
            .attr("y2", yScale(yDomain[1]));
          focus
            .select("#focusLineY")
            .attr("x1", xScale(xDomain[0]))
            .attr("y1", y)
            .attr("x2", xScale(xDomain[1]))
            .attr("y2", y);
          } else {
          d3.select("#focusCircle").style('display', 'none')
          focus.style('display', 'none')
          div.style('display', 'none')
          
          }

  

        });

      scatter.select("#line2").attr("d", line2);
      scatter.select(".line").attr("d", line);
      scatter.select("#area").attr("d", area);
    };

    var zoom = d3
      .zoom()
      .scaleExtent([0.5, 20]) // This control how much you can unzoom (x0.5) and zoom (x20)
      .extent([[0, 0], [containerWidth, containerHeight]])
      .on("zoom", updateChart);

    var svg = d3
      .select('#chart')
      .append("svg")
      .attr("width", containerWidth)
      .attr("height", containerHeight);

    var clip = svg
      .append("defs")
      .append("SVG:clipPath")
      .attr("id", "clip")
      .append("SVG:rect")
      .attr("width", containerWidth)
      .attr("height", containerHeight)
      .attr("x", 50)
      .attr("y", 0);

    // Create the scatter variable: where both the circles and the brush take place
    var scatter = svg.append("g").attr("clip-path", "url(#clip)");

    var margin = { top: 50, left: 50, right: 50, bottom: 80 };

    var height = containerHeight - margin.top - margin.bottom;
    var width = containerWidth - margin.left - margin.right;

    var xDomain = d3.extent(data, function(d) {
      return d.startTime;
    });
    var yDomain = d3.extent(data, function(d) {
      return d.magnitude;
    });

    var xScale = d3
      .scaleTime()
      .range([0, width])
      .domain(xDomain);
    var yScale = d3
      .scaleLinear()
      .range([height, 0])
      .domain(yDomain);

    var xAxis = d3.axisBottom(xScale);
    var yAxis = d3.axisLeft(yScale);

    var line = d3
      .line()
      .x(function(d) {
        return xScale(d.startTime);
      })
      .y(function(d) {
        return yScale(d.magnitude);
      });

    var line2 = d3
      .line()
      .x(function(d) {
        return xScale(d.startTime);
      })
      .y(function(d) {
        return yScale(d.startupMagnitude);
      });

    var area = d3
      .area()
      .x(function(d) {
        return xScale(d.startTime);
      })
      // .x0(function(d) {
      //   return xScale(d.startTime);
      // })
      // .x1(function(d) {
      //   return xScale(d.magnitude);
      // })
      .y0(function(d) {
        return yScale(d.startupMagnitude);
      })
      .y1(height);
    // .y0(height)
    // .y1(function(d) { return yScale(d.magnitude); });

    // var area = d3
    //   .area()
    //   .x0(function(d) {
    //     return xScale(d.startTime);
    //   })
    //   .x1(function(d) {
    //     return xScale(d.startTime);
    //   })
    //   .y0(function(d) {
    //     return yScale(d.magnitude);
    //   })
    //   .y1(function(d) {
    //     return yScale(0);
    //   });

    // var area = d3
    //   .area()
    //   .x(function(d) {
    //     return xScale(d.startTime);
    //   })
    //   .y0(function(d) {
    //     return yScale(d.magnitude);
    //   })
    //   .y1(yScale(0));

    // Define the div for the tooltip
    var div = d3
      .select("body")
      .append("div")
      .attr("class", "tooltip")
      .style("opacity", 0);

    var g = scatter
      .append("g")
      .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
      
    var g2 = svg
      .append("g")
      .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");

    g.append("path")
      .datum(data)
      .attr("class", "area")
      .attr("id", "area")
      .attr("d", area);

    g.append("g")
      .attr("class", "x axis")
      .attr("id", "axisX")
      .attr("transform", "translate(0, " + height + ")")
      .call(xAxis);

    g2.append("g")
      .attr("class", "y axis")
      .attr("id", "axisY")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .attr("text-anchor", "end")
      .text(yLabel);

    g.append("path")
      .datum(data)
      .attr("class", "line")

      .attr("d", line);

    g.append("path")
      .datum(data)
      .attr("class", "line2")
      .attr("id", "line2")
      .attr("d", line2);

    g.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return xScale(d.startTime);
      })
      .attr("cy", function(d) {
        return yScale(d.magnitude);
      })
      .attr("r", function(d) {
        if (d.startupRunningStatus === "OUT_OF_SYNC") {
          return 5;
        }
      })
      .attr("class", "circle");

    //legend code
    svg
      .append("circle")
      .attr("cx", 40)
      .attr("cy", 380)
      .attr("r", 6)
      .style("fill", "#1391d8");
    svg
      .append("circle")
      .attr("cx", 40)
      .attr("cy", 400)
      .attr("r", 6)
      .style("fill", "red");
    svg
      .append("text")
      .attr("x", 60)
      .attr("y", 380)
      .text("Startup Config")
      .style("font-size", "15px")
      .attr("alignment-baseline", "middle");
    svg
      .append("text")
      .attr("x", 60)
      .attr("y", 400)
      .text("Running Config")
      .style("font-size", "15px")
      .attr("alignment-baseline", "middle");

    // focus tracking
    var focus = g.append("g").style("display", "none");

    focus
      .append("line")
      .attr("id", "focusLineX")
      .attr("class", "focusLine");
    focus
      .append("line")
      .attr("id", "focusLineY")
      .attr("class", "focusLine");
    focus
      .append("circle")
      .attr("id", "focusCircle")
      .attr("r", 5)
      .attr("class", "circle focusCircle");

    //grid line
    const make_x_axis = () => {
      return d3.axisBottom(xScale);
    };

    scatter
      .append("g")
      .attr("class", "grid")
      .attr("transform", "translate(50," + (height + 50) + ")")
      .call(
        make_x_axis()
          .tickSize(-height, 0, 0)
          .tickFormat("")
      );

    var bisectDate = d3.bisector(function(d) {
      return d.startTime;
    }).left;
    var parseDate = d3.timeFormat("%Y-%m-%d %H:%M:%S");
    g
      .append("rect")
      .attr("class", "overlay")
      .attr("id","firstLayer")
      .attr("width", width)
      .attr("height", height)

      
      .on("mouseover", function(d) {
        console.log('ok')
        focus.style("display", null);
        div
          .transition()
          .duration(200)
          .style("opacity", 0.9);
      })
      .on("mouseout", function() {
        focus.style("display", "none");
        div
          .transition()
          .duration(300)
          .style("opacity", 0);
      })

      .on("mousemove", function() {
        var mouse = d3.mouse(this);
        var mouseDate = xScale.invert(mouse[0]);
        var i = bisectDate(data, mouseDate); // returns the index to the current data item

        var d0 = data[i - 1];
        var d1 = data[i];
        let d;
        // work out which date value is closest to the mouse
        if (typeof d1 !== "undefined") {
          d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
        } else {
          d = d0;
        }

        div
          .html(
            `<span>${parseDate(d.startTime)}</span>
        <span>Magnitude: ${d.magnitude} </span>`
          )
          .style("left", d3.event.pageX + "px")
          .style("top", d3.event.pageY - 28 + "px");
        var x = xScale(d.startTime);
        var y = yScale(d.magnitude);

        focus
          .select("#focusCircle")
          .attr("cx", x)
          .attr("cy", y);
        focus
          .select("#focusLineX")
          .attr("x1", x)
          .attr("y1", yScale(yDomain[0]))
          .attr("x2", x)
          .attr("y2", yScale(yDomain[1]));
        focus
          .select("#focusLineY")
          .attr("x1", xScale(xDomain[0]))
          .attr("y1", y)
          .attr("x2", xScale(xDomain[1]))
          .attr("y2", y);
      })

      .call(zoom);

    // warn line

    // if (
    //   warnLine &&
    //   yDomain[0] < warnLine.lineValue &&
    //   yDomain[1] > warnLine.lineValue
    // ) {
    //   g.append("line")
    //     .attr("x1", xScale(xDomain[0]))
    //     .attr("y1", yScale(warnLine.lineValue))
    //     .attr("x2", xScale(xDomain[1]))
    //     .attr("y2", yScale(warnLine.lineValue))
    //     .attr("class", "zeroline");
    //   g.append("text")
    //     .attr("x", xScale(xDomain[1]))
    //     .attr("y", yScale(warnLine.lineValue))
    //     .attr("dy", "1em")
    //     .attr("text-anchor", "end")
    //     .text(warnLine.label)
    //     .attr("class", "zerolinetext");
    // }
  }
  
    drawLineGraph(410, 700, data, "Magnitude", {
      lineValue: 0,
      label: "Startup Config!"
    });
.axis path,
.axis line {
  fill: none;
  stroke: #e0e0e0;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: red;
  stroke-width: 2px;
}

.line2 {
  fill: none;
  stroke: #2e59cf;
  stroke-width: 0.3px;
}

.circle {
  /* fill: white;
  stroke: steelblue;
  stroke-width: 2px; */
  fill: steelblue;
  stroke: steelblue;
  /* stroke-width: 2px; */
}

.area {
  fill: #2e59cf;
  stroke: none;
  opacity: 0.1;
}

.zeroline {
  fill: none;
  stroke: #1391d8;
  stroke-width: 1px;
  stroke-dasharray: 8 8;
}

.zerolinetext {
  fill: #1391d8;
}

.overlay {
  fill: none;
  stroke: none;
  pointer-events: all;
}

.focusLine {
  fill: none;
  stroke: steelblue;
  stroke-width: 0.5px;
}

.focusCircle {
  fill: red;
}

div.tooltip {
  position: absolute;
  text-align: center;
  width: 150px;
  height: 38px;
  padding: 2px;
  font: 12px sans-serif;
  background: lightsteelblue;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
}

.grid .tick {
  stroke: lightgrey;
  stroke-width: 0.7px;
  stroke-dasharray: 8 8;
  opacity: 0.3;
}
.grid path {
  stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div className="App">
  <h1>Chart</h1>
  </div>
 <div id="chart"></div>