d3.js v4, how do I have a line follow the mouse on hover, but also have a circle follow the path?

心已入冬 提交于 2019-12-08 05:22:32

问题


here is my js fiddle: https://jsfiddle.net/DerNalia/3wzLv9yg/1/

I've been trying to interpret the code from here: Multiseries line chart with mouseover tooltip, but I just can't seem to get it working.

This is what I have so far -- it's pretty much a copy paste.

// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
  .attr("class", "mouse-over-effects");

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

// keep a reference to all our lines
var lines = document.getElementsByClassName('line');

// here's a g for each circle and text on the line
var mousePerLine = mouseG.selectAll('.mouse-per-line')
  .data(data)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line");

// the circle
mousePerLine.append("circle")
  .attr("r", 7)
  .style("stroke", function(d) {
    return 'red';
  })
  .style("fill", "none")
  .style("stroke-width", "1px")
  .style("opacity", "0");

// the text
mousePerLine.append("text")
  .attr("transform", "translate(10,3)");

// rect to capture mouse movements
mouseG.append('svg:rect')
  .attr('width', width)
  .attr('height', height)
  .attr('fill', 'none')
  .attr('pointer-events', 'all')
  .on('mouseout', function() { // on mouse out hide line, circles and text
    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() { // on mouse in show line, circles and text
    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() { // mouse moving over canvas
    var mouse = d3.mouse(this);

    // move the vertical line
    d3.select(".mouse-line")
      .attr("d", function() {
        var d = "M" + mouse[0] + "," + height;
        d += " " + mouse[0] + "," + 0;
        return d;
      });

    // position the circle and text
    d3.selectAll(".mouse-per-line")
      .attr("transform", function(d, i) {

        console.log(width/mouse[0])
        console.log(mouse[1]);
        var xDate = x.invert(mouse[0]),
            bisect = d3.bisector(function(d) { return d.x; }).right;
            idx = bisect(d.values, xDate);

        // since we are use curve fitting we can't relay on finding the points like I had done in my last answer
        // this conducts a search using some SVG path functions
        // to find the correct position on the line
        // from http://bl.ocks.org/duopixel/3824661
        var beginning = 0,
            end = lines[i].getTotalLength(),
            target = null;

        while (true){
          target = Math.floor((beginning + end) / 2);
          pos = lines[i].getPointAtLength(target);
          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
        }

        // update the text with y value
        //d3.select(this).select('text')
        //  .text(y.invert(pos.y).toFixed(2));

              d3.select(this).select('circle')
              .attr('cy', pos.x)
              .attr('cx', pos.y);

        // return position
        return "translate(" + mouse[0] + "," + pos.y +")";
      });
  });

In case something goes wrong with the fiddle, here is what I have currently:

And here is how I would like it to appear (pardon horrible paint skills):

My issue could be related to my error as well. Cannot read property 'length' of undefined.


回答1:


Updated Fiddle: https://jsfiddle.net/3wzLv9yg/2/. There are a few things that are going awry:

Line Circles

var mousePerLine = mouseG.selectAll('.mouse-per-line')
  .data(data)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line");

This statement adds a new g element for every data point, rather than for every line. Replace it with an array with the length of the number of lines to get an element for each line. For example replace .data(data) with .data(d3.range(lines.length)).

Multiple Techniques of Circle Location Position

It looks like you've combined two different techniques for calculating the y location of your circles, one involving calculating from data values, and the other calculating from svg elements.

The code that calculates from data values has these lines:

var xDate = x.invert(mouse[0]),
    bisect = d3.bisector(function(d) { return d.x; }).right;
    idx = bisect(d.values, xDate);

There's an error in bisect(d.values, xDate); as d.values is not assigned anywhere. It should be bisect(data, xDate);, but it may be irrelevant as it isn't used anywhere else, since the rest of the function calculates y position from the svg paths. You can get rid of bisect and idx if you're using this approach:

var xDate = x.invert(mouse[0]);

Setting Location

This should alleviate console errors, but the mouse circles still do not track properly. That's because the location of the circle are set twice:

d3.select(this).select('circle')
  .attr('cy', pos.x)
  .attr('cx', pos.y);

// return position
return "translate(" + mouse[0] + "," + pos.y +")";

These statements sets of the g element to the correct location, but it also sets the circle to an offset of an equal amount. You only need one. Since the current implementation sets the transform of the g element, it's probably easier to keep that one and get rid of the circle offset.

// return position
return "translate(" + mouse[0] + "," + pos.y +")";

All the changes are here: https://jsfiddle.net/3wzLv9yg/2/



来源:https://stackoverflow.com/questions/38513688/d3-js-v4-how-do-i-have-a-line-follow-the-mouse-on-hover-but-also-have-a-circle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!