在D3轴上格式化分钟和秒

时间:2018-06-05 00:46:00

标签: javascript d3.js time axis

我正在开发一个D3项目,我有JSON格式的时间(分钟和秒),如下所示:

"Time": "36:50"

如何在D3图形的y轴上以该格式绘制时间?我应该使用d3.scaleTimed3.scaleLinear作为轴吗?如何制作以minute:second格式显示时间的刻度,就像它采用JSON格式一样?

修改:
这是我到目前为止的尺度/轴:

  //Format data for X Axis
      var yearFormat = "%Y";
      var parsedYear = years.map(function(d) {
        return d3.timeParse(yearFormat)(d)
      });


      var x = d3.scaleTime()
                .range([0, width])
                .domain(d3.extent(parsedYear));


      // Format data for Y Axis
      var specifier = "%M:%S";
      var parsedTime = times.map(function(d) {
        return d3.timeParse(specifier)(d)
      })

      var y = d3.scaleTime()
                .range([height, 0])
                .domain(d3.extent(parsedTime));

      // X Axis
      g.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x))
        .attr("stroke-width", 2)
        .style("font-size", ".6em")


      // Y Axis
      g.append("g")
        .call(d3.axisLeft(y))
        .attr("stroke-width", 2)
        .style("font-size", ".6em")
        .tickValues(parsedTime)
        .tickFormat(function(d){
           return d3.timeFormat(specifier)(d)
         });

1 个答案:

答案 0 :(得分:3)

Any possible answer for your question is dangerously opinion-based, because it depends on exactly what you want: do you want to treat time as time? Or do you want to treat time as just separate, individual events?

There are actually three options for you here:

  1. Using an ordinal scale (band, point...): in this case, each data entry will be just a categorical variable. The space between the ticks in the axis will be the same.
  2. Using a linear scale: this allows different distances for the ticks in the axis. However, the values won't be time, you'll have to create a math to set the values (the easiest one is probably calculating the number of seconds).
  3. Using a time scale: this has an advantage, which is the fact that you'll treat time as time. The main disadvantage, however, is exactly the previous statement: time is a complicated beast, you'll have leap years, daylight savings, time zone differences etc...

Since the linear scale is quite simple to create (and the ordinal scale is even more simple), here is an answer showing how to do it with a time scale.

Suppose you have this data array:

var data = ["12:32", "21:05", "24:56", "36:30", "45:14", "71:11"];

The first step is parsing the dates:

var specifier = "%M:%S";
var parsedData = data.map(function(d) {
  return d3.timeParse(specifier)(d)
});

And then creating the scale:

var scale = d3.scaleTime()
  .domain(d3.extent(parsedData));

For this to work, we'll have to set the tickValues using our parsed data array, and formatting the ticks for minutes and seconds:

var axis = d3.axisBottom(scale)
    .tickValues(parsedData)
    .tickFormat(function(d){
        return d3.timeFormat(specifier)(d)
    });

Here is the result:

var data = ["12:32", "21:05", "24:56", "36:30", "45:14", "71:11"];
var specifier = "%M:%S";
var svg = d3.select("svg");
var parsedData = data.map(function(d) {
  return d3.timeParse(specifier)(d)
});
var scale = d3.scaleTime()
  .domain(d3.extent(parsedData))
  .range([30, 470]);
var axis = d3.axisBottom(scale)
  .tickValues(parsedData)
  .tickFormat(function(d) {
    return d3.timeFormat(specifier)(d)
  });
var gX = svg.append("g")
  .attr("transform", "translate(0,50)")
  .call(axis)
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="100"></svg>

As you can see, the ticks are correctly spaced here. But we have a problem: those ticks are actual dates (if you inspect the code, you'll see that the year is 1900). Because of that, the last tick, which should be 71 minutes and 11 seconds, is shown as 11:11, because it is the eleventh minute of the next hour.

A simple solutions is referencing the original array of strings:

.tickFormat(function(d,i) {
    return data[i]
});

And here is the result:

var data = ["12:32", "21:05", "24:56", "36:30", "45:14", "71:11"];
var specifier = "%M:%S";
var svg = d3.select("svg");
var parsedData = data.map(function(d) {
  return d3.timeParse(specifier)(d)
});
var scale = d3.scaleTime()
  .domain(d3.extent(parsedData))
  .range([30, 470]);
var axis = d3.axisBottom(scale)
  .tickValues(parsedData)
  .tickFormat(function(d, i) {
    return data[i]
  });
var gX = svg.append("g")
  .attr("transform", "translate(0,50)")
  .call(axis)
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="100"></svg>