2016-01-19 18 views
9

this bl.ocks.org code exampleを使用して複数系列の折れ線グラフを作成しました。私はJSFiddleにそれを作り直しました。マウスオーバーツールヒントを含むマルチグラフの折れ線グラフ

Multi-series line chart

さて、私はあなたがその垂直位置を置くと、各ラインでツールチップを表示するx値のマウスオーバーツールチップを追加しようとしています。 thisのようなものですが、複数の行があります。

私は(それには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; 
} 

だから、単純にそれを置くために、私は私のline chart JSFiddleこのtooltip JSFiddleと組み合わせたいです。誰もこれを行う方法を知っていますか?または、このようなツールチップを作成する簡単な方法はありますか?どんな助けもありがとう!

+0

http://bl.ocks.org/Matthew-Weber/を参照してくださいが、 5645518 –

答えて

29

あなたが参照した質問私は4月に答えました。それ以来、私はSVGd3についてもう少し学んだので、私はこれをその答えへの更新として役立てます。

注:@ Duopixelの優れたコードサンプル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()'を使っている時間をx軸として使用していますx軸はjan、feb、.....のような月の名前です@マーク – Joyson

+0

私はあなたのコードを試しました[私の同様のデータを使って試してみました](https://jsfiddle.net/jasonantho/r9xrLnpp/1 /)私が間違ったことを確認して助けてください。@マーク – Joyson

+0

@マークはこの問題を見てください。https://stackoverflow.com/questions/45017352/d3-multi-line-chart-mousemove-issue – Praveen

関連する問題