在D3转换中获得预期的属性值

Getting expected attribute value in D3 transition

本文关键字:属性 D3 转换      更新时间:2023-09-26

例如,我有一个过渡:

var sel = container.selectAll('div')
    .transition()
    .duration(1000)
    .attr('transform', 'translate(100,500)');

有时我需要知道一些元素落在哪里,例如

setTimeout(() => {
    var value = d3.select('div#target')
        .expectedAttr('transform');
    assertEqual(value, 'translate(100,500)');
}, 500);

在D3中有这样的内置功能吗?否则,我将不得不在d3.transition().attr()方法上编写我自己的包装器来存储传递给它的值。

编辑

我发现D3在元素上创建__transition__字段,这似乎包含有关过渡的信息,但我看不到在那里找到目标属性值的方法。

起初我认为这是不可能的,因为目标值似乎被闭包隐藏了。但是,使用一个小技巧,可以检索该值。

您必须记住,当调用transition.attr()时,D3将执行以下操作:

对于每个选定的元素,为具有指定名称的属性创建一个属性间的属性到指定的目标值。

这个自动创建的补间可以通过调用transition.attrTween(attrName)来访问。

当这个渐变被D3调用时,它将返回一个插值器。这反过来又可以访问在创建插值器时关闭的目标值。当深入阅读文档时,真正的技巧变得显而易见:

返回的插值器然后被调用的每一帧过渡,按顺序,被传递缓和时间t,通常在范围[0,1]。

知道t的最终值& & &;在过渡结束时& &;如果1,你可以用这个值调用之前得到的插值器,它将产生过渡的目标值。

var targetValue = transition 
  .attrTween("x2")            // Get the tween for the desired attribute
  .call(line.node())          // Call the tween to get the interpolator
  (1);                        // Call the interpolator with 1 to get the target value

下面的示例通过打印已经运行的转换的目标值来显示这一点。

var line = d3.select("line");
line
  .transition()
  .duration(2000)
  .attr("x2", 100);
  
setTimeout(function() {
  var transition = d3.active(line.node())  // Get the active transition on the line
  var targetValue = transition 
    .attrTween("x2")                       // Get the tween for the desired attribute
    .call(line.node())                     // Call the tween to get the interpolator
    (1);                                   // Call the interpolator with 1 to get the target value
  console.log(targetValue);                // 100
}, 1000);
<script src="https://d3js.org/d3.v4.js"></script>
<svg><line x2="0" y2="100" stroke="black"></line></svg>

同样适用于样式过渡,你可以使用transition.styleTween()来获得渐变

今天碰到这个问题,发现高积云的答案很有帮助。然而,我发现(至少对于当前d3-transition@2.0.0),如果当前值是已经在目标值,.callattrTween将返回null而不是插值器。

我的解决方案看起来像这样:

const attrTargetValue = (selection, attr) => {
  const interpolator = selection
    .attrTween(attr)
    .call(selection.node());
  return interpolator === null
    ? selection.selection().attr(attr)
    : interpolator(1);
}