D3 V4 使用常规更新模式和合并输入元素时过渡



据我所知,不可能在标准输入/追加/合并链中的输入元素上包含过渡,因为这样做会将输入元素选择替换为无法与更新选择合并的过渡。(有关选择和过渡之间的区别,请参阅此处)。

(问题编辑以回应评论)

如果所需的效果是序列转换,一个在合并之前,一个在合并之后,可以按如下方式完成:

// Join data, store update selection      
circ = svg.selectAll("circle")
.data(dataset);
// Add new circle and store entering circle selection        
var newcirc = circ.enter().append("circle")
*attributes*
// Entering circle transition        
newcirc    
.transition()
.duration(1000)
*modify attributes*
.on("end", function () {
// Merge entering circle with existing circles, transition all        
circ = newcirc.merge(circ)
.transition()
.duration(1000)
*modify attributes*
});

斯菲德尔

我想知道是否有办法在不破坏进入/追加/合并链的情况下做到这一点。

毫无疑问,您必须在方法链接中至少有一个中断,因为您需要保留对更新选择的引用,以便以后能够将其合并到输入选择中。如果您对此感到满意,有一种方法可以在最初的断裂后保持链条完好无损。

我在回答">你能在转换后链接一个函数而不让它成为转换的一部分吗?这使用transition.selection(),允许您摆脱当前过渡并访问开始过渡的基础选择。但是,您的代码更复杂,因为链式转换增加了复杂性。

第一部分是像以前一样存储更新选择:

// Join data, store update selection      
const circUpd = svg.selectAll("circle")
.data(dataset);

第二个不间断的部分是这样的:

const circ = circUpd              // 2. Store merged selection from 1.
.enter().append("circle")
// .attr()...
.transition()
// .duration(1000)
// .attr()...
.on("end", function () {
circ.transition()           // 3. Use merged selection from 2.
// .duration(1000)
// .attr()...
})
.selection().merge(circUpd);  // 1. Merge stored update into enter selection.

这可能需要对上述编号步骤进行一些进一步的解释:

  1. 最后一行是最重要的一行 — 在开始转换后,代码使用.selection()来获取转换所基于的选择,即输入选择,这反过来又可用于轻松地将存储的更新选择合并到其中。

  2. 包含输入和更新选择的合并选择是整个链的结果,然后存储在circ中。

  3. 这是棘手的部分!重要的是要理解,提供给.on("end", function() {...})的函数是在转换结束之前不执行的回调。尽管此行是在合并选择之前出现的,但它实际上是在合并之后执行的。但是,通过引用circ,它会关闭(如果您愿意,可以捕获)对circ的引用。这样,当实际执行回调时,circ将已经引用先前合并的选择。

请查看以下工作片段:

var w = 250;
var h = 250;

// Create SVG          
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Create background rectangle          
svg.append("rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", w)
.attr("height", h)
.attr("fill", "aliceblue");
var dataset = [170, 220, 40, 120, 0, 300];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, w]);

var yScale = d3.scaleLinear()
.domain([0, 300])
.range([75, 200])

var rad = xScale.bandwidth()/2

// Join data  
var circ = svg.selectAll("circle")
.data(dataset);
// Create initial circles
circ.enter().append("circle")
.attr("cx", (d, i) => xScale(i)+rad)
.attr("cy", d => yScale(d))
.attr("r", rad)
.attr("fill", "blue");
// Trigger update on click
d3.select("h3")
.on("click", function () {
// Create new data value        
var newvalue = Math.floor(Math.random()*300);

dataset.push(newvalue);
xScale.domain(d3.range(dataset.length));

rad = xScale.bandwidth()/2;

// Join data, store update selection      
const circUpd = svg.selectAll("circle")
.data(dataset);

// Add new circle and store entering circle selection        
const circ = circUpd              // 2. Store merged selection from 1.
.enter().append("circle")
.attr("cx", "0")
.attr("cy", "25")
.attr("r", rad)
.attr("fill", "red")
.transition()
.duration(1000)
.attr("cx", (d, i) => xScale(i)+rad)
.on("end", function () {
circ.transition()           // 3. Use merged selection from 2.
.duration(1000)
.attr("cx", (d, i) => xScale(i)+rad)
.attr("cy", d => yScale(d))
.attr("r", rad)
.attr("fill", "blue");
	    })
.selection().merge(circUpd);  // 1. Merge stored update into enter selection.
});
<script src="https://d3js.org/d3.v4.js"></script>
<h3>Add a circle</h3>

最新更新