如何在d3.scaleTime()轴刻度中禁用年份?

时间:2018-05-30 09:38:47

标签: d3.js

使用D3(v4,升级到v5也是一个选项),我正在绘制时间间隔的时间序列。数据的持续时间未知,通常是几秒钟,我们有一个开始日期戳和结束日期戳。

我目前正在做的生成x轴的方法是这样的,其中startTimeendTime是数字时间戳,widthheight是数字和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中手动选择它并修改它的值,但是我想知道它是否&# 39;可以做到  使用D3比例/轴/格式/时间API的干净方式。

我可以告诉D3的轴刻度线生成器忽略大于分钟的时间单位吗?我已经通过了the docs for D3 time scales但却找不到任何东西。我认为可以使用Time.range(), but the docs aren't giving me many clues on how

更新:看起来我需要创建一个自定义函数来传递给.tickFormat(),类似于this example,但是复制了一个带有截断点的默认时间格式化程序添加到条件逻辑。接下来的问题是:我在哪里可以找到默认的D3时间格式逻辑...

2 个答案:

答案 0 :(得分:1)

查看D3源代码,似乎默认的智能时间刻度格式化程序都在d3-scale/src/time.js内的私有函数内部。它看起来完全是接受或离开它:您可以完全按原样使用它,也可以通过将自定义函数传递给.tickFormat()来完全替换它。

因此,您需要创建自定义函数,在d3-scale/src/time.js calendar导出中复制所需的逻辑,尤其是嵌套的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

答案 1 :(得分:1)

您可以在轴上使用.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/找到示例。

这是默认值,所以如果你想保持默认行为,你可以复制thos并改变年份:

%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".