暂时绕过 CSS 过渡

Posted

技术标签:

【中文标题】暂时绕过 CSS 过渡【英文标题】:Temporarily bypass a CSS transition 【发布时间】: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";

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

【问题讨论】:

【参考方案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() 不应阻止它们。

【讨论】:

以上是关于暂时绕过 CSS 过渡的主要内容,如果未能解决你的问题,请参考以下文章

CSS位置偏移反爬虫绕过

CSS位置偏移反爬虫绕过

httpclient绕过证书验证进行HTTPS请求

暂时禁用 CSS 过渡效果最干净的方法是啥?

我可以在构建元素时暂时关闭所有 CSS3 过渡/动画吗?

在OpenStack中绕过或停用security group (iptables)