对 d3.interpolateObject 的困惑

Confusion about d3.interpolateObject

我刚刚调查 D3 Interpolate Object function,我注意到一些奇怪的行为。但是,我对 D3 不是很熟悉,所以可能我只是误解了一些东西。给定以下数据和插值函数:

var a = {"Country": "Ireland", "Year": 2010, "Data": 10};
var b = {"Country": "Ireland", "Year": 2015, "Data": 50};

var iFunc = d3.interpolateObject(a, b);

以下结果符合预期:

console.log(iFunc(0.2)) // Returns: { Country: "Ireland", Year: 2011, Data: 18 }
console.log(iFunc(0.4)) // Returns: { Country: "Ireland", Year: 2012, Data: 26 }

但是,当两个函数调用都包含在同一个控制台日志中时,如下所示:

console.log(iFunc(0.2), iFunc(0.4)) 

输出只是第二个对象两次:

{ Country: "Ireland", Year: 2012, Data: 26 } { Country: "Ireland", Year: 2012, Data: 26 }

并且,当函数调用像这样放在数组中时:

console.log([iFunc(0.2), iFunc(0.4)])

之前的输出乘以二:

[{ Country: "Ireland", Year: 2014, Data: 42 }, { Country: "Ireland", Year: 2014, Data: 42 }]

这是怎么回事?

我正在研究这个的原因是我想使用类似的东西创建一系列中间对象:

var iVals = d3.range(0, 1, 0.2).map( iFunc );

如果有人能告诉我如何实现这一点,我将不胜感激!

这是一个有趣的问题。解释可以在自己的documentation:

中找到

Note: no defensive copy of the template object is created; modifications of the returned object may adversely affect subsequent evaluation of the interpolator. No copy is made for performance reasons; interpolators are often part of the inner loop of animated transitions. (emphasis mine)

如您所见,如果您使用相同的插值器,您会得到您描述的奇怪结果(打开浏览器的控制台,不要使用代码段的控制台):

var a = {
  "Country": "Ireland",
  "Year": 2010,
  "Data": 10
};
var b = {
  "Country": "Ireland",
  "Year": 2015,
  "Data": 50
};

var iFunc = d3.interpolateObject(a, b);

var iVals = d3.range(0, 1, 0.2).map(iFunc);

console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>

因此,最简单的解决方案是在 map():

中定义插值器函数
var iVals = d3.range(0, 1, 0.2).map(function(d) {
  return d3.interpolateObject(a, b)(d)
});

这是演示:

var a = {
  "Country": "Ireland",
  "Year": 2010,
  "Data": 10
};
var b = {
  "Country": "Ireland",
  "Year": 2015,
  "Data": 50
};

var iVals = d3.range(0, 1, 0.2).map(function(d) {
  return d3.interpolateObject(a, b)(d)
});

console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>

或者,创建一个 returns 插值器的函数:

var iFunc = function(d) {
    return d3.interpolateObject(a, b)(d)
};

var iVals = d3.range(0, 1, 0.2).map(iFunc);

对应的demo如下:

var a = {
  "Country": "Ireland",
  "Year": 2010,
  "Data": 10
};
var b = {
  "Country": "Ireland",
  "Year": 2015,
  "Data": 50
};

var iFunc = function(d) {
  return d3.interpolateObject(a, b)(d)
};

var iVals = d3.range(0, 1, 0.2).map(iFunc);

console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>


PS:与你的问题无关,但d3.range()中的"stop"值不包含在内。所以,如果要获取对象b中的值,应该是:

d3.range(0, 1.2, 0.2)

这里是:

var a = {
  "Country": "Ireland",
  "Year": 2010,
  "Data": 10
};
var b = {
  "Country": "Ireland",
  "Year": 2015,
  "Data": 50
};

var iVals = d3.range(0, 1.2, 0.2).map(function(d) {
  return d3.interpolateObject(a, b)(d)
});

console.log(iVals)
<script src="https://d3js.org/d3.v5.min.js"></script>