2017-01-23 78 views
1

我正在尝试动画(与转换)一个项目进入实时图表。如何在d3中使用与计时器的转换?

正如你所看到的,当点击“添加圆”按钮时,圆将停留在图表的底部,而不是向上动画。

有趣的是,在转换中添加.ease(d3.easeLinear)可解决问题。

为什么?

在d3中使用与计时器的转换在概念上是错误的吗?

const width = 300; 
 
const height = 100; 
 
const margin = { top: 0, right: 30, bottom: 30, left: 50 }; 
 

 
const main = d3.select('.chart') 
 
    .append('svg') 
 
    .attr('width', width + margin.left + margin.right) 
 
    .attr('height', height + margin.top + margin.bottom) 
 
    .append('g') 
 
    .attr('transform', `translate(${margin.left}, ${margin.top})`); 
 

 
const now = Date.now(); 
 
const xScale = d3.scaleTime().domain([now - 10000, now]).range([0, width]); 
 
const xAxis = d3.axisBottom(xScale).ticks(5); 
 

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

 
let data = []; 
 

 
function drawCircles() { 
 
    const t = d3.transition().duration(1000); //.ease(d3.easeLinear); 
 
    const update = main.selectAll('circle').data(data); 
 
    
 
    update.enter() 
 
     .append('circle') 
 
     .attr('r', d => d.value) 
 
    .merge(update) 
 
     .attr('cx', d => xScale(d.timestamp)) 
 
     .attr('cy', height) 
 
     .transition(t) 
 
     .attr('cy', height/2); 
 
} 
 

 
d3.timer(() => { 
 
    const now = Date.now(); 
 

 
    xScale.domain([now - 10000, now]); 
 
    xAxisG.call(xAxis); 
 
    
 
    drawCircles(); 
 
}); 
 

 
d3.select('.add-circle').on('click',() => { 
 
    data.push({ 
 
    timestamp: Date.now() - 3000, 
 
    value: 10 
 
    }); 
 
});
svg { 
 
    margin: 30px; 
 
} 
 
.add-circle { 
 
    margin-left: 200px; 
 
}
<script src="https://unpkg.com/[email protected]/build/d3.js"></script> 
 
<button class="add-circle">Add circle</button> 
 
<div class="chart"></div>

回答

1

这里的问题不是缺乏easing的。问题很可能是d3.timerselection.transition的混合。

让我们一步一步看。你说,这:

const t = d3.transition().duration(1000); 

不行的,而这一点:

const t = d3.transition().duration(1000).ease(d3.easeLinear); 

作品。那么,这不是事实:它不工作。谁使用easeLinear可以很容易地看到,这不是一个线性过渡!有趣的事情发生在这里...

现在,让我们改变缓动。默认缓动(即,如果你没有设置)是easeCubic。因此,如果我们这样做:

const t = d3.transition().duration(1000).ease(d3.easeCubic); 

它会有与您的第一个代码相同的效果,没有ease。因此,我们设置了一个ease这个事实并不是事实。

我用你的代码测试了所有的缓动,其中一些“工作”,如easeLineareaseCircle工作这里是引号之间,因为过渡是不是正常的一个,预计之一。其中一些,如easeCubic,根本没有效果。

而这是因为你不能混合d3.timer与转换。我没有机会看看源代码,但很可能他们使用相同的方法(如requestAnimationFrame()也许)并且相互冲突。

解决方案:将转换移到您的d3.timer所调用的函数之外。

实现该提议的解决方案有不同的方法(不同的编码器会创建不同的替代方案),但这是我的。首先,我们定义一个过渡功能:你的函数里面drawCircles

function transitioning(elem) { 
    const t = d3.transition().duration(1000).ease(d3.easeCubic); 
    d3.select(elem) 
     .transition(t) 
     .attr('cy', height/2); 
} 

,然后,我们称之为transitioning每个圆圈。然而,我们必须设置一个标志,所以我们不会一次又一次地调用transitioning功能相同的元素:

function drawCircles() { 

    const update = main.selectAll('circle').data(data); 

    update.enter() 
     .append('circle') 
     .attr('r', d => d.value) 
     .attr('cy', height) 
     .attr("flag", 0) 
     .merge(update) 
     .attr('cx', d => xScale(d.timestamp)) 
     .each(function() { 
      if (!(+d3.select(this).attr("flag"))) { 
       transitioning(this) 
      } 
      d3.select(this).attr("flag", 1); 
     }); 
} 

这是你更新CodePen:​​

在上面CodePen,我正在使用easeCubic。但你可以看到,在这另一支笔,它没有设置任何缓动工作:http://codepen.io/anon/pen/apwLxR?editors=0010

+1

@Misha,赫拉尔多:大哥哥在看我们;-)看看迈克·博斯托克的块[*我做圈*]( http://bl.ocks.org/mbostock/ad550c9d6d156ac726b45f48fa6ff2c7),它不完全回答* this *问题,但似乎有点奇怪地相关... – altocumulus

+0

Mike创建了此示例作为对以下内容的回复:https://github.com/ d3/d3-transition/issues/65 –

+0

*“感谢Mike,这比以前的回复更有帮助”*。我只希望你在谈论他*以前的回应...... –