事件'transitionend'被称为太早,它会延迟渲染

时间:2016-08-26 13:05:14

标签: javascript css animation css-animations

我正在做什么和什么是错的

当我点击按钮时,会出现一个滑块。 (here is an example of what it looks like不注意此代码

滑块通过动画显示。动画结束后,我应该包含一个我从服务器加载的HTML页面。我需要在之后的滑块中应用HTML 动画,否则动画会停止(重新计算DOM)。

我的算法

  1. 启动请求以使HTML显示在滑块内
  2. 启动动画
  3. 等待数据准备好转换完成

    为什么?如果我在动画期间应用HTML,它会在将新HTML添加到DOM时停止动画。所以我等到第4步之前结束。

  4. 在滑块
  5. 中应用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.2ms),最后一帧未在transitionend事件中呈现

    Google Chrome Timeline

    从Google文档中,我可以告诉您Pixel Pipeline的“绘制和合成”步骤在<{strong> Event(transitionend)之后被称为

    Pixel Pipe Line

    也许我在想这个。

    我该如何处理这种动画?

    如何等待动画完全渲染和渲染?

2 个答案:

答案 0 :(得分:2)

我不确定为什么transitionend在最后一帧渲染之前被触发,但在这个(非常粗略的)测试中,似乎setTimeout 确实有帮助。 ..

第一个例子显示了如何过早地进行html计算和注入。第二个示例将长时间运行的方法包装在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:setTimeout似乎解决了它

在html注入代码周围加setTimeout

var ended = 0;
var cb = function() {
  ended += 1;

  if (ended == 2) {
    setTimeout(function() {
      $(".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>

答案 1 :(得分:0)

好吧,如果转换不按照你想要的方式为你工作,你可以回过头几年并使用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)));