D3折线图鼠标悬停坐标效果

时间:2015-03-23 20:38:17

标签: javascript jquery svg d3.js

我正在尝试创建一个折线图,用户可以将鼠标悬停在点上以显示一个圆圈(并且还会在页面上发生其他事情,但我首先会显示一个圆圈)。

我已经尝试了几个教程,所有这些教程都有相同的结果:圆圈出现在线上方或侧面,几乎没有出现在图表上。

如何让圆圈出现在与之对应的坐标位置。这是问题的Fiddle和下面的d3代码。 JS Fiddle中还包括我的其他失败尝试。

基本图表:

var data=[{
    "date":"January 1, 2008","total":'73'},
    {"date":"February 1, 2008","total":'40'},
    {"date":"March 1, 2008","total":'43'},
    {"date":"April 1, 2008","total":'28'},
    {"date":"May 1, 2008","total":'35'},
    {"date":"June 1, 2008","total":'20'},
    {"date":"July 1, 2008","total":'48'}
          ];


var chartWidth =650;
    var chartHeight=375;
    var chartMargins ={
            top:20,
            right: 20,
            bottom: 20,
            left: 50};

    var yScaleDomain=[1, 100];



    var chart = d3.select('#chartCanvas'),
        WIDTH=chartWidth,
        HEIGHT=chartHeight,
        MARGINS= chartMargins;


    //SET AXIS SCALES


    xScale = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]).domain(d3.extent(data, function(d) { return new Date(d.date); }));
   var x = d3.time.scale()
    .range([0, WIDTH]);

var y = d3.scale.linear()
    .range([HEIGHT, 0]);
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain(yScaleDomain);



    //DRAW AXES

    xAxis = d3.svg.axis()
        .scale(xScale),
    yAxis = d3.svg.axis()
        .scale(yScale)
    .orient("left");

    chart.append("svg:g")
        .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
        .call(xAxis);
    chart.append("svg:g")
        .attr("transform", "translate(" + (MARGINS.left) + ",0)")
        .call(yAxis);

    //DRAW LINES/ PLOT DATA


   var lineGen = d3.svg.line()
        .x(function(d) {
            //Date() turns string date to numerical value
            return xScale(new Date(d.date));
        })
        .y(function(d) {
            return yScale(d.total);
        })
        //tell d3 what type of lines to draw (straight/linear ones)
        .interpolate('linear'); 

 chart.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", lineGen);

MouseOver尝试1(其他尝试在小提琴中注释掉):

var Y_value; 

    var curve1 = chart.select("path.line").data([data]);

    x = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]);
    y = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]);


    var circle = chart.append("circle")
            .attr("r", 8)
            .attr("cx", 0)
            .attr("cy", 0)
            .style({fill: '#fff', 'fill-opacity': .2, stroke: '#000', "stroke-width": '1px'})
            .attr("opacity", 0);

    var tooltip = circle.append("chart:title");


    chart.on("mouseover", function() {
        var X_pixel = d3.mouse(this)[0],
            X_date = x.invert(X_pixel);
        var Y_pixel = y(Y_value);

        var pathData = curve1.data()[0]; // recupere donnée de la courbe

        pathData.forEach(function(element, index, array) {
                if ((index+1 < array.length) && (array[index].date <= X_date) && (array[index+1].date >= X_date)) {
                    if (X_date-array[index].date < array[index+1].date-X_date)  Y_value = array[index].val;
                    else Y_value = array[index+1].val;
                }
            });

            circle.attr("opacity", 1)
                .attr("cx", X_pixel)
                .attr("cy", Math.round(y(Y_value)));

            tooltip.text("X = " + (X_date) + "\nY = " + (Y_value));
        });
     chart.append('svg:path')
            .attr('d', lineGen(data))
            .attr('stroke', 'green')
            .attr('stroke-width', 2)
            .attr('fill', 'none');

        stickyChart.append('svg:path')
            .attr('d', lineGen2(data))
            .attr('stroke', 'green')
            .attr('stroke-width', 2)
            .attr('fill', 'none');

1 个答案:

答案 0 :(得分:0)

这位先生有很好的实现:

https://bl.ocks.org/mbostock/3902569

如果它消失了,我在这里创建了它的独立版本:

&#13;
&#13;
var margin = {
    top: 20,
    right: 50,
    bottom: 30,
    left: 50
  },
  width = 400,
  height = 150;

var parseDate = d3.time.format("%d-%b-%y").parse,
  bisectDate = d3.bisector(function(d) {
    return d.date;
  }).left,
  formatValue = d3.format(",.2f"),
  formatCurrency = function(d) {
    return "$" + formatValue(d);
  };

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");

var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left");

var line = d3.svg.line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.close);
  });

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

function process(data) {

  data.sort(function(a, b) {
    return a.date - b.date;
  });

  x.domain([data[0].date, data[data.length - 1].date]);
  y.domain(d3.extent(data, function(d) {
    return d.close;
  }));

  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("Price ($)");

  svg.append("path")
    .datum(data)
    .attr("class", "line")
    .attr("d", line);

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

  function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
      i = bisectDate(data, x0, 1),
      d0 = data[i - 1],
      d1 = data[i],
      d = x0 - d0.date > d1.date - x0 ? d1 : d0;
    focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
    focus.select("text").text(formatCurrency(d.close));
  }
}

var data = [];
var close = 95;
for (var i = 0; i < 100; i++) {
  var date = new Date();
  var dateValue = date.getDate() + i;
  date.setDate(dateValue);
  close = close - 1 + 2 * Math.random();
  data[i] = {
    date: date,
    close: close
  };
}
process(data);
&#13;
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;
}

.overlay {
  fill: none;
  pointer-events: all;
}

.focus circle {
  fill: none;
  stroke: steelblue;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;