我正在修改 this example有多行并更新它以使用 v4。 The code I wrote有效,但我的更新函数递归的方式特别不优雅:
function repeat() {
paths.each(d => d.push(random()));
paths.attr("d", line)
.attr("transform", null)
.transition()
.attr("transform", "translate(" + x(0) + ")")
.duration(750)
.ease(d3.easeLinear);
paths.each(d => d.shift());
d3.select({}).transition().duration(750).on('end', repeat);
}
请注意空选择的转换只是为了在调用之间创建 750 毫秒的超时。我想这样做:
function repeat() {
paths.each(d => d.push(random()));
paths.attr("d", line)
.attr("transform", null)
.transition()
.attr("transform", "translate(" + x(0) + ")")
.duration(750)
.ease(d3.easeLinear)
.on('end', repeat);
paths.each(d => d.shift());
}
但是,这会导致对
repeat
的多个并发调用。 , 选择中的每个元素一个。有没有办法可以简化我的代码以摆脱额外的转换?似乎必须有一种更清洁的方法。
最佳答案
D3 转换可能有点棘手,因为结束事件在每个元素转换结束时触发。没有transition.on("endAll", ...
,因此由于您不想在转换结束时触发重复函数两次,因此您使用空转换来延迟调用重复。
您可以使用计数器查看上次转换何时完成,但这也不太干净。最终,在管理具有单独作用于元素的转换的多个事件时,您将获得不太优雅的代码。
相反,由于 d3-transition 的每个元素的计时器和转换事件特定于该元素(即使您将它们设置为一个组,每个元素都有自己的计时器和事件),创建一个特定于每个元素的重复函数:
function scroll(d) {
d.push(Math.random());
// Transition
d3.select(this).attr("transform",null)
.attr("d",line)
.transition()
.attr("transform", "translate(" + x(0) + ")")
.duration(750)
.ease(d3.easeLinear)
.on("end",scroll); // repeat
d.shift();
}
并调用
selection.each(scroll);
现在我们正在使用一个函数管理每个转换(分别处理选择中的每个项目),该函数一次处理选择中的一个项目。这允许您:
d
如果您使用 .each()
调用此函数,则直接(而不是在函数中使用 paths.each(...
两次),其他注意事项:
this
这是在工作:
let n = 40;
let random = d3.randomUniform(-1, 1);
let data = [d3.range(n).map(random), d3.range(n).map(random)];
let margin = {top: 6, right: 0, bottom: 6, left: 40};
let width = 960 - margin.right;
let height = 120 - margin.top - margin.bottom;
let x = d3.scaleLinear()
.domain([1, n - 2])
.range([0, width]);
let y = d3.scaleLinear()
.domain([-1, 1])
.range([height, 0]);
let line = d3.line()
.x(function(d, i) { return x(i); })
.y(function(d, i) { return y(d); })
.curve(d3.curveBasis);
let svg = d3.select("body").append("p").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", -margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).ticks(5));
let paths = svg.append('g')
.attr('id', 'lines')
.attr('clip-path', 'url(#clip)')
.selectAll('path').data(data).enter()
.append('path')
.attr('class', 'line')
.attr('stroke', (d, i) => d3.schemeCategory10[i])
.attr('d', line)
.each(scroll);
function scroll(d) {
d.push(random());
d3.select(this).attr("transform",null)
.attr("d",line)
.transition()
.attr("transform", "translate(" + x(0) + ")")
.duration(750)
.ease(d3.easeLinear)
.on("end",scroll);
d.shift();
}
#lines {
fill: none;
stroke: black;
stroke-width: 1.5px;
}
.y.axis path {
stroke: black;
}
p {
padding: 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
关于d3.js - 多个转换的单一事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51087514/