Флеш d3 v4 переход
Кто-нибудь знает способ "очистить" переход. У меня есть переход, определенный следующим образом:
this.paths.attr('transform', null)
.transition()
.duration(this.duration)
.ease(d3.easeLinear)
.attr('transform', 'translate(' + this.xScale(translationX) + ', 0)')
Я знаю, что могу сделать
this.paths.interrupt();
чтобы остановить переход, но это не заканчивает мою анимацию. Я хотел бы иметь возможность "очистить" переход, который немедленно завершит анимацию.
2 ответа
Если я правильно понимаю (и мог бы этого не делать), для этого не существует готового решения, если не пойти немного под капот. Тем не менее, я считаю, что вы могли бы создать функциональность относительно простым способом, если selection.interrupt()
имеет форму, которую вы ищете.
Для этого вам нужно будет создать новый метод для выбора d3, который обращается к данным перехода (расположен по адресу: selection.node().__transition
). Данные перехода включают в себя данные о подростках, таймере и других деталях перехода, но наиболее простым решением было бы установить длительность на ноль, что заставит переход завершиться и перевести его в конечное состояние:
Переменная данных __transition может иметь пустые слоты (с переменным числом), что может вызвать горе в Firefox (насколько я знаю, при использовании циклов forEach), поэтому я использовал подход с ключами, чтобы получить непустые значения. слот, который содержит переход.
d3.selection.prototype.finish = function() {
var slots = this.node().__transition;
var keys = Object.keys(slots);
keys.forEach(function(d,i) {
if(slots[d]) slots[d].duration = 0;
})
}
Если вы работаете с задержками, вы также можете вызвать обратный вызов таймера с помощью чего-то вроде: if(slots[d]) slots[d].timer._call();
, так как установка задержки на ноль не влияет на переход.
Используя этот блок кода вы звоните selection.finish()
который заставит переход в его конечное состояние, щелкните по кругу, чтобы вызвать метод:
d3.selection.prototype.finish = function() {
var slots = this.node().__transition;
var keys = Object.keys(slots);
keys.forEach(function(d,i) {
if(slots[d]) slots[d].timer._call();
})
}
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500);
var circle = svg.selectAll("circle")
.data([1,2,3,4,5,6,7,8])
.enter()
.append("circle")
.attr("cx",50)
.attr("cy",function(d) { return d * 50 })
.attr("r",20)
.on("click", function() { d3.select(this).finish() })
circle
.transition()
.delay(function(d) { return d * 500; })
.duration(function(d) { return d* 5000; })
.attr("cx", 460)
.on("end", function() {
d3.select(this).attr("fill","steelblue"); // to visualize end event
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.0/d3.min.js"></script>
Конечно, если вы хотите сохранить метод d3-ish, верните выделение, чтобы после него можно было включить дополнительные методы. А для полноты вы захотите убедиться, что есть переход к финишу. С этими дополнениями новый метод может выглядеть примерно так:
d3.selection.prototype.finish = function() {
// check if there is a transition to finish:
if (this.node().__transition) {
// if there is transition data in any slot in the transition array, call the timer callback:
var slots = this.node().__transition;
var keys = Object.keys(slots);
keys.forEach(function(d,i) {
if(slots[d]) slots[d].timer._call();
})
}
// return the selection:
return this;
}
Вот пример этой более полной реализации.
Выше для версий 4 и 5 D3. Воспроизвести это в версии 3 немного сложнее, так как таймеры и переходы были немного переработаны для версии 4. В третьей версии они немного менее дружественны, но поведение может быть достигнуто с небольшими изменениями. Для полноты, вот блок примера d3v3.
Эндрю ответ великий. Однако ради любопытства я считаю, что это можно сделать без расширения прототипов, используя .on("interrupt"
как слушатель.
Здесь я беззастенчиво копирую код Эндрю для переходов и этот ответ для получения целевого атрибута.
selection.on("click", function() {
d3.select(this).interrupt()
})
transition.on("interrupt", function() {
var elem = this;
var targetValue = d3.active(this)
.attrTween("cx")
.call(this)(1);
d3.select(this).attr("cx", targetValue)
})
Вот демо:
var svg = d3.select("svg")
var circle = svg.selectAll("circle")
.data([1, 2, 3, 4, 5, 6, 7, 8])
.enter()
.append("circle")
.attr("cx", 50)
.attr("cy", function(d) {
return d * 50
})
.attr("r", 20)
.on("click", function() {
d3.select(this).interrupt()
})
circle
.transition()
.delay(function(d) {
return d * 500;
})
.duration(function(d) {
return d * 5000;
})
.attr("cx", 460)
.on("interrupt", function() {
var elem = this;
var targetValue = d3.active(this)
.attrTween("cx")
.call(this)(1);
d3.select(this).attr("cx", targetValue)
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="500"></svg>
PS: в отличие от ответа Эндрю, так как я использую d3.active(node)
здесь щелчок работает, только если переход уже начался.