我正在尝试动画(转换)一个项目进入实时图表。
如您所见,单击“添加圆圈”按钮时,圆圈会停留在图表的底部,而不是向上设置动画。
有趣的是,在转换中添加.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/d3@4.4.1/build/d3.js"></script>
<button class="add-circle">Add circle</button>
<div class="chart"></div>
答案 0 :(得分:1)
这里的问题不是缺少easing。问题很可能是d3.timer
和selection.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
并不会导致过渡发生。
我使用您的代码测试了所有缓动,其中一些&#34;工作&#34;,如easeLinear
或easeCircle
。这里的工作是在引号之间,因为转换是不正常的,即预期的转换。其中一些,如easeCubic
,根本没有效果。
那是因为你不能将d3.timer
与过渡混合在一起。我没有机会看一下源代码,但很可能他们使用相同的方法(比如requestAnimationFrame()
)并且是冲突的。
解决方案:将转换移至d3.timer
调用的函数外部。
实施该提议的解决方案有不同的方法(不同的编码器将创建不同的替代方案),但这是我的。首先,我们定义一个转换函数:
function transitioning(elem) {
const t = d3.transition().duration(1000).ease(d3.easeCubic);
d3.select(elem)
.transition(t)
.attr('cy', height / 2);
}
然后,在您的函数drawCircles
中,我们为每个圈子调用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:http://codepen.io/anon/pen/ZLyXma?editors=0010
在上面的CodePen中,我使用easeCubic
。但是你可以在其他笔中看到,它可以在不设置任何宽松的情况下工作:http://codepen.io/anon/pen/apwLxR?editors=0010