Событие "переход" называется слишком ранним, оно задерживает рендеринг
Что я делаю и что неправильно
Когда я нажимаю кнопку, появляется слайдер. (вот пример того, как он выглядит, не обращать внимания на этот код)
Ползунок показывает анимацию. Когда анимация закончена, я должен включить HTML-страницу, загруженную с сервера. Мне нужно применить HTML в слайдере после анимации, в противном случае анимация остановится (DOM пересчитан).
Мой алгоритм
- Запустите запрос, чтобы HTML отображался внутри слайдера.
- Запустите анимацию
-
Подождите, пока данные будут готовы и, который будет завершен.
Почему? Если я применяю HTML во время анимации, он останавливает анимацию, а новый HTML добавляется в DOM. Поэтому я жду, когда оба закончится до шага 4.
- Применение HTML внутри слайдера
Вот укороченный код:
// Start loading data & animate transition
var count = 0;
var data = null;
++count;
$.get(url, function (res) {
data = res;
cbSlider();
});
// Animation starts here
++count;
$(document).on('transitionend', '#' + sliderId, function () {
$(document).off('transitionend', '#' + sliderId);
cbSlider()
});
function cbSlider() {
--count;
// This condition is only correct when both GET request and animation are finished
if (count == 0) {
// Attempt to enforce the frame to finish (doesn't work)
window.requestAnimationFrame(() => { return });
$('#' + sliderId + ' .slider-content').html(data);
}
}
Подробный вопрос
transitionend
называется слишком ранним. Это делает последний анимированный фрейм слишком длинным (477.2мс) и последний кадр не отображается в событии transitionend
.
![Хронология Google Chrome]()
Из документации Google я могу сказать вам, что шаг Paint и Composite Пиксельный трубопровод называется после Event(transitionend)
:
![Пиксельная линия трубопровода]()
Возможно, я слишком задумываюсь над этим.
Как мне обрабатывать такие виды анимаций?
Как я могу подождать, пока анимация будет полностью закончена и отображена?
Ответы
Ответ 1
Я не уверен, почему transitionend
запускается до того, как последний кадр отобразился, но в этом (очень грубом) тесте кажется, что setTimeout
помогает...
В первом примере показано, как вычисление и инъекция html происходит слишком рано. Второй пример завершает метод long running в setTimeout
и, похоже, не вызывает каких-либо перехватов в анимации.
Пример 1: воспроизведение вашей проблемы
var ended = 0;
var cb = function() {
ended += 1;
if (ended == 2) {
$(".animated").html(createLongHTMLString());
}
}
$(".load").click(function() {
$(".animated").addClass("loading");
$(".animated").on("transitionend", cb);
setTimeout(cb, 100);
});
function createLongHTMLString() {
var str = "";
for (var i = 0; i < 100000; i += 1) {
str += "<em>Test </em>";
}
return str;
};
.animated,
.target {
width: 100px;
height: 100px;
position: absolute;
text-align: center;
line-height: 100px;
overflow: hidden;
}
.target,
.animated.loading {
transform: translateX(300%);
}
.animated {
background: green;
z-index: 1;
transition: transform .2s linear;
}
.target {
background: red;
z-index: 0;
}
.wrapper {
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="animated">Loading</div>
<div class="target"></div>
</div>
<button class="load">load</button>
Ответ 2
Ну, если переходы не работают для вас так, как вы хотите, вы можете вернуться через несколько лет и использовать анимацию jQuery вместо этого?
(function(slider){
$.get(url, function (res) {
slider.animate({
// put whatever animations you need here
left: "5%",
}, 5000, function() {
// Animation complete.
slider.find('.slider-content').html(res);
});
});
}($('#' + sliderId)));
Вы также можете запустить оба действия одновременно, а затем добавить html в документ только после завершения анимации и завершения запроса, но для этого потребуется флаг.
(function(slider){
// whether the animation is finished
var finished = false;
// whether the html has been added already
var added = false;
// your html data
var html = null;
function add() {
if (finished && html && !added) {
// make sure function will only add html once
added = true;
slider.find('.slider-content').html(html);
}
}
$.get(url, function (res) {
html = res;
add();
});
slider.animate({
// put whatever animations you need here
left: "5%",
}, 5000, function() {
// Animation complete.
finished = true;
add();
});
}($('#' + sliderId)));