如何修复d3 v4中的重播过渡?

时间:2017-01-16 00:51:19

标签: d3.js

我正在使用具有滑动x轴的d3实现图表。 Demo

问题是,当我换到另一个标签,然后返回(比如10秒后),d3似乎试图重放丢失的过渡,这导致轴的非常尴尬的行为。请参阅here

迈克博斯托克mentions that

  

D3 4.0通过更改时间定义来解决此问题。转换通常不需要与绝对时间同步;转换主要是用于跨视图跟踪对象的感知辅助工具。因此,D3 4.0在感知时间上运行,仅在页面位于前景时才进行。当一个标签背景并返回到前台时,它就会发现它没有发生任何事情。

这真的是固定的吗?我做错了吗?

const timeWindow = 10000;
const transitionDuration = 3000;

const xScaleDomain = (now = new Date()) =>
  [now - timeWindow, now];

const totalWidth = 500;
const totalHeight = 200;
const margin = {
  top: 30,
  right: 50,
  bottom: 30,
  left: 50
};
const width = totalWidth - margin.left - margin.right;
const height = totalHeight - margin.top - margin.bottom;

const svg = d3.select('.chart')
  .append('svg')
    .attr('width', totalWidth)
    .attr('height', totalHeight)
  .append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`)

svg
  .append('rect')
    .attr('width', width)
    .attr('height', height);

// Add x axis
const xScale = d3.scaleTime()
  .domain(xScaleDomain(new Date() - transitionDuration))
  .range([0, width]);

const xAxis = d3.axisBottom(xScale);

const xAxisSelection = svg
  .append('g')
    .attr('transform', `translate(0, ${height})`)
  .call(xAxis);

// Animate
const animate = () => {
  xScale.domain(xScaleDomain());

  xAxisSelection
    .transition()
    .duration(transitionDuration)
    .ease(d3.easeLinear)
    .call(xAxis)
    .on('end', animate);
};

animate();
svg {
  margin: 30px;
  background-color: #ccc;
}

rect {
  fill: #fff;
  outline: 1px dashed #ddd;
}
<script src="https://unpkg.com/d3@4.4.1/build/d3.js"></script>
<div class="chart"></div>

1 个答案:

答案 0 :(得分:1)

问题不在于D3过渡。这里的问题是new Date()

每次转到另一个标签页时,转换都会暂停。到现在为止还挺好。但是当你回到图表时,让我们说,在20秒之后,你得到一个新的日期,即当前日期......但是你的timeWindow和你的transitionDuration是一样的:< / p>

const timeWindow = 10000;
const transitionDuration = 3000;

const xScaleDomain = (now = new Date()) => [now - timeWindow, now];

这使得轴向前跳得更快,因为域中任何一点的旧值和新值之间的差异不再是3秒。

这是一个非常简单的解决方案,太粗糙,需要改进,只是为了向您展示问题是new Date()。在这个解决方案中(再次,远非完美),我手动设置每个动画中的日期跳跃10秒,无论你停留在另一个标签中多久:

var t = xScale.domain()[1];
t.setSeconds(t.getSeconds() + 10);

xScale.domain([xScale.domain()[1], t]);

以下是CodePen:http://codepen.io/anon/pen/GrjMxy?editors=0010

使用您的代码的更好的解决方案是更改timeWindowtransitionDuration以考虑新new Date()和旧new Date()之间的差异(即,用户在另一个标签中的时间长度。)