Line up x-axis with tick marks in d3.js

房东的猫 提交于 2020-01-04 20:12:20

问题


How can I properly line up the x-axis values to the vertical lines in the chart? Currently they are not properly aligned.

var data = [
  {
    "due": 89.99, 
    "datestr": "2012-12-01 00:00:00", 
    "paid": 89.99
  }, 
  {
    "due": 89.99, 
    "datestr": "2013-01-01 00:00:00", 
    "paid": 101.25
  }, 
  {
    "due": 109.99, 
    "datestr": "2013-02-01 00:00:00", 
    "paid": 143.69
  }, 
  {
    "due": 109.99, 
    "datestr": "2013-03-01 00:00:00", 
    "paid": 130.65
  }, 
  {
    "due": 139.99, 
    "datestr": "2013-04-01 00:00:00", 
    "paid": 150.25
  },

  {
    "due": 149.99, 
    "datestr": "2013-05-01 00:00:00", 
    "paid": 139.99
  },
  {
    "due": 149.99, 
    "datestr": "2013-06-01 00:00:00", 
    "paid": 139.99
  },
  {
    "due": 89.99, 
    "datestr": "2013-07-01 00:00:00", 
    "paid": 139.99
  }
];




//Load values into new arrays

var amtDue = new Array();
var amtPaid = new Array();

for(var i=0; i < data.length; i++) {
  amtDue[i] = data[i].due;
  amtPaid[i] = data[i].paid;
}

// Margins, width and height. 
var margin = {top: 20, right: 20, bottom: 60, left: 50},
    width = 1000,
    height = 400,
    padding = 100;

// Scales.
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var due = d3.scale.linear().range([0, width]);
var paid = d3.scale.linear().range([0, width]);


//Domains
x.domain([d3.min(data, function(d) { return d.date; }), d3.max(data, function(d) { return d.date;})]);
y.domain([50, 300]);
due.domain([0, amtDue.length]);
paid.domain([0, amtPaid.length]);

// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");

// X-axis MONTHS.
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("top")
    .tickSize(-height)
    .tickFormat(d3.time.format('%b'));
svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(100," + 0 + ")")
    .call(xAxis);

//X-axis Due Date
var dDate = d3.svg.axis()
    .scale(due)
    .orient("bottom")
    .tickSize(0)
    .tickFormat(function(d, i) {return amtDue[i];});
svg.append("g")
    .attr("class", "damount axis")
    .attr("transform", "translate(100," + (height) + ")")
    .call(dDate);

//X-axis Paid Date
var pDate = d3.svg.axis()
    .scale(paid)
    .orient("bottom")
    .tickSize(0)
    .tickFormat(function(d, i) {return amtPaid[i];});
svg.append("g")
    .attr("class", "pamount axis")
    .attr("transform", "translate(100," + (height+20) + ")")
    .call(pDate);

//Y-axis
var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickSize(0);

svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(" + 0 + ",0)")
    .call(yAxis);

// Date parsing.
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S");
data.forEach(function(d) {
  d.date = parseDate.parse(d.datestr);
  d.close = +d.close;
});

// Set scale domains. 
x.domain(d3.extent(data, function(d) { return d.date; }));
//y.domain([0, d3.max(data, function(d) { return d.due; })]);

// Call x-axis. 
d3.select(".x.axis")
    //.transition().duration(1000)
    .call(xAxis);


// Draw bars for amount due. 
var bars = svg.selectAll("rect")
        .data(data, function(d) { return d.datestr; });

bars.exit().remove();

// bars.transition().duration(1000)
//     .attr("x", function(d) { return x(d.date); })
//     .attr("width", width / data.length)
//     .attr("y", function(d) { return y(d.due); })
//     .attr("height", function(d) { return height - y(d.due);});

bars.enter().append("rect")
    .attr("class", "duebar")
    .attr("width", width/data.length)
    .attr("x", function(d, i) {  return i * (width / data.length); })
    .attr("y", height)
    .attr("height", 0)
    .transition().duration(1000)
    .attr("y", function(d) { return y(d.due); })
    .attr("height", function(d) { return (y(d.due)+3)-y(d.due);});



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


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

This is the fiddle.


回答1:


Fiddle here: http://jsfiddle.net/henbox/hbmwbnx6/

Using your current approach, this is sort-of-fixable. To start with, you have 8 data values (Dec '12 to July '13). Using the d3.time.scale() with that date domain will give you 8 'month' tick, but a scale divided into 7 equal 'gaps' between the ticks.

So for the d3.scale.linear() scales to line up, you should use:

due.domain([0, amtDue.length - 1]);

rather than:

due.domain([0, amtDue.length]);

because the array length counts from 1, not 0.

Also note that from your code I removed several horizontal translate's.

The reason I say this is only a sort-of-fix, is that the bars for "amount due" don't line up correctly with the months. That's because the bar widths are equal (using .attr("width", width/(data.length -1))) but months (on the d3.time scale) are not of equal length:

The solution to this would be to use the x (time) scale to define the bar width

Update

Here's an update of the fiddle with a (slightly hacky) solution to the bar width issue by taking the current date and previous date values from the data: http://jsfiddle.net/henbox/hbmwbnx6/1/

Code is:

// The first date from your data - don't hardcode!
var previousdate = new Date("2012-12-01 00:00:00")

bars.enter().append("rect")
    .attr("class", "duebar")
    .attr("width", function(d, i) { 
        widthval = x(d.date) - (x(previousdate));
        previousdate = d.date;
        return widthval;
    })
    .attr("x", function(d, i) { 
        xval = x(previousdate);
        previousdate = d.date;
        console.log(previousdate);
        return xval;
    })
    ...

Note that this code highlights another issue: Bars appear to show data between 2 dates, but actually show data at a specific point in time. The December '12 'amount due' value isn't shown, while the Jan '13 value is shown as the bar between Dec and Jan in your chart. You'd need to decide how you wanted to display this



来源:https://stackoverflow.com/questions/29924721/line-up-x-axis-with-tick-marks-in-d3-js

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!