你的第二个问题...
- 多重转换如何运作?
回答起来很简单。到这里,API就很清楚了:
过渡.transition()
返回与此转换相同的选定元素上的新转换,计划在此转换结束时开始。
因此,新的过渡将在前一个过渡完成后开始,依此类推。让我们来看看它:
var rect = d3.select("svg").append("rect")
.attr("width", 100)
.attr("height", 0)
.style("fill", "black")
.transition()
.duration(1000)
.attr("height", 100)
.transition()
.duration(1000)
.style("fill", "teal")
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
有趣的问题
然而,这里有趣的问题是你的第一个问题:
- 转换是否影响转换之前的任何语句,还是转换仅影响转换之后的语句?
嗯,这也是非常基本的:transition()
方法创建起始值和目标值之间的转换。您必须在转换之前(起始值)和转换之后(目标值)设置属性。
问题是先前未设置的属性将具有null
value.
让我们看看:没有指定height
的矩形,它是null
:
var rect = d3.select("svg").append("rect");
console.log(rect.attr("height"))
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
因此,如果在转换之前没有设置高度,则高度不会平滑转换:
var rect = d3.select("svg").append("rect")
.attr("width", 100)
.transition()
.duration(1000)
.attr("height", 100);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
有趣的一点是:我写了一个答案(现已删除,>10k 的用户可以看到)解释插值器无法从null
到给定值。然而,它can插:
var interpolator = d3.interpolateNumber(null, 100);
d3.range(0,1.1,.1).forEach(function(d){
console.log(interpolator(d))
});
<script src="https://d3js.org/d3.v4.min.js"></script>
所以,这里发生的事情有点复杂。使用interpolateNumber
我们可以从中插值null
到给定值。看看这个演示,其中height
未设置:
var rect = d3.select("svg").append("rect")
.attr("width", 100)
.transition()
.duration(1000)
.attrTween("height", function() {
return d3.interpolateNumber(d3.select(this).attr("height"), 100);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
为什么默认transition.attr()
当您在转换之前未设置属性时不起作用?
经过一番调查后,我认为transition()
正在将数字目标值转换为字符串(例如100
to "100"
)。这很奇怪,因为API 明确表示 https://github.com/d3/d3-transition#transition_attr that...
...使用以下算法根据目标值的类型选择插值器:
- 如果值是数字,则使用
interpolateNumber
.
- 如果 value 是颜色或可强制转换为颜色的字符串,请使用
interpolateRgb
.
- Use
interpolateString
.
这里是源代码 https://github.com/d3/d3-transition/blob/master/src/transition/attr.js#L73,看最后一行:
export default function(name, value) {
var fullname = namespace(name),
i = fullname === "transform" ? interpolateTransform : interpolate;
return this.attrTween(name, typeof value === "function"
? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value))
: value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)
: (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + ""));
//converting to string here ------------------------------------------^
}
正如我们所看到的,它将值强制转换为字符串。因此,转换将不起作用,因为它将使用interpolateString
代替interpolateNumber
:
var interpolator = d3.interpolateString(null, "100");
d3.range(0,1.1,.1).forEach(function(d){
console.log(interpolator(d))
});
<script src="https://d3js.org/d3.v4.min.js"></script>
结论
transition()
创建从起始值到目标值的转换。如果没有设置起始值,则默认为null
。然而,作为transition()
uses interpolateString
,插值将无法正常工作。
更新于 18/11/2017
根据 Mike Bostock(D3 创建者)的说法,源代码是正确的(因此,文档是错误的)。正如他在这篇文章中所说GitHub问题 https://github.com/d3/d3-transition/pull/67:
interpolateString
(or interpolateRgb
) 是这里的正确行为。interpolateNumber
永远不应该用于属性。
解释是像这样的值"100px"
or "1.3em"
不会与interpolateNumber
。所以,再次强调,不要依赖null
起始值:始终在之前和之后设置属性transition()
.
感谢 @altocumulus 对 GitHub 问题的评论。