Д3.шкала времени js: красиво разнесенные тики через минутные интервалы, когда данные находятся в секундах?
Я работаю с временной шкалой D3. Мои входные данные находятся в секундах, и это данные о продолжительности, а не даты - так 10 секунд, 30 секунд и т. д.
Я хочу создать ось, которая позволяет мне делать следующее:
- отображение тиков, отформатированных в минутах и секундах: например, "0m 30s", "1m 00s" и т. д. Это форматирование само по себе довольно просто, но не тогда, когда мне это нужно...
- отображение тиков с интервалами, которые выглядят аккуратно при форматировании в минутах. Если я просто использую форматирование ТИКов по умолчанию D3, то я получаю тики с интервалами, которые имеют смысл в минутах, но не секундах.
вот мой код:
var values = [100,200,300....]; // values in seconds
var formatCount = d3.format(",.0f"),
formatTime = d3.time.format("%Mm %Ss"),
formatMinutes = function(d) {
var t = new Date(2012, 0, 1, 0, 0, d);
t.setSeconds(t.getSeconds() + d);
return formatTime(t);
};
var x = d3.scale.linear()
.domain([0, d3.max(values)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatMinutes);
Это дает мне красиво отформатированные тики с нерегулярными интервалами:" 16m 40s"," 33m 20s " и т. д. Как я могу генерировать Тики на "10m 00s", "20m 00s" и т. д.?
очевидным ответом было бы преобразовать массив значений в минуты, использовать линейный масштаб и написать форматер для его обработки, но я бы предпочел использовать шкала времени, если это возможно
вот JSFiddle, чтобы продемонстрировать проблему:http://jsfiddle.net/83Xmf/
1 ответов
обычно при создании шкалы времени вы бы использовали d3.time.scale()
, а не линейный масштаб.
ваш случай немного странный в том, что вы используете abstract продолжительностью времени, а не конкретных точки во время для ваших данных. К сожалению, кажется, что встроенная во времени функциональность d3 не подходит для этого случая. Есть несколько вариантов, которые я могу придумать для обходных путей:
Вариант 1: Использовать a линейная шкала с ручным .tickValues()
вместо форматирования тиков с помощью
formatMinutes = function(d) {
var hours = Math.floor(d / 3600),
minutes = Math.floor((d - (hours * 3600)) / 60),
seconds = d - (minutes * 60);
var output = seconds + 's';
if (minutes) {
output = minutes + 'm ' + output;
}
if (hours) {
output = hours + 'h ' + output;
}
return output;
};
в основном, это занимает общее количество секунд, создает час за каждые 3600 секунд, создает минуту за каждые оставшиеся 60 секунд и, наконец, возвращает оставшиеся секунды. Затем он выводит строковое представление, для пример: 17s
или 12m 42s
или 4h 8m 22s
.
затем, когда вы делаете свою ось, вы можете использовать .tickValues()
метод, чтобы назначить диапазон от нуля до максимального значения ваших данных, идя шагами 600, так как есть 600 секунд в 10 минутах. Это выглядело бы так:
var x = d3.scale.linear()
.domain([0, d3.max(values)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatMinutes)
.tickValues(d3.range(0, d3.max(values), 600));
здесь JSFiddle выходной.
Вариант 2: Используйте шкалу времени с фиксированной продолжительностью для .ticks()
шкала времени позвольте вам прямо указать, что вы хотите, чтобы тики каждые 10 минут. Вы делаете это, просто передавая длительность d3 и множитель в .ticks()
способ своей оси. Вот так:
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.minute, 10)
для этого необходимо сначала настроить шкалу времени. Для области вашего масштаба вы можете использовать диапазон миллисекундных значений, так как d3 превратит их в Date
объекты. В этом случае, поскольку ваши данные находятся в секундах, мы можем просто умножить на 1000, чтобы получить миллисекунды. В этом деле мы округлим максимальное значение до ближайшей миллисекунды, так как это должно быть целое число, чтобы сделать допустимую дату:
var x = d3.time.scale()
.domain([0, Math.ceil(d3.max(values) * 1000)])
.range([0, width]);
наконец, вы можете передать свой формат непосредственно на ось, используя .tickFormat()
:
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.minute, 10)
.tickFormat(d3.time.format('%Mm %Ss'));
.tickFormat чтобы показать часы, а также:
.tickFormat(d3.time.format('%Hh %Mm %Ss'));
есть взгляните на JSFiddle о том, каким будет результат...
в зависимости от того, где вы находитесь в мире, вы получите разные значения для часов, место. Я на восточном побережье США, поэтому мое место часов говорит 19. - Откуда это? Разве не должно быть ноль?
ну, к сожалению, когда мы сделали домен масштаба от 0 до количества миллисекунд наибольшего значения данных, он создал regular Date
объекты, используя эти значения для миллисекундного ввода. Это означает, что они представляют собой количество миллисекунд с полуночного времени UTC 1 января 1970 года. Здесь, в восточном часовом поясе США, это означает, что это было 19:00:00 31 декабря 1969 года. Вот откуда 19 взялось, или что-то еще сделать.
если вы знаете, что все ваши данные будут меньше 1 часа, то, возможно, вы можете просто игнорировать это. Если вам нужно использовать часов, вы можете работать вокруг этого принуждение d3 использовать время UTC для форматирования оси с помощью d3.time.format.utc()
:
.tickFormat(d3.time.format.utc('%Hh %Mm %Ss'))
здесь JSFiddle обновлено для использования UTC.
теперь вы можете видеть, что час равен 0, как и ожидалось.
конечно, если какие-либо из ваших данных когда-либо длиннее 24 часов, этот метод не будет работать вообще, и вам придется прибегнуть к выполнению оси вручную, как в 1.
надеюсь, это поможет, по крайней мере, получить вы начали, это сложная проблема, и, похоже, нет элегантного решения, встроенного в библиотеку для обработки этого. Возможно, это сделало бы хороший запрос функции на Git-РЕПО d3. Я хотел бы услышать, Есть ли у @mbostock какие-либо предложения о том, как обрабатывать абстрактные длительности времени в d3 без привязки к Date
объекты, которые требуют ссылок на абсолютные точки во времени.