简体   繁体   中英

D3 Line Chart Mouseover Coordinates Effect

I am trying to create a line chart, where users can mouse over points to reveal a circle (and also make other things happen on the page, but I'll start with making a circle appear, first).

I've tried several tutorials and all of them have the same results: The circles appear either above the line or off to the side, barely on the chart.

How does one go about getting the circles to appear at the coordinate location to which it corresponds. Here is a Fiddle of the problem and the d3 code below. Also included in the JS Fiddle are my other failed attempts.

Basic chart:

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 Attempt 1 (other attempts commented out in Fiddle):

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

This gentleman has a pretty good implementation of this:

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

In case it goes away, I've created a standalone version of it here:

 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); 
 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; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM