簡體   English   中英

D3.js折線圖工具提示問題

[英]D3.js line chart tooltip issue

我已經使用d3.js創建了折線圖,並在圖表中添加了工具提示,在鼠標懸停時會創建一個小圓圈並顯示y軸值。 到此階段,一切正常,但問題出在鼠標懸停時顯示的圓的位置。

我希望此圓圈顯示在線條上方,但始終顯示在線條圖上方或下方。

所以,我必須發出:

  1. 要修復工具提示圓,使其始終位於折線圖的直線上?
  2. 如何顯示定義點之間的點的y值?

我正在使用.interpolate("basis")來生成直線,這是由於圓圈位置變得混亂。 我不知道如何解決此問題,因為我的代碼中需要.interpolate("basis")

請任何人在此代碼中了解如何解決這些問題:

 var data = [{ x: '1-May-12', y: 5 }, { x: '30-Apr-12', y: 28 }, { x: '27-Apr-12', y: 58 }, { x: '26-Apr-12', y: 88 }, { x: '25-Apr-12', y: 8 }, { x: '24-Apr-12', y: 48 }, { x: '23-Apr-12', y: 28 }, { x: '20-Apr-12', y: 68 }, { x: '19-Apr-12', y: 8 }, { x: '18-Apr-12', y: 58 }, { x: '17-Apr-12', y: 5 }, { x: '16-Apr-12', y: 80 }, { x: '13-Apr-12', y: 38 }], width = 1200, height = 360, margin = { top: 30, right: 20, bottom: 30, left: 50 }; width -= margin.left - margin.right; height -= margin.top - margin.bottom; var parseDate = d3.time.format("%d-%b-%y").parse; var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height, 0]); var xAxis = d3.svg.axis().scale(x) .orient("bottom").ticks(0).tickSize(0) .tickFormat("").outerTickSize(0); var yAxis = d3.svg.axis().scale(y) .orient("left").tickSize(0).ticks(0) .tickFormat(""); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("class", "bg-color") .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // function for the y grid lines function make_y_axis() { return d3.svg.axis() .scale(y) .orient("left") .ticks(10); } // Draw the y Grid lines svg.append("g") .attr("class", "grid") .call(make_y_axis() .tickSize(-width, 0, 0) .tickFormat("") ); x.domain(d3.extent(data, function(d) { return parseDate(dx); })); y.domain([0, d3.max(data, function(d) { return dy; })]); data.sort(function(a, b) { return parseDate(ax) - parseDate(bx); }); /*x.domain([parseDate(data[0].x), parseDate(data[data.length - 1].x)]); y.domain(d3.extent(data, function (d) { return dy; }));*/ // Add the X Axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // Add the Y Axis svg.append("g") .attr("class", "y axis") .call(yAxis); // Add the valueline path svg.append("path") .attr("class", "line"); // Define the line var lineGen = d3.svg.line() .x(function(d) { return x(parseDate(dx)); }) .y(function(d) { return y(dy); }) .interpolate("basis"); svg.append('path') .attr("class", "line") .attr('d', lineGen(data)); var focus = svg.append("g") .attr("class", "focus") .style("display", "none"); focus.append("circle") .attr("r", 4.5); focus.append("text") .attr("x", 9) .attr("dy", ".35em"); svg.append("rect") .attr("class", "overlay") .attr("width", width) .attr("height", height) .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); var bisectDate = d3.bisector(function(d) { return parseDate(dx); }).left; function mousemove() { var x0 = x.invert(d3.mouse(this)[0]), i = bisectDate(data, x0, 1), d0 = data[i - 1], d1 = data[i], d = x0 - parseDate(d0.x) > parseDate(d1.x) - x0 ? d1 : d0; console.log(y(dy)); focus.attr("transform", "translate(" + x(parseDate(dx)) + "," + y(dy) + ")"); focus.select("text").text(dy); } 
 .x.axis path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } .overlay { fill: none; pointer-events: all; } .focus circle { fill: none; stroke: steelblue; } .axis path, .axis line { fill: none; stroke: grey; stroke-width: 2; shape-rendering: crispEdges; } .grid .tick { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

https://jsfiddle.net/nikunj2512/w5y1q5ds/1/

這個問題不時出現,我可以想到兩個解決方案。

解決方案1-您可以解析路徑本身並將這些點(和值)顯示為工具提示,以保留在擬合路徑上。 該解決方案的d3是,根據d3擬合的方式,您最終會得到額外的點或稍微偏離位置的點:

// get path
var p = svg.select('.line')
  .attr("d");
// parse it by M coords, L coords and C coords
// build an array of points used in generating the path
var points = p.split(/(?=[LMC])/).map(function(d){
  var fChar = d.slice(0, 1);
  if (fChar === "M" || fChar === "L"){
    return d.slice(1, d.length)
            .split(",")
            .map(function(p){ return +p; });
  } else if (fChar === "C") {
    var s = d.split(",");
    return [+s[s.length - 2], +s[s.length - 1]];
  }
});
// modify the bisect our array of points
var bisectDate = d3.bisector(function(d) {
  return d[0];
}).left;
// modify the mousemove
function mousemove() {
  var x0 = d3.mouse(this)[0],
    i = bisectDate(points, x0, 1) - 1,
    y0 = y.invert(points[i][1]);

  //console.log(y(d.y));
  focus.attr("transform", "translate(" + points[i][0] + "," + points[i][1] + ")");
  focus.select("text").text(y0);
}

解決方案2-只需讓您的工具提示完整地遵循此路徑即可。 對於某些內置的SVG構造,這是一種非常簡單的方法:

var path = svg.select('.line').node();
var totLength = path.getTotalLength();
function mousemove() {
  var x0 = d3.mouse(this)[0],
      per = width / x0;
      point = path.getPointAtLength(totLength / per)
      y0 = y.invert(point.y);

  focus.attr("transform", "translate(" + point.x + "," + point.y + ")");
  focus.select("text").text(y0);
}

解決方案1-完整的工作代碼:

 <!DOCTYPE html> <html> <head> <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> <style> .x.axis path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } .overlay { fill: none; pointer-events: all; } .focus circle { fill: none; stroke: steelblue; } .axis path, .axis line { fill: none; stroke: grey; stroke-width: 2; shape-rendering: crispEdges; } .grid .tick { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } </style> </head> <body> <script> var data = [{ x: '1-May-12', y: 5 }, { x: '30-Apr-12', y: 28 }, { x: '27-Apr-12', y: 58 }, { x: '26-Apr-12', y: 88 }, { x: '25-Apr-12', y: 8 }, { x: '24-Apr-12', y: 48 }, { x: '23-Apr-12', y: 28 }, { x: '20-Apr-12', y: 68 }, { x: '19-Apr-12', y: 8 }, { x: '18-Apr-12', y: 58 }, { x: '17-Apr-12', y: 5 }, { x: '16-Apr-12', y: 80 }, { x: '13-Apr-12', y: 38 }], width = 1200, height = 360, margin = { top: 30, right: 20, bottom: 30, left: 50 }; width -= margin.left - margin.right; height -= margin.top - margin.bottom; var parseDate = d3.time.format("%d-%b-%y").parse; var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height, 0]); var xAxis = d3.svg.axis().scale(x) .orient("bottom").ticks(0).tickSize(0) .tickFormat("").outerTickSize(0); var yAxis = d3.svg.axis().scale(y) .orient("left").tickSize(-width, 0, 0); //.ticks(0) //.tickFormat(""); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("class", "bg-color") .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // function for the y grid lines function make_y_axis() { return d3.svg.axis() .scale(y) .orient("left") .ticks(10); } x.domain(d3.extent(data, function(d) { return parseDate(dx); })); y.domain([0, d3.max(data, function(d) { return dy; })]); console.log(y.domain()) data.sort(function(a, b) { return parseDate(ax) - parseDate(bx); }); /*x.domain([parseDate(data[0].x), parseDate(data[data.length - 1].x)]); y.domain(d3.extent(data, function (d) { return dy; }));*/ // Add the X Axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // Add the Y Axis svg.append("g") .attr("class", "y axis") .call(yAxis); // Define the line var lineGen = d3.svg.line() .x(function(d) { return x(parseDate(dx)); }) .y(function(d) { return y(dy); }) .interpolate("basis"); svg.append('path') .attr("class", "line") .attr('d', lineGen(data)); var focus = svg.append("g") .attr("class", "focus") .style("display", "none"); focus.append("circle") .attr("r", 4.5); focus.append("text") .attr("x", 9) .attr("dy", ".35em"); svg.append("rect") .attr("class", "overlay") .attr("width", width) .attr("height", height) .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); var p = svg.select('.line') .attr("d"); var points = p.split(/(?=[LMC])/).map(function(d){ var fChar = d.slice(0, 1); if (fChar === "M" || fChar === "L"){ return d.slice(1, d.length) .split(",") .map(function(p){ return +p; }); } else if (fChar === "C") { var s = d.split(","); return [+s[s.length - 2], +s[s.length - 1]]; } }); var bisectDate = d3.bisector(function(d) { return d[0]; }).left; function mousemove() { var x0 = d3.mouse(this)[0], i = bisectDate(points, x0, 1) - 1, y0 = y.invert(points[i][1]); //console.log(y(dy)); focus.attr("transform", "translate(" + points[i][0] + "," + points[i][1] + ")"); focus.select("text").text(y0); } </script> </body> </html> 


解決方案2-完整的工作代碼:

 <!DOCTYPE html> <html> <head> <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> <style> .x.axis path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } .overlay { fill: none; pointer-events: all; } .focus circle { fill: none; stroke: steelblue; } .axis path, .axis line { fill: none; stroke: grey; stroke-width: 2; shape-rendering: crispEdges; } .grid .tick { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } </style> </head> <body> <script> var data = [{ x: '1-May-12', y: 5 }, { x: '30-Apr-12', y: 28 }, { x: '27-Apr-12', y: 58 }, { x: '26-Apr-12', y: 88 }, { x: '25-Apr-12', y: 8 }, { x: '24-Apr-12', y: 48 }, { x: '23-Apr-12', y: 28 }, { x: '20-Apr-12', y: 68 }, { x: '19-Apr-12', y: 8 }, { x: '18-Apr-12', y: 58 }, { x: '17-Apr-12', y: 5 }, { x: '16-Apr-12', y: 80 }, { x: '13-Apr-12', y: 38 }], width = 1200, height = 360, margin = { top: 30, right: 20, bottom: 30, left: 50 }; width -= margin.left - margin.right; height -= margin.top - margin.bottom; var parseDate = d3.time.format("%d-%b-%y").parse; var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height, 0]); var xAxis = d3.svg.axis().scale(x) .orient("bottom").ticks(0).tickSize(0) .tickFormat("").outerTickSize(0); var yAxis = d3.svg.axis().scale(y) .orient("left").tickSize(-width, 0, 0); //.ticks(0) //.tickFormat(""); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("class", "bg-color") .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // function for the y grid lines function make_y_axis() { return d3.svg.axis() .scale(y) .orient("left") .ticks(10); } x.domain(d3.extent(data, function(d) { return parseDate(dx); })); y.domain([0, d3.max(data, function(d) { return dy; })]); data.sort(function(a, b) { return parseDate(ax) - parseDate(bx); }); /*x.domain([parseDate(data[0].x), parseDate(data[data.length - 1].x)]); y.domain(d3.extent(data, function (d) { return dy; }));*/ // Add the X Axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // Add the Y Axis svg.append("g") .attr("class", "y axis") .call(yAxis); // Define the line var lineGen = d3.svg.line() .x(function(d) { return x(parseDate(dx)); }) .y(function(d) { return y(dy); }) .interpolate("basis"); svg.append('path') .attr("class", "line") .attr('d', lineGen(data)); var focus = svg.append("g") .attr("class", "focus") .style("display", "none"); focus.append("circle") .attr("r", 4.5); focus.append("text") .attr("x", 9) .attr("dy", ".35em"); svg.append("rect") .attr("class", "overlay") .attr("width", width) .attr("height", height) .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); var path = svg.select('.line').node(); var totLength = path.getTotalLength(); function mousemove() { var x0 = d3.mouse(this)[0], per = width / x0; point = path.getPointAtLength(totLength / per) y0 = y.invert(point.y); focus.attr("transform", "translate(" + point.x + "," + point.y + ")"); focus.select("text").text(y0); } </script> </body> </html> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM