CSS过渡未启动/回调ISNN'

发布于 2025-02-13 03:50:20 字数 1360 浏览 2 评论 0 原文

我有一个大型游戏项目,该项目在其代码中使用了广泛的jQuery。不久前,我剥离了所有的jQuery,并用纯JS代替了它,但是我遇到的一件事是更换游戏中弹丸的动作呼吁。

看来我应该用CSS过渡替换它们,并且游戏需要知道何时完成过渡,因此我需要在过渡中添加回调。一切都很好,除非我为弹丸分配了新的位置值,否则过渡被完全跳过,没有调用回调。出于某种不敬虔的原因,如果我将更改包裹在SettieMout中的CSS位置为1毫秒的情况下,它开始有效,即使那样,有时它仍然会跳过过渡。

现在通常可以工作,除了大约10次过渡时,它将播放,但不会调用回调。我不知道为什么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.

I have a large game project that used extensive jquery in its code. Some time ago I stripped out all of the jquery and replaced it with pure JS, but the one thing I had trouble with was replacing the .animation calls for projectiles in the game.

It appeared that I should replace them with CSS transitions, and the game needed to know when the transition was done, so I needed to add a callback to the transition. All well and good, except when I assigned new location values for the projectile, the transition was skipped entirely and no callback was called. For some ungodly reason, it started working if I wrapped the change to its css position in a SetTimeout for 1ms--and even then, sometimes it would still skip the transition.

Now it usually works, except about one time in 10 the transition will play but then the callback will not be called. I don't know why the settimeout helps to be there, and I don't know why the callback sometimes doesn't work. Can anyone help me understand?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

帝王念 2025-02-20 03:50:21

有关为什么会发生这种情况的全面解释,请参见 /a 这个

基本上,在设置新的样式时,浏览器仍未应用一组内联,您的元素的计算样式仍然具有其 display> display 值设置为> '' “
,因为这是DOM默认值不在的元素。
它的 top 计算值仍然是 0px ,即使您确实在标记中设置了它。

这意味着,当过渡属性将在下一个帧绘画之前应用于左和 top 已经是您设置的,因此过渡将无关:它不会开火。

为了绕过它,您可以强迫浏览器执行此reecalc。的确,几个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" 
});

但实际上,最好的可能是直接使用 web动画API 几乎在任何地方支持 。这将在正确的时间照顾正确的事情。

let tablehtml = `
<div id="spanky" 
  style="
    position: absolute; 
    background-color:blue; 
    width:20px; 
    height:20px;
  "
</div>`;

document.body.innerHTML += tablehtml;

let animdiv = document.getElementById("spanky");
const anim = animdiv.animate([
    { left: "10px",  top: "10px"  },
    { left: "100px", top: "100px" }
  ],
  {
    duration: 1000,
    fill: "forwards"
  });

anim.finished.then(() => {
  animdiv.style.backgroundColor = "green";
});

For a comprehensive explanation of why this happens, see this Q/A, and this one.

Basically, at the time you set the new style the browser still has not applied the one set inline, your element's computed style still has its display value set to ""
, because it's what elements that are not in the DOM default to.
Its left and top computed values are still 0px, even though you did set it in the markup.

This means that when the transition property will get applied before next frame paint, left and top will already be the ones you did set, and thus the transition will have nothing to do: it will not fire.

To circumvent it, you can force the browser to perform this recalc. Indeed a few DOM methods need the styles to be up to date, and thus browsers will be forced to trigger what is also called a reflow.
Element.offsetHeight getter is one of these method:

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" 
});

But really, the best is probably to directly use the Web Animation API, which is supported almost everywhere (but in long dead IE). This will take care of doing the right thing at the right time.

let tablehtml = `
<div id="spanky" 
  style="
    position: absolute; 
    background-color:blue; 
    width:20px; 
    height:20px;
  "
</div>`;

document.body.innerHTML += tablehtml;

let animdiv = document.getElementById("spanky");
const anim = animdiv.animate([
    { left: "10px",  top: "10px"  },
    { left: "100px", top: "100px" }
  ],
  {
    duration: 1000,
    fill: "forwards"
  });

anim.finished.then(() => {
  animdiv.style.backgroundColor = "green";
});

淡淡離愁欲言轉身 2025-02-20 03:50:21

这与新元素何时实际上是“绘制”的时间有关...

我知道这也是一个kludge,但是...

我发现100%成功的一种方式是等待两帧以下(在60fps约为33.333毫米的60fps处 - 但是如今的浏览器中的 settimeout 在幽灵或其他方面添加了人造“软糖” - 无论如何...

request> request> request> request> request> request> request> request> request> request> request> request> request>; gt; gt; gt; requestAnimationFrame()=&gt; {...您的代码...})做同样的事情

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 对我来说大约是10分之一,但是双重请求使得不可能失败

it has to do with the timing of when the new element is actually "painted" ...

I know this is a kludge too but ...

the one way I've found to guarantee 100% success is to wait just under two frames (at 60fps that's about 33.333ms - but the setTimeout in browsers these days has a artificial "fudge" added due to spectre or something - anyway ...

requestAmiationFrame(() => requestAnimationFrame() => { ... your code ...})) does the same thing except it could be as little as 16.7ms delay, i.e. just over one frame

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" 
	}); 
}));

having a single requestAnimationFrame failed about 1 in 10 for me, but the double request makes it impossible to fail

穿越时光隧道 2025-02-20 03:50:21

通过遵循 settimeout()> 。
但是对我而言, and.fined.then(()=&gt; {}); 实际上未调用。调用此回调花了2毫秒。

对于我的情况, and.onfinish =()=&gt; {} 确保框架已绘制,并花费16毫秒来调用回调。

I was able to get rid of setTimeout() by following the Web Animation Approach suggested by Kaiido.
But for me anim.finished.then(()=>{}); is not invoked after the frame is actually painted. It took 2ms to invoke this callback.

For my case anim.onfinish=()=>{} makes sure the frame is painted and it took 16ms to invoke the callback.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文