2016-01-19 9 views
9

Ich habe eine Multi-Serie Liniendiagramm mit this bl.ocks.org code example erstellt. Ich habe es geschafft, es auf JSFiddle neu zu erstellen. JetztMultiseries Liniendiagramm mit Mouseover Tooltip

Multi-series line chart

, ich versuche, einen x-Wert Mouseover Tooltip hinzuzufügen, die in jeder Zeile die ein Tooltip angezeigt, wenn Sie seine vertikale Position schweben. So etwas wie this, aber für mehrere Zeilen.

Ich fand this StackOverflow answer (es enthält eine JSFiddle), aber ich kann nicht scheinen, damit es mit meiner Multiserien Liniendiagramm funktioniert.

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

var mouseCircle = causation.append("g") // for each line, add group to hold text and circle 
     .attr("class","mouseCircle"); 

mouseCircle.append("circle") // add a circle to follow along path 
    .attr("r", 7) 
    .style("stroke", function(d) { console.log(d); return color(d.key); }) 
    .style("fill","none") 
    .style("stroke-width", "1px"); 

mouseCircle.append("text") 
    .attr("transform", "translate(10,3)"); // text to hold coordinates 

var bisect = d3.bisector(function(d) { return d.YEAR; }).right; // reusable bisect to find points before/after line 

svg.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(".mouseLine") 
      .style("opacity", "0"); 
     d3.selectAll(".mouseCircle circle") 
      .style("opacity", "0"); 
     d3.selectAll(".mouseCircle text") 
      .style("opacity", "0"); 
    }) 
    .on('mouseover', function(){ // on mouse in show line, circles and text 
     d3.select(".mouseLine") 
      .style("opacity", "1"); 
     d3.selectAll(".mouseCircle circle") 
      .style("opacity", "1"); 
     d3.selectAll(".mouseCircle text") 
      .style("opacity", "1"); 
    }) 
    .on('mousemove', function() { // mouse moving over canvas 
     d3.select(".mouseLine") 
     .attr("d", function(){ 
      yRange = y.range(); // range of y axis 
      var xCoor = d3.mouse(this)[0]; // mouse position in x 
      var xDate = x.invert(xCoor); // date corresponding to mouse x 
      d3.selectAll('.mouseCircle') // for each circle group 
       .each(function(d,i){ 
       var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse 
       var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line 
         yRange[0], 
         xCoor, 
         yRange[1], 
         x(data[i].values[rightIdx-1].YEAR), 
         y(data[i].values[rightIdx-1].VALUE), 
         x(data[i].values[rightIdx].YEAR), 
         y(data[i].values[rightIdx].VALUE)); 

       d3.select(this) // move the circle to intersection 
        .attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')'); 

       d3.select(this.children[1]) // write coordinates out 
        .text(xDate.toLocaleDateString() + "," + y.invert(interSect.y).toFixed(0)); 

       }); 

      return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line 
     }); 
    }); 

// from here: https://stackoverflow.com/a/1968345/16363 
function get_line_intersection(p0_x, p0_y, p1_x, p1_y, 
    p2_x, p2_y, p3_x, p3_y) 
{ 
    var rV = {}; 
    var s1_x, s1_y, s2_x, s2_y; 
    s1_x = p1_x - p0_x;  s1_y = p1_y - p0_y; 
    s2_x = p3_x - p2_x;  s2_y = p3_y - p2_y; 

    var s, t; 
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y))/(-s2_x * s1_y + s1_x * s2_y); 
    t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x))/(-s2_x * s1_y + s1_x * s2_y); 

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) 
    { 
     // Collision detected 
     rV.x = p0_x + (t * s1_x); 
     rV.y = p0_y + (t * s1_y); 
    } 

    return rV; 
} 

Also, um es einfach auszudrücken, ich möchte meine line chart JSFiddle mit diesem tooltip JSFiddle kombinieren. Weiß jemand, wie man das macht? Oder gibt es eine einfachere Möglichkeit, einen Tooltip wie diesen zu erstellen? Jede Hilfe wird geschätzt!

+0

Siehe http://bl.ocks.org/Matthew-Weber/ 5645518 –

Antwort

29

Die Frage, auf die Sie verwiesen haben, antwortete ich im April. Seitdem habe ich ein wenig mehr über SVG und d3 gelernt, also werde ich dies als ein Update für diese Antwort dienen lassen.

Hinweis, ich lieh ein wenig Code aus @ Duopixels ausgezeichneten Codebeispiel here.

Hier ist die kommentierte Beschreibung:

// 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(cities) 
    .enter() 
    .append("g") 
    .attr("class", "mouse-per-line"); 

// the circle 
mousePerLine.append("circle") 
    .attr("r", 7) 
    .style("stroke", function(d) { 
    return color(d.name); 
    }) 
    .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]) 
     var xDate = x.invert(mouse[0]), 
      bisect = d3.bisector(function(d) { return d.date; }).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)); 

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

Vollarbeitscode:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <script data-require="[email protected]" 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; 
 
    } 
 
    
 
    .x.axis path { 
 
     display: none; 
 
    } 
 
    
 
    .line { 
 
     fill: none; 
 
     stroke: steelblue; 
 
     stroke-width: 1.5px; 
 
    } 
 
    </style> 
 
</head> 
 

 
<body> 
 
    <script> 
 
    var myData = "date \t New York \t San Francisco \t Austin\n\ 
 
20111001 \t 63.4 \t 62.7 \t 72.2\n\ 
 
20111002 \t 58.0 \t 59.9 \t 67.7\n\ 
 
20111003 \t 53.3 \t 59.1 \t 69.4\n\ 
 
20111004 \t 55.7 \t 58.8 \t 68.0\n\ 
 
20111005 \t 64.2 \t 58.7 \t 72.4\n\ 
 
20111006 \t 58.8 \t 57.0 \t 77.0\n\ 
 
20111007 \t 57.9 \t 56.7 \t 82.3\n\ 
 
20111008 \t 61.8 \t 56.8 \t 78.9\n\ 
 
20111009 \t 69.3 \t 56.7 \t 68.8\n\ 
 
20111010 \t 71.2 \t 60.1 \t 68.7\n\ 
 
20111011 \t 68.7 \t 61.1 \t 70.3\n\ 
 
20111012 \t 61.8 \t 61.5 \t 75.3\n\ 
 
20111013 \t 63.0 \t 64.3 \t 76.6\n\ 
 
20111014 \t 66.9 \t 67.1 \t 66.6\n\ 
 
20111015 \t 61.7 \t 64.6 \t 68.0\n\ 
 
20111016 \t 61.8 \t 61.6 \t 70.6\n\ 
 
20111017 \t 62.8 \t 61.1 \t 71.1\n\ 
 
20111018 \t 60.8 \t 59.2 \t 70.0\n\ 
 
20111019 \t 62.1 \t 58.9 \t 61.6\n\ 
 
20111020 \t 65.1 \t 57.2 \t 57.4\n\ 
 
20111021 \t 55.6 \t 56.4 \t 64.3\n\ 
 
20111022 \t 54.4 \t 60.7 \t 72.4\n"; 
 

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

 
    var parseDate = d3.time.format("%Y%m%d").parse; 
 

 
    var x = d3.time.scale() 
 
     .range([0, width]); 
 

 
    var y = d3.scale.linear() 
 
     .range([height, 0]); 
 

 
    var color = d3.scale.category10(); 
 

 
    var xAxis = d3.svg.axis() 
 
     .scale(x) 
 
     .orient("bottom"); 
 

 
    var yAxis = d3.svg.axis() 
 
     .scale(y) 
 
     .orient("left"); 
 

 
    var line = d3.svg.line() 
 
     .interpolate("basis") 
 
     .x(function(d) { 
 
     return x(d.date); 
 
     }) 
 
     .y(function(d) { 
 
     return y(d.temperature); 
 
     }); 
 

 
    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); 
 

 
    color.domain(d3.keys(data[0]).filter(function(key) { 
 
     return key !== "date"; 
 
    })); 
 

 
    data.forEach(function(d) { 
 
     d.date = parseDate(d.date); 
 
    }); 
 

 
    var cities = color.domain().map(function(name) { 
 
     return { 
 
     name: name, 
 
     values: data.map(function(d) { 
 
      return { 
 
      date: d.date, 
 
      temperature: +d[name] 
 
      }; 
 
     }) 
 
     }; 
 
    }); 
 

 
    x.domain(d3.extent(data, function(d) { 
 
     return d.date; 
 
    })); 
 

 
    y.domain([ 
 
     d3.min(cities, function(c) { 
 
     return d3.min(c.values, function(v) { 
 
      return v.temperature; 
 
     }); 
 
     }), 
 
     d3.max(cities, function(c) { 
 
     return d3.max(c.values, function(v) { 
 
      return v.temperature; 
 
     }); 
 
     }) 
 
    ]); 
 

 
    var legend = svg.selectAll('g') 
 
     .data(cities) 
 
     .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); 
 

 
    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("Temperature (ºF)"); 
 

 
    var city = svg.selectAll(".city") 
 
     .data(cities) 
 
     .enter().append("g") 
 
     .attr("class", "city"); 
 

 
    city.append("path") 
 
     .attr("class", "line") 
 
     .attr("d", function(d) { 
 
     return line(d.values); 
 
     }) 
 
     .style("stroke", function(d) { 
 
     return color(d.name); 
 
     }); 
 

 
    city.append("text") 
 
     .datum(function(d) { 
 
     return { 
 
      name: d.name, 
 
      value: d.values[d.values.length - 1] 
 
     }; 
 
     }) 
 
     .attr("transform", function(d) { 
 
     return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; 
 
     }) 
 
     .attr("x", 3) 
 
     .attr("dy", ".35em") 
 
     .text(function(d) { 
 
     return d.name; 
 
     }); 
 

 
    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(cities) 
 
     .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('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.date; }).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> 
 

 
</html>

+0

Hier verwenden Sie die Zeit als x-Achse verwenden Sie 'd3.time.scale()' was ich f die X-Achse ist im Monatsnamen wie jan, feb, ..... @Mark – Joyson

+0

Ich habe Ihren Code versucht [mein Versuch mit meinem ähnlichen Array von Daten] (https://jsfiddle.net/jasonantho/r9xrLnpp/1 /) Können Sie bitte überprüfen und helfen, was ich falsch gemacht habe @Mark – Joyson

+0

@Mark könnten Sie bitte dieses Problem https://stackoverflow.com/questions/45017352/d3-multi-line-chart-mousemove-issue betrachten – Praveen