Как убедиться, что серия billboard.js завершила все переходы в onrendered?
Я пытаюсь добавить некоторые элементы в диаграмму, и я делаю это в onrendered
функция передана billboard.js
конфигурации.
В этой функции я должен знать, есть ли некоторые серии на графике, и это можно сделать, легко проверяя их непрозрачность. Теперь, в приведенном ниже примере, вы можете видеть, что иногда переходы непрозрачности не выполняются, когда код в onrendered
пожары.
Как убедиться, что происходит в onrendered
это после того, как все серии показаны / скрыты от графика? Так что после всех переходов закончились.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.min.css" />
<script src="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.pkgd.min.js"></script>
<title>JS Bin</title>
</head>
<body>
<div id="chart"></div>
<script>
console.log('should be d1=1,d2=1')
var c = bb.generate({
bindto: "#chart",
size: {
width: 500,
height: 250
},
transition: 0,
data: {
x: "x",
columns: [
["x", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06"],
["data1", 30, 200, 100, 400, 150, 250],
["data2", 130, 340, 200, 500, 250, 350],
]
},
axis: {
x: {
type: "timeseries",
tick: {
format: "%Y-%m-%d"
}
}
},
onrendered: function() {
var data1op = d3.select(".bb-chart-line.bb-target.bb-target-data1").style("opacity");
var data2op = d3.select(".bb-chart-line.bb-target.bb-target-data2").style("opacity");
console.log(`data1 op: ${data1op} --- data2 op: ${data2op}`);
}
});
setTimeout(function(){console.log('should be d1=0,d2=1');c.hide('data1')},500);
setTimeout(function(){console.log('should be d1=0,d2=0');c.hide('data2')},1000);
setTimeout(function(){console.log('should be d1=1,d2=0');c.show('data1')},1500);
setTimeout(function(){console.log('should be d1=1,d2=1');c.show('data2')},2000);
setTimeout(function(){console.log('more waiting between hide/show')},3500);
setTimeout(function(){console.log('should be d1=0,d2=1');c.hide('data1')},5000);
setTimeout(function(){console.log('should be d1=0,d2=0');c.hide('data2')},6000);
setTimeout(function(){console.log('should be d1=1,d2=0');c.show('data1')},7000);
setTimeout(function(){console.log('should be d1=1,d2=1');c.show('data2')},8000);
</script>
</body>
</html>
1 ответ
Проблема в том, что onrendered
называется, когда все svg
манипуляции сделаны. Но переходы еще должны начаться. Вы должны ждать, пока переходы не будут закончены.
Наивный способ - получить transition
после звонка c.hide
или же c.show
но это еще не прикреплено. Вы должны подождать, пока не будет выполнена первая обработка на холостом ходу и не будет обработан первый тик перехода. Вы можете сделать это, используя тайм-аут в несколько миллисекунд, а затем использовать transition
и подключить к нему продолжение и дождаться начала его вызова функции, которая будет обработана после завершения перехода. Это делается с помощью следующей функции.
function atEndOf(dataN, action) {
setTimeout(function() {
var transition = d3.active(d3.select(".bb-chart-line.bb-target.bb-target-"+dataN).node());
if (!transition) { return; } // no transition running
transition.transition().on("start", action);
}, 10);
}
Чтобы узнать, когда переходы скрытия и показа сделаны, вы можете создать atEndOf
цепь
// wait till main animation is finished
setTimeout(function() {
c.hide('data1');
atEndOf('data1', function () {
console.log('should be d1=0,d2=1');
showOpacity();
c.hide('data2');
atEndOf('data2', function () {
console.log('should be d1=0,d2=0');
showOpacity();
});
});
}, 6000);
Я не знаю почему, но этот трюк не работает для основного начального перехода. Я получаю ошибку "слишком поздно: уже началось"
Это использует следующую функцию
function showOpacity() {
var data1op = d3.select(".bb-chart-line.bb-target.bb-target-data1").style("opacity");
var data2op = d3.select(".bb-chart-line.bb-target.bb-target-data2").style("opacity");
console.log(`data1 op: ${data1op} --- data2 op: ${data2op}`);
}
Поскольку show
а также hide
переходы используют длительность по умолчанию 1 с, трудно увидеть, что мы ждем до конца перехода. Чтобы изменить продолжительность этих переходов, нам нужно украсить ChartInternal.endAll()
метод. Здесь я установил длительность 5000 мс для всех переходов.
var bb_endall = bb.chart.internal.fn.endall;
bb.chart.internal.fn.endall = function (transition, callback) {
transition.duration(5000);
bb_endall(transition, callback);
};
Я пытался использовать другое украшение, которое использует atEndOf
техника для вызова пользовательской функции, но я не мог заставить ее работать.
Полный пример со всеми переходами, установленными на длительность 5000 мс
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.min.css" />
<script src="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.pkgd.min.js"></script>
<title>JS Bin</title>
</head>
<body>
<div id="chart"></div>
<script>
var bb_endall = bb.chart.internal.fn.endall;
bb.chart.internal.fn.endall = function (transition, callback) {
transition.duration(5000);
bb_endall(transition, callback);
};
//console.log('should be d1=1,d2=1');
var c = bb.generate({
bindto: "#chart",
size: {
width: 500,
height: 250
},
// transition: 0,
transition: {duration: 5000},
data: {
x: "x",
columns: [
["x", "2013-01-01", "2013-01-02", "2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06"],
["data1", 30, 200, 100, 400, 150, 250],
["data2", 130, 340, 200, 500, 250, 350],
]
},
axis: {
x: {
type: "timeseries",
tick: {
format: "%Y-%m-%d"
}
}
},
onrendered: function() {
// not usefull for end of transition
}
});
function showOpacity() {
var data1op = d3.select(".bb-chart-line.bb-target.bb-target-data1").style("opacity");
var data2op = d3.select(".bb-chart-line.bb-target.bb-target-data2").style("opacity");
console.log(`data1 op: ${data1op} --- data2 op: ${data2op}`);
}
function atEndOf(dataN, action) {
setTimeout(function() {
var transition = d3.active(d3.select(".bb-chart-line.bb-target.bb-target-"+dataN).node());
if (!transition) { return; } // no transition running
transition.transition().on("start", action);
}, 10);
}
// wait till main animation is finished
setTimeout(function() {
c.hide('data1');
atEndOf('data1', function () {
console.log('should be d1=0,d2=1');
showOpacity();
c.hide('data2');
atEndOf('data2', function () {
console.log('should be d1=0,d2=0');
showOpacity();
});
});
}, 6000);
// setTimeout(function(){console.log('should be d1=0,d2=1');c.hide('data1')},500);
// setTimeout(function(){console.log('should be d1=0,d2=0');c.hide('data2')},1000);
// setTimeout(function(){console.log('should be d1=1,d2=0');c.show('data1')},1500);
// setTimeout(function(){console.log('should be d1=1,d2=1');c.show('data2')},2000);
// setTimeout(function(){console.log('more waiting between hide/show')},3500);
// setTimeout(function(){console.log('should be d1=0,d2=1');c.hide('data1')},5000);
// setTimeout(function(){console.log('should be d1=0,d2=0');c.hide('data2')},6000);
// setTimeout(function(){console.log('should be d1=1,d2=0');c.show('data1')},7000);
// setTimeout(function(){console.log('should be d1=1,d2=1');c.show('data2')},8000);
</script>
</body>
</html>
Есть некоторый код в billboard.js
что вызывает onrendered
когда есть TabVisible. Он будет отложен до завершения перехода. Я пока понятия не имею, когда это так.