我如何确定两个d3比例尺是否相等或者一个d3比例尺是否发生了改变?

4
我正在使用React和d3,并尝试找出如何在比例尺更改时正确触发渲染。然而,可靠地检测比例尺是否已更改相当困难。
理论上,我可以想到两种解决方法。一种是每次渲染时重新生成比例尺,然后有一种可靠的方式来确定两个比例尺是否相同(例如比较它们的域、范围等)。但仍然存在一些问题,例如,我不知道如何区分具有相同域和范围的线性比例尺和对数比例尺。(如果我尝试一个测试值-例如域的中点-如果给定非数字比例尺,它将崩溃。我可以通过“反向鸭式类型转换”所有的d3各种比例尺来避免这种情况,但这似乎非常繁琐,如果测试函数变得太复杂,则可能成为性能陷阱。)
另一种解决方案是在渲染过程中保持一致的比例尺对象,但从某种程度上让该比例尺在其任何属性更改时触发渲染。我认为这最终将导致与第一种选项相同的结果,但也许d3中有一些我不知道的技巧,使其更容易?

我认为对你的问题最好的答案取决于你是否在渲染方法中使用d3,还是在其他生命周期方法中使用d3选择器。 - Tom Fenech
在定义比例尺的位置使用一些实用变量来确定比例尺类型如何?例如 var isLinear = true; 并且每当您更改比例尺时修改它。 - mtx
1个回答

1

(警告:这是一种极其hacky的解决方案,用于了解您拥有哪种比例尺。如果您不熟悉,请告诉我,我会将其删除)

D3 v4.x中的每个比例尺都有一个copy函数,该函数公开比例尺的类型。例如,这是线性比例尺的copy函数:

scale.copy = function() {
    return copy(scale, linear());
};

这是一个乐队音阶的 复制 函数:
scale.copy = function() {
    return band()
        .domain(domain())
        .range(range)
        .round(round)
        .paddingInner(paddingInner)
        .paddingOuter(paddingOuter)
        .align(align);
};

以及其他类似的内容...

正如您所看到的,比例尺的类型始终存在于返回值中(如果您正在使用未压缩版本的D3)。

因此,我们可以使用这个来比较比例尺(除了您现在正在做的比较域和范围)。以下是一个例子:

var scale1 = d3.scaleBand();
  
var scale2 = d3.scaleLinear();
  
var scale3 = d3.scaleLinear();
  
console.log("Is scale1 the same of scale2? " + (scale1.copy.toString() === scale2.copy.toString()))
console.log("Is scale1 the same of scale3? " + (scale1.copy.toString() === scale3.copy.toString()))
console.log("Is scale2 the same of scale3? " + (scale2.copy.toString() === scale3.copy.toString()))
<script src="https://d3js.org/d3.v4.min.js"></script>

这可以用来测试具有相同域和范围的不同比例尺(正如您在问题中所问):

var scale1 = d3.scaleLinear()
  .domain([10, 20])
  .range([10, 20]);

var scale2 = d3.scaleLog()
  .domain([10, 20])
  .range([10, 20]);

console.log("Is scale1 the same of scale2? " + (scale1.copy.toString() === scale2.copy.toString()))
<script src="https://d3js.org/d3.v4.min.js"></script>

编辑:与其比较copy,最好比较整个函数。正如@altocumulus在评论部分中指出的那样,有些比例返回相同的copy


如果您正在使用缩小版本,则会出现此问题。对我来说,这感觉有点太“hacky”。 - altocumulus
@altocumulus 不,它不会出错,它与缩小版本的工作方式相同,因为函数是不同的,正如您在片段中所看到的那样。我知道这太过hacky了,极其确切地意味着达到最高程度。 - Gerardo Furtado
我的错,我只读了第一部分,以为你要的是字面上的名称。我走了一个类似的路径,没有涉及到比较.copy(),而是比较整个函数,即scale1.toString() === scale2.toString()。但这只是个人口味问题。 - altocumulus
是的,你可以使用 scale.toString() 或者 scale.copy.toString()。完全透明化:我使用了 copy 是因为我试图在 return 中提取比例尺名称... 然后,感到无聊和疲倦,我决定使用简单的 toString() 来编写这个答案。 - Gerardo Furtado
经过进一步的调查,我的发现是,您确实应该比较整个函数,因为 scalePowscaleSqrt 以及 scaleTimescaleUtc 共享相同的副本方法。 - altocumulus
@altocumulus 你说得对,我测试了其中一些但不是全部,这两个配对确实是相同的。 - Gerardo Furtado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接