CSS 转换没有 start/callback 没有被调用

CSS transition doesn't start/callback isn't called

我有一个大型游戏项目,其代码中使用了大量 jquery。前段时间我删除了所有 jquery 并用纯 JS 替换它,但我遇到的一件事是替换游戏中对射弹的 .animation 调用。

看来我应该用 CSS 转换替换它们,并且游戏需要知道转换何时完成,因此我需要向转换添加回调。一切都很好,除了当我为射弹分配新的位置值时,过渡被完全跳过并且没有调用任何回调。出于某种不敬虔的原因,如果我将更改包装到 SetTimeout 中的 css 位置 1 毫秒,它就会开始工作——即便如此,有时它仍会跳过过渡。

现在它通常可以正常工作,除了大约十分之一的时间会播放过渡但不会调用回调。我不知道为什么 settimeout 在那里有帮助,我也不知道为什么回调有时不起作用。谁能帮我理解一下?

let tablehtml = '<div id="'+animid+'" style="position: absolute; left: ' + ammocoords.fromx + 'px; top: ' + ammocoords.fromy + 'px; background-image:url(\'graphics/' + ammographic.graphic + '\');background-repeat:no-repeat; background-position: ' + ammographic.xoffset + 'px ' + ammographic.yoffset + 'px; transition: left '+duration+'ms linear 0s, top '+duration+'ms linear 0s;"><img src="graphics/spacer.gif" width="32" height="32" /></div>';

document.getElementById('combateffects').innerHTML += tablehtml;
let animdiv = document.getElementById(animid);
animdiv.addEventListener("transitionend", function(event) { 
  FinishFirstAnimation();
}, false);

setTimeout(function() { Object.assign(animdiv.style, {left: ammocoords.tox+"px", top: ammocoords.toy+"px" }); }, 1); // THIS IS A TOTAL KLUDGE
// For some reason, the transition would not run if the 1ms pause was not there. It would skip to the end, and not
// fire the transitionend event. This should not be necessary.

这与新元素实际出现的时间有关 "painted" ...

我知道这也是一种拼凑,但是...

我发现保证 100% 成功的一种方法是等待不到两帧(在 60fps 下大约是 33.333 毫秒 - 但是现在浏览器中的 setTimeout 有一个人为的 "fudge" 由于幽灵或其他原因添加 - 无论如何......

requestAmiationFrame(() => requestAnimationFrame() => { ... your code ...})) 做同样的事情,只是延迟可能低至 16.7 毫秒,即仅超过一帧

let tablehtml = `
 <div id="spanky" 
   style="position: absolute; 
     left: 10px; 
      top: 10px; 
      background-color:blue; 
      width:20px; 
      height:20px;
      transition: left 1000ms linear 0s, top 1000ms linear 0s;">
</div>`;

document.body.innerHTML += tablehtml;
let animdiv = document.getElementById('spanky');
animdiv.addEventListener("transitionend", function(event) { 
  animdiv.style.backgroundColor='red';
}, false);
requestAnimationFrame(() => requestAnimationFrame(() => {
  animdiv.style.backgroundColor='green';
 Object.assign(animdiv.style, {
  left: "100px", 
   top: "100px" 
 }); 
}));

对我来说,有一个 requestAnimationFrame 失败的概率约为十分之一,但双重请求使它不可能失败

有关为什么会发生这种情况的全面解释,请参阅 , and

基本上,当您设置新的 style 时,浏览器仍未应用内联设置,您元素的计算样式仍将其 display 值设置为 "" ,因为它是不在 DOM 中的元素的默认值。 它的 lefttop 计算值仍然是 0px,即使您确实在标记中设置了它。

这意味着当 transition 属性 将在下一帧绘制之前应用时,lefttop 将已经是您设置的那些,因此过渡将无事可做:它不会触发。

要绕过它,您可以强制浏览器执行此重新计算。事实上,一些 DOM 方法需要样式是最新的,因此浏览器将被迫触发也称为重排的操作。
Element.offsetHeight getter 是以下方法之一:

let tablehtml = `
<div id="spanky" 
  style="position: absolute; 
    left: 10px; 
    top: 10px; 
    background-color:blue; 
    width:20px; 
    height:20px;
    transition: left 1000ms linear 0s, top 1000ms linear 0s;">
</div>`;

document.body.innerHTML += tablehtml;

let animdiv = document.getElementById('spanky');
animdiv.addEventListener("transitionend", function(event) { 
  animdiv.style.backgroundColor='red';
}, false);
// force a reflow
animdiv.offsetTop;
// now animdiv will have all the inline styles set
// it will even have a proper display

animdiv.style.backgroundColor='green';
Object.assign(animdiv.style, {
  left: "100px", 
  top: "100px" 
});