简体   繁体   中英

How to use x and width in a bar chart with scaleTime?

I have a codepen here - https://codepen.io/anon/pen/xpaYYw?editors=0010

Its a simple test graph but the date will be formatted like this.

I have dates on the x axis and amounts on the y

How can I use the x scale to set the width and x position of the bars.

    layers.selectAll('rect')
      .data(data)
      .enter()
      .append('rect')

      .attr('height', function(d, i) {
        return height - y(d.one);
      })

      .attr('y', function(d, i) {
        return y(d.one);
      })

      .attr('width', function(d, i) {
        return 50;
      })

      .attr('x', function(d, i) {
        return 80*i;
      })

      .style('fill', (d, i) => {
        return colors[i];
      });

The problem with your question has nothing to do with programming, or JavaScript, or D3... the problem is a basic dataviz concept (that's why I added the tag in your question):

What you're trying to do is not correct! You should not use bars with a time scale. Time scales are for time series (in which we use dots, or dots connected by lines).

If you use bars with time in the x axis you'll face problems:

  1. Positioning the bar: the left margin of the bar will be always at the date you set. The whole bar will lie after that date;
  2. Setting the width of the bar: in a real bar chart, which uses categorical variables for the x axis, the width has no meaning. But in a time scale the width represents time .

However, just for the sake of explanation, let's create this bar chart with a time scale (despite the fact that this is a wrong choice)... Here is how to do it:

First, set the "width" of the bars in time . Let's say, each bar will have 10 days of width:

.attr("width", function(d){
    return x(d3.timeDay.offset(d.date, 10)) - x(d.date)
})

Then, set the x position of the bar to the current date less half its width (that is, less 5 days in our example):

.attr('x', function(d, i) {
    return x(d3.timeDay.offset(d.date, -5));
})

Finally, don't forget to create a "padding" in the time scale:

var x = d3.scaleTime()
  .domain([d3.min(data, function(d) {
    return d3.timeDay.offset(d.date, -10);
  }), d3.max(data, function(d) {
    return d3.timeDay.offset(d.date, 10);
  })])
  .range([0, width]);

Here is your code with those changes:

 var keys = []; var legendKeys = []; var maxVal = []; var w = 800; var h = 450; var margin = { top: 30, bottom: 40, left: 50, right: 20, }; var width = w - margin.left - margin.right; var height = h - margin.top - margin.bottom; var colors = ['#FF9A00', '#FFEBB6', '#FFC400', '#B4EDA0', '#FF4436']; var data = [{ "one": 4306, "two": 2465, "three": 2299, "four": 988, "five": 554, "six": 1841, "date": "2015-05-31T00:00:00" }, { "one": 4378, "two": 2457, "three": 2348, "four": 1021, "five": 498, "six": 1921, "date": "2015-06-30T00:00:00" }, { "one": 3404, "two": 2348, "three": 1655, "four": 809, "five": 473, "six": 1056, "date": "2015-07-31T00:00:00" }, ]; data.forEach(function(d) { d.date = new Date(d.date) }) for (var i = 0; i < data.length; i++) { for (var key in data[i]) { if (!data.hasOwnProperty(key) && key !== "date") maxVal.push(data[i][key]); } } var x = d3.scaleTime() .domain([d3.min(data, function(d) { return d3.timeDay.offset(d.date, -10); }), d3.max(data, function(d) { return d3.timeDay.offset(d.date, 10); })]) .range([0, width]); var y = d3.scaleLinear() .domain([0, d3.max(maxVal, function(d) { return d; })]) .range([height, 0]); var svg = d3.select('body').append('svg') .attr('class', 'chart') .attr('width', w) .attr('height', h); var chart = svg.append('g') .classed('graph', true) .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); var layersArea = chart.append('g') .attr('class', 'layers'); var layers = layersArea.append('g') .attr('class', 'layer'); layers.selectAll('rect') .data(data) .enter() .append('rect') .attr('height', function(d, i) { return height - y(d.one); }) .attr('y', function(d, i) { return y(d.one); }) // .attr('width', function(d, i) { // return 50; // }) .attr("width", function(d) { return x(d3.timeDay.offset(d.date, 10)) - x(d.date) }) .attr('x', function(d, i) { return x(d3.timeDay.offset(d.date, -5)); }) .style('fill', (d, i) => { return colors[i]; }); chart.append('g') .classed('x axis', true) .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x) .tickFormat(d3.timeFormat("%Y-%m-%d")).tickValues(data.map(function(d) { return new Date(d.date) }))); chart.append('g') .classed('y axis', true) .call(d3.axisLeft(y) .ticks(10));
 <script src="https://d3js.org/d3.v4.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