Событие "переход" называется слишком ранним, оно задерживает рендеринг

Что я делаю и что неправильно

Когда я нажимаю кнопку, появляется слайдер. (вот пример того, как он выглядит, не обращать внимания на этот код)

Ползунок показывает анимацию. Когда анимация закончена, я должен включить 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)));