Temporarily bypass a CSS transition

落爺英雄遲暮 提交于 2019-12-23 12:45:33

问题


Let's say I have a style like this, with a transition:

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

And some code:

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

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

So, simply there is a animateX function, which simply says what it does, animates the left propery of theElement, now what if I also want to have a function that instantly sets the left property, without a transition:

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

Why doesn't this work and how do I fix it?


回答1:


In order to make this work, you need to flush the CSS changes by reading them in the JS. The answer to this question explain how this works:

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

Working example below:

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>



回答2:


You can do this but you have to restore the old transition property with a timeout.

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

JSFiddle

If you don't use a timeout to restore the old transition property, the browser will never know it has changed at all. It won't render the page unless your code has executed completely.

I also recommend to unset the transition property like this:

element.style.transition = "";

Otherwise you override the stylesheet forever for this element. Before you set the transition style on the element, the transition is determined by the stylesheet. If you set the transition style on the element, you override the stylesheet.

Update: It seems the timeout is not sufficient. I tried it in FF/Linux and it did not work. So you either add some more milliseconds (not recommended) or you apply the solution of @jperezov (recommended).




回答3:


The other solutions are good, but I wanted to provide an alternative and explain what is happening.

The reason it's not working as expected in your example is because all of the CSS properties are changed and then a browser reflow event is triggered and the transition starts. In other words, the transition property is set to none, then the left property is changed, then the transition property is changed back to left 1s.

After all the style properties have been updated, a browser reflow event is triggered, the CSS is repainted, and then the transition starts:

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.

There are a few reasons it is executed like this.

The main reason is performance. Rather than repainting each element after changing a single CSS property, it is much more efficient to change all of the properties and have a single repaint event. Additionally, if you are transitioning multiple properties you would expect each property to be changed before the element is transitioned (which is exactly what is happening).

If you want a clean alternative that doesn't involve any timeouts/delays or forced reflows, you could simply set the transitionDuration property to 0s before setting the value, and then remove this inline style when transitioning the element.

For example:

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>

Similarly, you could also toggle a class on the element before transitioning or setting the value:

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>

The above snippet works, but it's also worth pointing out that you can listen to the transitionend event and remove the class when the transition ends:

For instance:

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);
  }
}


来源:https://stackoverflow.com/questions/34726154/temporarily-bypass-a-css-transition

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!