元素创建后立即触发转换

问题描述 投票:0回答:3

我正在尝试创建一个动作,其中创建一个 div 并立即向上“浮动”,直到它离开屏幕。

为了实现此目的,我尝试使用 CSS transition,它将完全由 JavaScript 驱动(由于我的用例的限制)。

当我创建一个元素,为其分配过渡样式属性,然后立即尝试通过更改样式来启动过渡 (

top
) 时,会出现问题。

看起来发生了一个计时问题,

top
样式更改在过渡变得可用之前触发,因此只是立即将我的 div 移出屏幕,而不是实际执行过渡。

这是一个简化的示例:

var
    defaultHeight = 50,
    iCount = 0,
    aColors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'];

function createBlock() {
    var testdiv = document.createElement('div');
    testdiv.className = 'testdiv';
    document.body.appendChild(testdiv);
    testdiv.style.left = '50%';
    testdiv.style.backgroundColor = aColors[iCount % aColors.length];
    testdiv.style.width = defaultHeight + 'px';
    testdiv.style.height = defaultHeight + 'px';
    testdiv.style.fontSize = '30px';
    testdiv.style.textAlign = 'center';
    testdiv.innerHTML = iCount;
    testdiv.style.top = '500px';
    testdiv.style.position = 'absolute';
    iCount++;
    return testdiv;
}

document.getElementById('go').onclick = function() {
    var testdiv = createBlock();

    testdiv.style.transition = "top 2.0s linear 0s";

    setTimeout(function() {
        testdiv.style.top = (defaultHeight*-2) + 'px';
    }, 0); // <- change this to a higher value to see the transition always occur
};

当快速单击“go”按钮(参见 JSBin)时,div 仅偶尔出现(可能是由于上述计时问题)。

如果增加

setTimeout
的延迟值,您可以看到过渡几乎总是有效。

有没有一种方法可以在创建元素后立即确定地启动转换(而不必诉诸延迟)?

javascript css transition race-condition
3个回答
5
投票

对于转换,您需要两种不同的状态。改变。

两个渲染周期之间的样式仅被覆盖。它们仅在(重新)渲染节点时应用。
因此,如果您的 setTimeout() 在应用“旧”样式之前触发,则样式只会被覆盖,并且您的节点将使用目标样式进行渲染。

据我所知。大多数(桌面)浏览器都争取 60fps 的帧速率,这使得间隔为 16.7 毫秒。所以 setTimeout(fn,0) 很可能会在那之前触发。

您可以像您提到的那样增加超时(我建议至少 50 毫秒),或者您可以触发/强制节点的渲染;例如询问它的尺寸。

要获取节点的大小,浏览器首先必须将所有样式应用于节点,以了解它们如何影响它。
另外,上下文对于 css 很重要,因此必须将 Node 添加到 DOM 的某个位置,以便浏览器能够获取最终计算的样式。

长答案短:

document.getElementById('go').onclick = function() {
    var testdiv = createBlock();
    testdiv.style.transition = "top 2.0s linear 0s";

    testdiv.scrollWidth;  //trigger rendering

    //JsBin brags that you don't do anything with the value.
    //and some minifyer may think it is irrelevant and remove it.
    //so, increment it (`++`); the value is readonly, you can do no harm by that.
    //but the mere expression, as shown, already triggers the rendering.

    //now set the target-styles for the transition
    testdiv.style.top = (defaultHeight*-2) + 'px';
};

1
投票

另一种方法是使用

animation

如果您想使用 javascript 动态创建

keyframes
,这里有一篇很好的文章显示:https://stackoverflow.com/questions/10342494/set-webkit-keyframes-values-using-javascript-variable

var
    defaultHeight = 50,
    iCount = 0,
    aColors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'];

function createBlock() {
    var testdiv = document.createElement('div');
    testdiv.className = 'testdiv';
    document.body.appendChild(testdiv);
    testdiv.style.left = '50%';
    testdiv.style.backgroundColor = aColors[iCount % aColors.length];
    testdiv.style.width = defaultHeight + 'px';
    testdiv.style.height = defaultHeight + 'px';
    testdiv.style.fontSize = '30px';
    testdiv.style.textAlign = 'center';
    testdiv.innerHTML = iCount;
    testdiv.style.top = '300px';
    testdiv.style.position = 'absolute';
    // you can preset the animation here as well
    //testdiv.style.animation = 'totop 2.0s linear forwards';
    iCount++;
    return testdiv;
}

document.getElementById('go').onclick = function() {
    var testdiv = createBlock();
    testdiv.style.animation = 'totop 2.0s linear forwards';
};
@keyframes totop {
  100% { top: -100px; }
}
<button id="go">go</button>


0
投票

由于转换需要在重绘之间进行状态更改,因此只能在第一次绘制元素后触发转换。

您可以使用

window.requestAnimationFrame
指定在下次重绘之前运行的回调。

必须请求两个后续动画帧,因为您希望第一次重绘看到旧状态。

requestAnimationFrame(() => requestAnimationFrame(() => testdiv.style.top = (defaultHeight*-2) + 'px';
© www.soinside.com 2019 - 2024. All rights reserved.