Есть множество способов добиться этого.Как отмечено в другом ответе, использование функции для этого сделает ваш код компактным.Лично я склонен использовать конечное событие перехода для запуска следующего перехода из функции перехода.
Общая форма для такого рода функции следующая:
function transition() {
// .... optional logic here.
d3.select(this) // do transition:
.transition()
.attr("fill", ... )
.on("end", transition); // and repeat.
}
Это может бытьвызывается с selection.each(transition)
Один из подходов к управлению текущим цветом / переходом в цикле заключается в использовании пользовательского атрибута.Ниже я использую .attr("i")
для отслеживания:
var data = [
["orange","purple","blue","yellow"],
["blue","yellow","orange","purple"]
];
var svg = d3.select("svg");
var circles = svg.selectAll()
.data(data)
.enter()
.append("circle")
.attr("r", 20)
.attr("cx", function(d,i) { return i * 50 + 50; })
.attr("cy", 50)
.attr("fill", function(d) { return d[0]; })
.attr("i",0)
.each(transition);
// cycle endlessly:
function transition() {
var selection = d3.select(this);
// keep track of current value:
var i = selection.attr("i")
selection
.attr("i",i = ++i%4)
.transition()
.duration(1000)
.attr("fill", function(d) { return d[i] })
.on("end", transition);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Если у вас есть переходы, которые отличаются только по порядку (начальная точка), вы можете немного изменить вышеуказанный подход:
var data = [50,75,100,125,150,175,200,225];
var colors = ["orange","purple","blue","yellow"];
var svg = d3.select("svg");
var circles = svg.selectAll()
.data(data)
.enter()
.append("circle")
.attr("r", 20)
.attr("cx", function(d) { return d; })
.attr("cy", 50)
.attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
.attr("i", function(d,i) { return i%4; }) // record start position.
.each(transition);
function transition() {
var selection = d3.select(this);
var i = selection.attr("i");
i = ++i%colors.length;
selection.transition()
.duration(1000)
.attr("i",i)
.attr("fill", colors[i])
.on("end", transition);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Первый - я использую наборы цветов в качестве базовых данных, второй - просто положение, получающее доступ к цвету с атрибутом i
Вышеуказанные методы используют пользовательский атрибут, но мы также можем использовать данные.Это позволяет нам немного лучше познакомиться с D3.
Например, у нас может быть свойство с именем color и increment, которое при настройке заливки для каждого перехода (опять же, при условии, что у нас есть массив цветов, и каждый круг начинаетсяв другом месте):
function transition() {
d3.select(this).transition()
.duration(1000)
.attr("fill", function(d) { return colors[++d.color%colors.length]; })
.on("end", transition);
}
var data = d3.range(8).map(function(d) { return {x: d*25+50}; })
var colors = ["orange","purple","blue","yellow"];
var svg = d3.select("svg");
var circles = svg.selectAll()
.data(data)
.enter()
.append("circle")
.attr("r", 20)
.attr("cx", function(d) { return d.x; })
.attr("cy", 50)
.attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
.each(function(d,i) { d.color = i%4; }) // record start position.
.each(transition);
function transition() {
d3.select(this).transition()
.duration(1000)
.attr("fill", function(d) { return colors[++d.color%colors.length]; })
.on("end", transition);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Если вы хотите, чтобы переходы повторялись x раз или даже заканчивались на одном и том же цвете, мы можем сделать это довольно легко, добавив новое свойствок исходным данным для отслеживания завершенных циклов:
var data = d3.range(8).map(function(d) { return {x: d*25+50}; })
var colors = ["orange","purple","blue","yellow"];
var svg = d3.select("svg");
var circles = svg.selectAll()
.data(data)
.enter()
.append("circle")
.attr("r", 20)
.attr("cx", function(d) { return d.x; })
.attr("cy", 50)
.attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
.each(function(d,i) { d.color = d.start = i%4; }) // record start position.
.each(transition);
var n = 8; // n cycles
function transition() {
d3.select(this).transition()
.duration(1000)
.attr("fill", function(d) { return colors[++d.color%colors.length]; })
.on("end", function(d) { if(d.color - d.start < n) transition.apply(this); else return null; });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Есть более ленивые способы, которые деструктивно изменяют данные (или их части), такие как использование сдвига в этом цикле перехода, который может выполняться только один раз:
var data = [
["orange","purple","blue","yellow"],
["blue","yellow","orange","purple"]
];
var svg = d3.select("svg");
var circles = svg.selectAll()
.data(data)
.enter()
.append("circle")
.attr("r", 20)
.attr("cx", function(d,i) { return i * 50 + 50; })
.attr("cy", 50)
.attr("fill", function(d) { return d.shift(); })
.each(transition);
function transition() {
var selection = d3.select(this);
if(selection.datum().length) {
selection.transition()
.duration(1000)
.attr("fill", function(d) { return d.shift() })
.on("end", transition);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Вы можете заметить, что ни один из моих фрагментов не использует дочерние селекторы: этого можно избежать с помощью пользовательских атрибутов, свойств данных или, проще, используя .attr("fill",function(d,i){
всам переход для дифференциации нечетных и четных элементов.Я ничего не имею против этих селекторов, просто требуется дополнительный выбор.