С D3 (v4, обновление до v5 также возможно), я рисую временной ряд для временного интервала. Данные имеют неизвестную продолжительность, обычно это количество секунд, и у нас есть отметка даты начала и отметка даты окончания.
То, что я сейчас делаю для создания оси x, выглядит следующим образом, где startTime и endTime - числовые метки времени, width и height - числа, а xAxisGroupSelection - выбор D3:
const xScale = d3.scaleTime()
.domain([0, startTime - endTime)
.range([0, width])
const xAxis = d3.axisBottom()
.scale(this.xScale)
xAxisGroupSelection
.attr('transform', `translate(0, ${height})`)
.call(xAxis)
Что-то это делает очень хорошо, так это дает разумные форматы для чисел. Например, он стилизует секунды, как :01, и миллисекунды, как .500, и если я получу довольно длинный интервал, который длится минуты или часы, он справится с этим.
Проблема в том, что поскольку он считывает метку времени 0, он добавляет метку года 1970 в начало профиля. Это, конечно, правильный год для метки времени 0, но я не хочу, чтобы он отображал год любой для временного ряда в секундах. Есть ли способ, используя конфигурацию D3, я могу сохранить форматирование времени по умолчанию, но не добавлять отметки для года (или месяца, дня и т. д.)?
<g class = "axis-group x-axis" transform = "translate(0, -12)" fill = "none" font-size = "10" font-family = "sans-serif" text-anchor = "middle">
<path class = "domain" stroke = "#000" d = "M0.5,2V0.5H0.5V2"></path>
<g class = "tick" opacity = "1" transform = "translate(0.5,0)">
<line stroke = "#000" y2 = "2"></line>
<text fill = "#000" y = "5" dy = "0.71em">1970</text> <!-- <=== -->
</g>
<g class = "tick" opacity = "1" transform = "translate(0.5,0)">
<line stroke = "#000" y2 = "2"></line>
<text fill = "#000" y = "5" dy = "0.71em">.500</text>
</g>
<g class = "tick" opacity = "1" transform = "translate(0.5,0)">
<line stroke = "#000" y2 = "2"></line>
<text fill = "#000" y = "5" dy = "0.71em">:01</text>
</g>
<g class = "tick" opacity = "1" transform = "translate(0.5,0)">
<line stroke = "#000" y2 = "2"></line>
<text fill = "#000" y = "5" dy = "0.71em">.500</text>
</g>
Очевидно, есть много окольных способов, которыми я мог бы удалить или изменить это, от использования правила первого потомка CSS, чтобы скрыть его, до ручного выбора его в D3 и изменения его значения, но я хотел бы знать, можно ли это сделать в чистый способ использования API масштабирования / оси / формата / времени D3.
Могу ли я указать генератору тиков оси D3 игнорировать единицы времени, превышающие минуты? Я прошел через документы для шкал времени D3, но ничего не нашел. Думаю, можно было бы использовать Time.range(), но документы не дают мне много подсказок о том, как.
Обновлять: Похоже, мне нужно создать настраиваемую функцию для передачи в .tickFormat(), похожую на этот пример, но дублирующую средство форматирования времени по умолчанию с точкой отсечения, добавленной в условную логику. Итак, следующий вопрос: где мне найти логику формата времени D3 по умолчанию ...





Глядя на исходный код D3, кажется, что форматировщик умных отметок времени по умолчанию находится внутри частных функций внутри d3-scale/src/time.js. Похоже, что это полностью «бери или оставляй»: вы можете использовать его точно так же, как есть, или полностью заменить, передав пользовательскую функцию в .tickFormat().
Итак, вам нужно создать пользовательскую функцию, дублирующую нужную логику внутри экспорта d3-scale/src/time.jscalendar, и особенно вложенного function tickFormat(date).
В моем случае важной частью этого является форматирование метки времени 0 как особого случая, который возвращает 0, а не самое широкое возможное описание первой миллисекунды 1970 года. Итак, моя функция по существу:
xAxis.tickFormat((date) => {
// If this describes 0 milliseconds from the Epoch, return '0'
if (d3.timeFormat('%Q')(date) === '0') return '0'
// Then copy and paste as many lines as needed from `d3-scale/src/time.js`
// from the `function tickFormat` adding the definitions of the formatters
// like `formatMillisecond` etc from the `calendar` scope
})
Я попытался найти способ извлечь средство форматирования тиков по умолчанию, чтобы я мог просто выполнить проверку 0, а затем использовать значение по умолчанию, ничего не дублируя, но не было никакого способа получить к нему доступ.
Помимо кода в исходном коде D3, здесь есть демонстрация условных форматов времени в D3: https://bl.ocks.org/wboykinm/34627426d84f3242e0e6ecb2339e9065
Вы можете использовать .tickFormat () на своей оси с такой функцией форматирования:
function multiFormat(date) {
return (d3.timeSecond(date) < date ? formatMillisecond
: d3.timeMinute(date) < date ? formatSecond
: d3.timeHour(date) < date ? formatMinute
: d3.timeDay(date) < date ? formatHour
: d3.timeMonth(date) < date ? (d3.timeWeek(date) < date ? formatDay :
formatWeek)
: d3.timeYear(date) < date ? formatMonth
: formatYear)(date);
}
Замените "formatSomething" форматированием d3.timeFormat ("x"). Вы можете найти примеры здесь https://bl.ocks.org/zanarmstrong/raw/ca0adb7e426c12c06a95/.
Вот значения по умолчанию, поэтому вы можете просто скопировать их и изменить год, если хотите сохранить поведение по умолчанию:
%Y - for year boundaries, such as "2011".
%B - for month boundaries, such as "February".
%b %d - for week boundaries, such as "Feb 06".
%a %d - for day boundaries, such as "Mon 07".
%I %p - for hour boundaries, such as "01 AM".
%I:%M - for minute boundaries, such as "01:23".
:%S - for second boundaries, such as ":45".
.%L - milliseconds for all other times, such as ".012".