【问题标题】:Immediately trigger transition after element created创建元素后立即触发转换
【发布时间】:2016-05-24 01:26:25
【问题描述】:

我正在尝试创建一个操作,其中创建一个 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
};

当快速单击“开始”按钮 (see JSBin) 时,div 只会偶尔出现(可能是由于上述时间问题)。

如果您增加setTimeout 的延迟值,您可以看到转换几乎总是有效。

有没有办法在创建元素后立即确定性地启动过渡(无需求助于延迟)?

【问题讨论】:

  • 您需要在类中添加动画(新)顶部值并分配该类,否则过渡不知道要在哪个顶部之间设置动画,因为后一个值会覆盖前者。
  • @LGSon 新创建的 div 的初始 top 值为 '500px'...为什么需要在 CSS 类中定义(而不是作为最佳实践)?
  • 稍微误读了您的问题...将在几秒钟内发布答案。

标签: javascript css transition race-condition


【解决方案1】:

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

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

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

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

要获得节点的大小,浏览器首先必须将所有样式应用于节点,以了解它们如何影响它。
此外,上下文对 css 很重要,因此必须将节点添加到 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';
};

【讨论】:

  • 在这种情况下,您可以有意地利用这种行为,或者它可能会对您的代码产生重大的性能影响,只需按照您在代码中执行操作的顺序即可。因为就性能而言,您能做的最昂贵的事情就是触发不必要的重新渲染;通过更改节点上的某些 className,然后在其中一个子节点上询问受样式影响的任何内容。比如这个职位。
  • 所以你的意思是,在这种情况下,使用animation 将获得性能来防止使用transition 触发重新渲染?
  • 不,我是说,您应该牢记这种行为,尤其是在寻找性能问题时。我们经常仅根据我们在代码中执行操作的顺序触发不必要的重新渲染。大多数时候,我们并不关心(太多)前端的性能,但是当您正在寻找不必要的重新渲染时,这是一个很好的起点。
  • 有什么方法可以强制setTimeout 回调到下一个渲染周期而不使用承诺,如果那是承诺的话?我遇到的问题是我通过添加带有 JS 的类来转换 after 伪元素
【解决方案2】:

另一种方法是使用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; }
}
&lt;button id="go"&gt;go&lt;/button&gt;

【讨论】:

  • 这看起来不错,但是使用动画需要定义关键帧,需要用 CSS 设置。我正在寻找一个全 JS 解决方案,我不必在文档的样式中插入任何新内容。我知道这是非标准的。你知道为什么动画在这里有效,但不是过渡吗?
  • @Jonathan.Brink 动画在创建时有一个开始值和一个结束值,而没有过渡,因此需要先绘制过渡,然后再为其分配第二个顶部值,这会启动过渡。您案件的延误确实使这种情况发生。
  • ...啊“需要先绘制过渡”可能是理解问题的关键...“需要先绘制”是什么意思?您是指在过渡实际开始之前发生的步骤(通过样式更改)?
  • @Jonathan.Brink Thomas 刚刚发布了一个很好描述它的答案。
  • @Jonathan.Brink 用链接更新了我的答案,以满足您对如何使用脚本创建 keyframes 的要求
猜你喜欢
  • 2021-01-18
  • 2014-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-29
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多