提取属性以便在 D3 转换中重用

Factoring out attributes for reuse in D3 transition

在下面的最小示例中,SVG 元素转换到不同的状态,然后恢复到原始状态。我怎样才能分解出原始属性,这样它们就不必重复了? selection.each()transition.each() 给了我意想不到的混乱结果。

var circle = d3.select('svg').append('circle')
  .attr('cx', 50) // this part should be factored out
  .attr('cy', 130)
  .attr('r', 25)
  .attr('fill', 'blue');

circle.transition().duration(1000).delay(500)
  .attr('cx', 200)
  .attr('cy', 50)
  .attr('r', 50)
  .attr('fill', 'red')
  .transition().duration(1000).delay(500)
  .attr('cx', 50) // repeated
  .attr('cy', 130)
  .attr('r', 25)
  .attr('fill', 'blue');
<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>

<svg width='300' height='170'>
</svg>

一个解决方案可能是利用 D3 data binding 的强大功能。您可以定义包含两者的配置对象,稍后要恢复的原始值以及元素应转换到的值。通过将此信息绑定到 D3 创建的 DOM 元素,您可以在以后的调用中访问它们。这些配置对象可能如下所示:

var circle = [{   // D3 binds data contained in arrays
  orig: {         // The original values
    "cx": 50,
    // ...
  },
  trans: {        // The values to transition to
    "cx": 200,
    // ...
  }
}];

投入 d3-selection-multi module you can directly use the above defined configuration objects by handing them to the selection.attrs() 方法:

函数的 return 值必须是具有字符串值的对象,然后用于设置当前元素的属性。

// Array of configuration objects
var circle = [{
  orig: {
    "cx": 50,
    "cy": 130,
    "r":  25,
    "fill": "red"
  },
  trans: {
    "cx": 200,
    "cy": 50,
    "r":  50,
    "fill": "blue"
  }
}];

// Create new elements by binding data and using data joins
var circle = d3.select('svg').selectAll('circle')
  .data(circle)                               // Bind configuration objects to elements
  .enter().append('circle')
    .attrs(function(d) { return d.orig; });   // Use the original values

circle.transition().duration(1000).delay(500)
    .attrs(function(d) { return d.trans; })   // Transition to new values
  .transition().duration(1000).delay(500)
    .attrs(function(d) { return d.orig; });   // And transition back to original values
<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>

<svg width='300' height='170'>
</svg>

我需要的是 call() 而不是 each()。我知道这是可能的!

function revert(selection) {
  selection
  .attr('cx', 50)
  .attr('cy', 130)
  .attr('r', 25)
  .attr('fill','blue');
}

var circle = d3.select('svg').append('circle')
  .call(revert);

circle.transition().duration(1000).delay(500)
  .attr('cx', 200)
  .attr('cy', 50)
  .attr('r', 50)
  .attr('fill','red')
  .transition().duration(1000).delay(500)
  .call(revert);
<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>

<svg width='300' height='170'>
</svg>