暂时绕过 CSS 过渡



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

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

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

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

我可以使用javascript强制浏览器"刷新"任何待处理的布局更改吗?

工作示例如下:

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>

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

在您的示例中,它未按预期工作的原因是因为所有 CSS 属性都已更改,然后触发浏览器重排事件并开始转换。换句话说,transition 属性设置为 none然后更改 left 属性,然后将 transition 属性更改回 left 1s

更新所有样式属性后,将触发浏览器重排事件,重新绘制 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.

它像这样执行有几个原因。

主要原因是性能。与其在更改单个 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>

上面的代码片段有效,但值得指出的是,您可以侦听 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);
  }
}

您可以执行此操作,但必须使用超时还原旧的转换属性。

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的解决方案(推荐(。

只是为了对Josh Crozier的回答进行一些补充:

还存在 ontransitioncalcel 事件,该事件在事件被取消时触发,因此您可以测试某些方法是否取消它。从目前的草案中,我挑选:

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

因此,您可以删除元素,进行更改,然后将元素添加回其原始位置; 设置display: none有各种副作用,停止它们之间的过渡,

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

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

如果运行转换

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

因此,您可以将 left 属性设置为 auto 或先initial以中断过渡。

唉,没有一个过渡事件是可以取消的,所以在其中调用e.preventDefault()不应该阻止它们。

相关内容

  • 没有找到相关文章

最新更新