【问题标题】:Temporarily bypass a CSS transition暂时绕过 CSS 过渡
【发布时间】:2016-04-16 00:49:53
【问题描述】:

假设我有这样的风格,有一个过渡:

#theElement {
    position: relative;
    left: 200px;
    transition: left 1s;
}

还有一些代码:

var element = document.getElementById("theElement");

function animateX(px) {
    element.style.left = px + "px";
}

所以,只有一个 animateX 函数,它简单地说明它的作用,动画 theElement 的左侧属性,现在如果我想要一个立即设置的函数left 属性,没有过渡:

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    element.style.transition = "left 1s";
}

为什么这不起作用,我该如何解决?

【问题讨论】:

    标签: javascript css css-transitions


    【解决方案1】:

    为了完成这项工作,您需要通过在 JS 中读取 CSS 更改来刷新它们。这个问题的答案解释了它是如何工作的:

    Can I use javascript to force the browser to "flush" any pending layout changes?

    下面的工作示例:

    var element = document.getElementById("theElement");
    
    function animateX(px) {
        element.style.left = px + "px";
    }
    
    function setX(px) {
        element.style.transition = "none";
        element.style.left = px + "px";
        // apply the "transition: none" and "left: Xpx" rule immediately
        flushCss(element);
        // restore animation
        element.style.transition = "";
    }
    
    function flushCss(element) {
      // By reading the offsetHeight property, we are forcing
      // the browser to flush the pending CSS changes (which it
      // does to ensure the value obtained is accurate).
      element.offsetHeight;
    }
    #theElement {
      position: relative;
      left: 200px;
      transition: left 1s;
      width: 50px;
      height: 50px;
      background: red;
    }
    .wrapper {
      width: 400px;
      height: 100px;
    }
    <div class="wrapper">
      <div id="theElement"></div>
    </div>
    <button onclick="animateX(100)">Animate 100</button>
    <button onclick="setX(0)">Set 0</button>

    【讨论】:

    • 你真的需要刷两次,因为lex82的回答只需要一次超时吗?
    • 没有。我认为通过显示两次同花会更容易理解,但我想它只会让它更加混乱。我已经编辑了我的答案来证明这一点。
    • 正如我的回答中所解释的,您不应通过在元素上明确设置动画来恢复动画。您将覆盖样式表(就像您在抑制动画时所做的那样)。
    • 好答案。我的版本是在 setTimeout 中完成后半部分,但这更好。
    • 使用“offsetHeight”强制应用属性的非常有趣的方式 - 感谢您的洞察力!
    【解决方案2】:

    其他解决方案很好,但我想提供一个替代方案并解释正在发生的事情。

    在您的示例中无法按预期工作的原因是所有 CSS 属性都已更改,然后触发 browser reflow event 并开始转换。换句话说,transition 属性设置为none然后left 属性更改,然后transition 属性更改回@ 987654328@.

    在所有样式属性更新后,触发浏览器重排事件,重新绘制 CSS,然后开始过渡:

    element.style.transition = "none";
    element.style.left = px + "px";
    element.style.transition = "left 1s";
    // The transition starts after all the CSS has been modified and a reflow has been trigged.
    

    这样执行有几个原因。

    主要原因是性能。与 repainting 更改单个 CSS 属性后的每个元素相比,更改所有属性并具有单个重绘事件更有效。此外,如果您要转换多个属性,您会希望在转换元素之前更改每个属性(这正是正在发生的事情)。

    如果您想要一个不涉及任何超时/延迟或强制回流的干净替代方案,您可以在设置值之前将transitionDuration 属性设置为0s,然后在转换元素时删除此内联样式.

    例如:

    var element = document.getElementById("theElement");
    
    function setX(px) {
      element.style.transitionDuration = '0s';
      element.style.left = px + "px";
    }
    
    function animateX(px) {
      element.style.transitionDuration = '';
      element.style.left = px + "px";
    }
    #theElement {
      position: relative;
      width: 100px;
      height: 100px;
      border: 2px solid;
      left: 200px;
      transition: left 1s;
    }
    <div id="theElement"></div>
    
    <button onclick="animateX(100)">Transition 100</button>
    <button onclick="animateX(300)">Transition 300</button>
    <button onclick="setX(0)">Set 0</button>
    <button onclick="setX(50)">Set 50</button>

    同样,您也可以在转换或设置值之前切换元素上的类:

    var element = document.getElementById("theElement");
    
    function setX(px) {
      element.classList.remove('transition');
      element.style.left = px + "px";
    }
    
    function animateX(px) {
      element.classList.add('transition');
      element.style.left = px + "px";
    }
    #theElement {
      position: relative;
      width: 100px;
      height: 100px;
      border: 2px solid;
      left: 200px;
    }
    #theElement.transition {
      transition: left 1s;
    }
    <div id="theElement"></div>
    
    <button onclick="animateX(100)">Transition 100</button>
    <button onclick="animateX(300)">Transition 300</button>
    <button onclick="setX(0)">Set 0</button>
    <button onclick="setX(50)">Set 50</button>

    上面的 sn-p 可以工作,但也值得指出的是,你可以监听transitionend 事件并在过渡结束时删除类:

    例如:

    function animateX(px) {
      element.classList.add('transition');
      element.style.left = px + "px";
      element.addEventListener('transitionend', callback);
    
      function callback() {
        this.classList.remove('transition');
        this.removeEventListener('transitionend', callback);
      }
    }
    

    【讨论】:

    • 很好的答案。我不知道你可以收听transitionend 事件。本次活动是否有良好的浏览器支持?
    • @jperezov 是的,它有good browser support。所有现代浏览器都支持它,但您可能需要为旧浏览器使用前缀版本,例如webkitTransitionEndmsTransitionEnd
    【解决方案3】:

    您可以这样做,但您必须通过超时恢复旧的转换属性。

    function setX(px) {
        element.style.transition = "none";
        element.style.left = px + "px";
        setTimeout(function() {
            element.style.transition = "left 1s";
        });
    }
    

    JSFiddle

    如果你不使用超时来恢复旧的转换属性,浏览器将永远不会知道它已经改变了。除非您的代码完全执行,否则它不会呈现页面。

    我还建议像这样取消设置过渡属性:

    element.style.transition = "";
    

    否则,您将永远覆盖此元素的样式表。在元素上设置过渡样式之前,过渡由样式表确定。如果在元素上设置过渡样式,则会覆盖样式表。

    更新:似乎超时时间不够。我在 FF/Linux 中尝试过,但没有成功。因此,您要么添加更多毫秒(不推荐),要么应用@jperezov 的解决方案(推荐)。

    【讨论】:

    • 另外,setTimeout 不需要第二个参数来表示毫秒吗?
    • 它没有。默认为零,因此几乎没有延迟。
    • 永远覆盖样式表是什么意思,能举个例子吗?
    • 在不知道幕后发生的事情的情况下推荐 setTimeout 不是一个好主意,这在 Josh Crozier 的回答中得到了更好的解释。
    【解决方案4】:

    只是为了对 Josh Crozier 的回答进行充分且充分解释的补充:

    还有ontransitioncalcel事件,当事件被取消时触发,所以你可以测试一些方法是否取消它。来自current draft我cherrypick:

    如果一个元素不再在文档中,实现必须取消任何正在运行的转换

    这样您就可以删除元素,进行更改,然后将元素添加回原来的位置;设置display: none 有各种副作用,停止它们之间的转换,

    如果元素具有属性的运行转换,并且没有匹配的转换属性值,则实现必须取消正在运行的转换

    因此您可以尝试删除转换属性值,

    如果正在运行的过渡中属性的当前值等于更改后样式中的属性值,或者如果这两个值不可过渡,则实现必须取消正在运行的过渡。

    因此您可以先将 left 属性设置为 autoinitial 以中断过渡。

    唉,没有任何过渡事件是可取消的,因此在其中调用 e.preventDefault() 不应阻止它们。

    【讨论】:

      猜你喜欢
      • 2017-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-22
      • 1970-01-01
      • 2023-03-08
      • 2014-04-21
      相关资源
      最近更新 更多