2016-07-18 15 views
1

无法显示的X轴值只有“矩形”内一次使用D3

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="utf-8"> 
 
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
 
<script data-require="[email protected]" data-semver="3.5.3" src="https://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.category20(); 
 

 
    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.selectAll('.mouse-per-line') // Rectangle 
 
     .data([cities]) 
 
     .enter() 
 
     .append("rect") 
 
     .attr("width", width) 
 
     .attr("height", 90) 
 
     .style("padding", "5px") 
 
     .style("stroke", "#272525") 
 
     .style("fill", "#272525") 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0") 
 
     .attr('x', 10) 
 
     .attr('y', -45); 
 

 
    mousePerLine.selectAll('.mouse-per-line') // Circle 
 
     .data(cities) 
 
     .enter() 
 
     .append("circle") 
 
     .attr("r", 5) 
 
     .style("stroke", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0"); 
 

 
    mousePerLine.selectAll('.mouse-per-line') // Text 
 
     .data(cities) 
 
     .enter() 
 
     .append("text") 
 
     .attr("transform", "translate(15,13)") 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("font-weight", "bold") 
 
     .style("font-size", "10pt"); 
 

 
    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 rect") 
 
      .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 rect") 
 
      .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("foo", function(d, i) { 
 
      var xDate = x.invert(mouse[0]); 
 
      var bisect; 
 
      var heights = []; 
 

 
      var xDateValue = /\w*.\s.\d.\d*.\d*.:\d*.:\d*/.exec(xDate); 
 
      // console.log(xDateValue); 
 

 
      d3.selectAll('circle') 
 
      .attr("transform", function(d, j) { 
 
       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[j].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 
 
       } 
 
       heights[j] = pos.y; 
 
       return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
      }); 
 
      var avgheight = 0; 
 
      for (var z = 0; z < heights.length; z++) { 
 
      avgheight = avgheight + heights[z]; 
 
      } 
 
      avgheight = avgheight/d.length; 
 

 
      d3.select(this).selectAll('rect') 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + avgheight + ")"; 
 
      }); 
 
      var rectangleText = ""; 
 
      for (var t = 1; t < heights.length; t++) { 
 
      rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2); 
 
      } 
 

 
      d3.select(this) 
 
      .selectAll('text').text(function(d, i) { 
 
       return xDateValue + " " + d.name + " " + y.invert(heights[i]).toFixed(2) 
 
      }).attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + (avgheight + 30 - (i * 25)) + ")"; 
 
      }).attr("dx", '20px'); 
 
      return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
     }); 
 
    }); 
 
    </script> 
 
</body> 
 

 
</html>
你好,我是新来D3库,并试图建立一个多折线图,并显示XY轴的数据内鼠标悬停的“rect”。我已经挣扎了很多,并且迄今为止已经实现了将鼠标悬停在“矩形”内的X-Y轴的数据。但是多线图中X轴的数据多次显示多轴。我希望日期值/ X轴值只能在其他数据上显示一次,而不是多次显示。请帮助我提供任何提示/建议。感谢您提前提供任何帮助。我知道我犯了一些愚蠢的错误,但我被卡住了。

+0

你好,请帮助我。我被卡住了。 – Rach

回答

1

下面是对现有代码的快速修改。它继续你的方法,并添加一个额外的text元素日期:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="utf-8"> 
 
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
 
<script data-require="[email protected]" data-semver="3.5.3" src="https://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.category20(); 
 

 
    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.selectAll('.mouse-per-line') // Rectangle 
 
     .data([cities]) 
 
     .enter() 
 
     .append("rect") 
 
     .attr("width", width) 
 
     .attr("height", 110) 
 
     .style("padding", "5px") 
 
     .style("stroke", "#272525") 
 
     .style("fill", "#272525") 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0") 
 
     .attr('x', 10) 
 
     .attr('y', -45); 
 

 
    mousePerLine.selectAll('.mouse-per-line') // Circle 
 
     .data(cities) 
 
     .enter() 
 
     .append("circle") 
 
     .attr("r", 5) 
 
     .style("stroke", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0"); 
 

 
    mousePerLine.append("text") 
 
     .attr("class","DateText"); 
 
    mousePerLine.selectAll('.mouse-per-line') // Text 
 
     .data(cities) 
 
     .enter() 
 
     .append("text") 
 
     .attr("class", "ValueText") 
 
     .attr("transform", "translate(15,13)") 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("font-weight", "bold") 
 
     .style("font-size", "10pt"); 
 

 
    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 rect") 
 
      .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 rect") 
 
      .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("foo", function(d, i) { 
 
      var xDate = x.invert(mouse[0]); 
 
      var bisect; 
 
      var heights = []; 
 

 
      var xDateValue = /\w*.\s.\d.\d*.\d*.:\d*.:\d*/.exec(xDate); 
 
      // console.log(xDateValue); 
 

 
      d3.selectAll('circle') 
 
      .attr("transform", function(d, j) { 
 
       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[j].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 
 
       } 
 
       heights[j] = pos.y; 
 
       return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
      }); 
 
      var avgheight = 0; 
 
      for (var z = 0; z < heights.length; z++) { 
 
      avgheight = avgheight + heights[z]; 
 
      } 
 
      avgheight = avgheight/d.length; 
 

 
      d3.select(this).selectAll('rect') 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + avgheight + ")"; 
 
      }); 
 
      var rectangleText = ""; 
 
      for (var t = 1; t < heights.length; t++) { 
 
      rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2); 
 
      } 
 
    
 
      d3.select(this) 
 
      .select(".DateText") 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + (avgheight - 25) + ")"; 
 
      }).attr("dx", '20px') 
 
      .text(xDateValue) 
 
      .style("fill", "white"); 
 

 
      d3.select(this) 
 
      .selectAll('.ValueText').text(function(d, i) { 
 
       return d.name + " " + y.invert(heights[i]).toFixed(2) 
 
      }).attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + (avgheight + 50 - (i * 25)) + ")"; 
 
      }).attr("dx", '20px'); 
 
      return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
     }); 
 
    }); 
 
    </script> 
 
</body> 
 

 
</html>

+0

太好了!非常感谢Mark。你救了我。我仔细检查了你的代码,附加了两个文本类并在不同的文本类中显示不同的数据是一种常识。从stackoverflow学习很多东西。 :) – Rach

0

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="utf-8"> 
 
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
 
    <script data-require="[email protected]" data-semver="3.5.3" src="https://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.category20(); 
 

 
    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.selectAll('.mouse-per-line') // Rectangle 
 
     .data([cities]) 
 
     .enter() 
 
     .append("rect") 
 
     .attr("width", width/2) 
 
     .attr("height", height/3) 
 
     .style("padding", "5px") 
 
     .style("stroke", "#272525") 
 
     .style("fill", "#272525") 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0") 
 
     .attr('x', 10) 
 
     .attr('y', -45); 
 

 
    mousePerLine.selectAll('.innerRect') 
 
     .data(cities) 
 
     .enter() 
 
     .append("rect") 
 
     .attr("width", 10) 
 
     .attr("height", 10) 
 
     .style("fill", "none") 
 
     .attr("class", "innerRect"); 
 

 
    mousePerLine.selectAll('.mouse-per-line') // Circle 
 
     .data(cities) 
 
     .enter() 
 
     .append("circle") 
 
     .attr("r", 5) 
 
     .style("stroke", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("stroke-width", "1px") 
 
     .style("opacity", "0"); 
 

 
    mousePerLine.append("text") 
 
     .attr("class", "DateText"); 
 

 
    mousePerLine.selectAll('.mouse-per-line') // Text 
 
     .data(cities) 
 
     .enter() 
 
     .append("text") 
 
     .attr("class", "ValueText") 
 
     // .attr("transform", "translate(15,13)") 
 
     .style("fill", function(d) { 
 
     return color(d.name); 
 
     }) 
 
     .style("font-weight", "bold") 
 
     .style("font-size", "10pt"); 
 

 
    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 rect") 
 
      .style("opacity", "0"); 
 
     d3.selectAll(".mouse-per-line innerRect") 
 
      .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 hover show line, circles and text 
 
     d3.select(".mouse-line") 
 
      .style("opacity", "1"); 
 
     d3.selectAll(".mouse-per-line rect") 
 
      .style("opacity", "1"); 
 
     d3.selectAll(".mouse-per-line innerRect") 
 
      .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); 
 
     console.log(mouse); 
 
     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("foo", function(d, i) { 
 
      var xDate = x.invert(mouse[0]); 
 
      var bisect; 
 
      var heights = []; 
 

 
      var xDateValue = /\w*.\s.\d.\d*.\d*.:\d*.:\d*/.exec(xDate); 
 
      // console.log(xDateValue); 
 

 
      d3.selectAll('circle') 
 
      .attr("transform", function(d, j) { 
 
       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[j].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 
 
       } 
 
       heights[j] = pos.y; 
 
       return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
      }); 
 
      var avgheight = 0; 
 
      for (var z = 0; z < heights.length; z++) { 
 
      avgheight = avgheight + heights[z]; 
 
      } 
 
      avgheight = avgheight/d.length; 
 

 
      d3.select(this) 
 
      .select('rect') 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + avgheight + ")"; 
 
      }); 
 

 
      var rectangleText = ""; 
 
      for (var t = 1; t < heights.length; t++) { 
 
      rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2); 
 
      } 
 

 
      d3.select(this) 
 
      .selectAll('.innerRect') 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + (mouse[0] + 15) + "," + (avgheight + 40 - (i * 20)) + ")"; 
 
      }) 
 
      .attr('width', 10) 
 
      .attr('height', 10) 
 
      .attr("dx", '20px') 
 
      .attr("dy", '0px') 
 
      .style('fill', function(d) { 
 
       return color(d.name); 
 
      }); 
 

 
      d3.select(this) 
 
      .select(".DateText") 
 
      .text(xDateValue) 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + mouse[0] + "," + (avgheight - 25) + ")"; 
 
      }) 
 
      .attr("dx", '20px') 
 
      .style("fill", "white") 
 
      .style("font-weight", "bold") 
 
      .style("font-size", "10pt"); 
 

 
      d3.select(this) 
 
      .selectAll('.ValueText') 
 
      .text(function(d, i) { 
 
       return d.name + " " + y.invert(heights[i]).toFixed(2) 
 
      }) 
 
      .attr("transform", function(d, i) { 
 
       return "translate(" + (mouse[0] + 17) + "," + (avgheight + 50 - (i * 20)) + ")"; 
 
      }) 
 
      .attr("dx", '20px'); 
 

 
      return "translate(" + mouse[0] + "," + pos.y + ")"; 
 
     }); 
 
    }); 
 
    </script> 
 
</body> 
 

 
</html>

下面是使用D3为 “鼠标悬停” 动画的完整解决方案这个多线图。感谢所有的帮助和支持。希望这可以帮助。