2016-01-19 11 views
9

के साथ मल्टीसाइरीज लाइन चार्ट मैंने this bl.ocks.org code example का उपयोग करके एक बहु-श्रृंखला रेखा चार्ट बनाया है। मैंने इसे JSFiddle पर पुन: बनाने में कामयाब रहा है।माउसओवर टूलटिप

Multi-series line chart

अब, मैं एक एक्स-मूल्य माउसओवर टूलटिप, जो प्रत्येक पंक्ति के लिए जब आप अपने ऊर्ध्वाधर स्थिति मंडराना पर एक टूलटिप प्रदर्शित करता है जोड़ने की कोशिश कर रहा हूँ। this की तरह कुछ, लेकिन कई लाइनों के लिए।

मुझे this StackOverflow answer मिला (इसमें JSFiddle शामिल है), लेकिन मैं इसे अपने मल्टीज़रीज़ लाइन चार्ट के साथ काम नहीं कर सकता।

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

तो, बस इसे डाल करने के लिए, मैं अपने इस tooltip JSFiddle साथ line chart JSFiddle संयोजित करना चाहते हैं। क्या किसी को भी पता है की यह कैसे किया जाता है? या इस तरह एक टूलटिप बनाने का कोई आसान तरीका है? किसी भी मदद की सराहना की है!

+0

देखें http://bl.ocks.org/Matthew-Weber/ 5645518 –

उत्तर

29

आपके द्वारा संदर्भित प्रश्न मैंने अप्रैल में जवाब दिया था। तब से, मैंने SVG और d3 के बारे में कुछ और सीखा है, इसलिए मैं इसे उस उत्तर के अपडेट के रूप में प्रस्तुत करने दूंगा।

नोट, मैंने @ डुओपिक्सेल के उत्कृष्ट कोड नमूना here से थोड़ा सा कोड उधार लिया।

यहाँ टिप्पणी की ब्यौरे है:

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

पूर्ण काम कर कोड:

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

यहां आप एक्स अक्ष के रूप में समय का उपयोग कर रहे हैं जिसका उपयोग आप 'd3.time.scale() 'का उपयोग कर रहे हैं' एफ एक्स अक्ष महीने के नाम में है जैसे जैन, फेब, ..... @ मार्क – Joyson

+0

मैंने आपके कोड की कोशिश की है [डेटा की इसी तरह की मेष के साथ मेरी कोशिश] (https://jsfiddle.net/jasonantho/r9xrLnpp/1 /) क्या आप कृपया गलत कर सकते हैं और मैंने जो किया है उसकी सहायता कर सकते हैं @ मार्क – Joyson

+0

@ मार्क क्या आप इस मुद्दे को देख सकते हैं https://stackoverflow.com/questions/45017352/d3-multi-line-chart-mousemove-issue – Praveen