鼠标移动和鼠标悬停时未显示工具提示

时间:2017-11-07 00:44:12

标签: javascript d3.js

我正在使用以下代码创建一个带有工具提示的多线图,并对其进行了一些定制以使x轴顺序,并且我还对数据进行了编码。除了工具提示外,其他所有工作似乎都能正常工作。

https://bl.ocks.org/larsenmtl/e3b8b7c2ca4787f77d78f58d41c3da91

当我将鼠标悬停在数据点上时,工具提示不显示,我也试图使x轴离散,这意味着我希望看到数据变量中数据的工具提示而不是x值介于两者之间。

有人可以建议吗?

以下是我的代码:

    <head>
    <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> 
      <style>
        body {
          font: 10px sans-serif;
        }
        
        .axis path,
        .axis line {
          fill: none;
          stroke: #000;
          shape-rendering: crispEdges;
        }
        
        .line {
          fill: none;
          stroke: steelblue;
          stroke-width: 1.5px;
        }
      </style>
    </head>
    
    <body>
      <script>
    
        var margin = {
            top: 20,
            right: 80,
            bottom: 30,
            left: 50
          },
          width = 900 - margin.left - margin.right,
          height = 500 - margin.top - margin.bottom;
    
    //    var parseDate = d3.time.format("%Y%m%d").parse;
    
    //    var x = d3.scale.linear()
    //      .range([0, width]);
        var x  = d3.scale.ordinal()
        .domain(["20170101","20170108","20170115","20170122","20170128"])
        .rangePoints([0, width],0.5);
    
        var y = d3.scale.linear()
          .range([height, 0]);
    
        var color = d3.scale.category10();
    
        var xAxis = d3.svg.axis()
          .scale(x)
    //      .ticks(5)
          .orient("bottom")
    //      .tickValues(["2017-01-01", "2017-01-08", "2017-01-15", "2017-01-22", "2017-01-28"]);
    
        var yAxis = d3.svg.axis()
          .scale(y)
          .orient("left");
    
        var line = d3.svg.line()
          .interpolate("linear")
          .x(function(d) {
            return x(d.week);
          })
          .y(function(d) {
            return y(d.avg_test_drives);
          });
    
        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 + ")");
    
    //    var data = d3.tsv.parse(myData);
    //      console.log(data[0]);
    //      alert (JSON.stringify(data));
          
        var data =  [{"week": "20170101", "dealer": "68", "peers": "73","all dealers":"123"},
                     {"week": "20170108", "dealer": "121","peers":"112","all dealers":"131"},
                     {"week": "20170115", "dealer": "104","peers":"101","all dealers":"106"},
                     {"week": "20170122", "dealer": "123","peers":"131","all dealers":"122"},
                     {"week": "20170128", "dealer": "106","peers":"107","all dealers":"122"}]
    //    alert (JSON.stringify(data));
    
        color.domain(d3.keys(data[0]).filter(function(key) {
          return key !== "week";
        }));
        
    
        data.forEach(function(d) {
          d.week = d.week;
        });
            
    
        var testdrives = color.domain().map(function(name) {
          return {
            name: name,
            values: data.map(function(d) {
              return {
                week: d.week,
                avg_test_drives: +d[name]
              };
            })
          };
        });
     
       
    //    x.domain(d3.extent(data, function(d) {
    //      return d.week;
    //    }));
          
         
    
        y.domain([
          d3.min(testdrives, function(c) {
            return d3.min(c.values, function(v) {
              return v.avg_test_drives;
            });
          }),
          d3.max(testdrives, function(c) {
            return d3.max(c.values, function(v) {
              return v.avg_test_drives;
            });
          })
        ]);
          
        
    
        var legend = svg.selectAll('g')
          .data(testdrives)
          .enter()
          .append('g')
          .attr('class', 'legend');
    
        legend.append('rect')
          .attr('x', width - 20)
          .attr('y', function(d, i) {
            return i * 20;
          })
          .attr('width', 10)
          .attr('height', 10)
          .style('fill', function(d) {
            return color(d.name);
          });
    
        legend.append('text')
          .attr('x', width - 8)
          .attr('y', function(d, i) {
            return (i * 20) + 9;
          })
          .text(function(d) {
            return d.name;
          });
          
        
    
        svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis)
    //      .ticks(5);
    
        svg.append("g")
          .attr("class", "y axis")
          .call(yAxis)
          .append("text")
          .attr("transform", "rotate(-90)")
          .attr("y", 6)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Test Drives");
    
        var entity = svg.selectAll(".entity")
          .data(testdrives)
          .enter().append("g")
          .attr("class", "entity");
    //    alert (JSON.stringify(entity));   
       
        entity.append("path")
          .attr("class", "line")
          .attr("d", function(d) {
            return line(d.values);
          })
          .style("stroke", function(d) {
            return color(d.name);
          });
    
        entity.append("text")
          .datum(function(d) {
            return {
              name: d.week,
              value: d.values[d.values.length - 1]
            };
          })
          .attr("transform", function(d) {
            return "translate(" + x(d.value.week) + "," + y(d.value.avg_test_drives) + ")";
          })
          .attr("x", 3)
          .attr("dy", ".35em")
          .text(function(d) {
            return d.week;
          });
          
    
    
        var mouseG = svg.append("g")
          .attr("class", "mouse-over-effects");
    
        mouseG.append("path") // this is the black vertical line to follow mouse
          .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(testdrives)
          .enter()
          .append("g")
          .attr("class", "mouse-per-line");
          
       
        mousePerLine.append("circle")
          .attr("r", 7)
          .style("stroke", function(d) {
            return color(d.week);
          })
          .style("fill", "none")
          .style("stroke-width", "1px")
          .style("opacity", "0");
    
        mousePerLine.append("text")
          .attr("transform", "translate(10,3)");
          
    
        mouseG.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(".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);
            d3.select(".mouse-line")
              .attr("d", function() {
                var d = "M" + mouse[0] + "," + height;
                d += " " + mouse[0] + "," + 0;
                return d;
            
              });
            
          
         
           d3.selectAll(".mouse-per-line")
              .attr("transform", function(d, i) {
                console.log(width/mouse[0])
                var xDate = x.invert(mouse[0]),
                    bisect = d3.bisector(function(d) { return d.week; }).right;
                    idx = bisect(d.values, xDate);
                
                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
                }
                
                d3.select(this).select('text')
                  .text(y.invert(pos.y).toFixed(2));
                  
                return "translate(" + mouse[0] + "," + pos.y +")";
              });
          });
          
      </script>
    </body>

1 个答案:

答案 0 :(得分:0)

您尝试访问invert()的{​​{1}}函数,但由于它现在不是x而是linear(),因此无法将其反转,并且函数{ ordinal()缺少{1}},因此尝试调用此函数导致错误并阻止执行下一行代码,因此只需注释掉:

invert()

x = d3.scale.ordinal()中,在这里,工具提示再次运作:

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