2016-01-19 17 views
9

Tôi đã tạo biểu đồ đường nhiều hàng loạt bằng cách sử dụng this bl.ocks.org code example. Tôi đã quản lý để tạo lại nó trên JSFiddle.Biểu đồ đường kẻ Multiseries với chú giải công cụ di chuột

Multi-series line chart

Bây giờ, tôi đang cố gắng để thêm một tooltip mouseover x có giá trị, mà hiển thị một tooltip ở mỗi dòng khi bạn di chuột vị trí thẳng đứng của nó. Một cái gì đó như this, nhưng đối với nhiều dòng.

Tôi đã tìm thấy this StackOverflow answer (bao gồm JSFiddle), nhưng tôi dường như không làm cho nó hoạt động với biểu đồ đường kẻ đa dòng của tôi.

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

Vì vậy, để đặt nó chỉ đơn giản, tôi muốn kết hợp của tôi line chart JSFiddle với tooltip JSFiddle này. Có ai biết làm thế nào để làm điều này? Hoặc có cách nào dễ dàng hơn để tạo chú giải công cụ như thế này không? Bất kỳ trợ giúp được đánh giá cao!

+0

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

Trả lời

29

Câu hỏi mà bạn đã tham chiếu tôi đã trả lời vào tháng 4. Kể từ đó, tôi đã tìm hiểu thêm một chút về SVGd3, vì vậy tôi sẽ để điều này làm cập nhật cho câu trả lời đó.

Lưu ý, tôi đã mượn một chút mã từ mẫu mã tuyệt vời @ Duopixel here.

Dưới đây là các nội nhận xét:

// 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 +")"; 
     }); 
    }); 

mã làm việc đầy đủ:

<!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

Ở đây bạn đang sử dụng thời gian như trục x bạn đang sử dụng 'd3.time.scale()' những gì tôi f trục x nằm trong tên tháng như jan, feb, ..... @Mark – Joyson

+0

Tôi đã thử mã của bạn [thử của tôi với dữ liệu tương tự của tôi] [https://jsfiddle.net/jasonantho/r9xrLnpp/1 /) bạn có thể vui lòng kiểm tra và trợ giúp về những gì tôi đã làm sai @Mark – Joyson

+0

@Mark bạn có thể xem xét vấn đề này https://stackoverflow.com/questions/45017352/d3-multi-line-chart-mousemove-issue – Praveen

Các vấn đề liên quan