Daniel
Daniel

Reputation: 1601

How to make transition duration to be the same for all values?

We have two similar width transitions, that are interrupted in the middle and their final transition values are changed. The only difference between them is that green box final width is exactly the same as it's starting width and orange box final width is different from it's starting width.

This causes green box transition to be faster, as if it's just "undoing" the initial transition.

animation

Link to a full demo

html:

<div id="reference"></div>
<div id="boxA" ></div>
<div id="boxB" ></div>

css:

#reference {
  width: 100px;
  height: 100px;
  background-color: rgba(0, 204, 255, 0.5);
}

#boxA {
  background-color: rgba(166, 255, 0, 0.5);
  height: 100px;
  transition: width 1s linear;
}

#boxB {
  background-color: rgba(255, 136, 0, 0.5);
  height: 100px;
  transition: width 1s linear;
}

javascript:

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

document.getElementById("boxA").style.width = "100px";
document.getElementById("boxB").style.width = "100px";

(async () => {
await sleep(1000)

document.getElementById("boxA").style.width = "200px";
document.getElementById("boxB").style.width = "200px";

await sleep(500)

document.getElementById("boxA").style.width = "100px";
document.getElementById("boxB").style.width = "101px";

})()

Upvotes: 0

Views: 228

Answers (2)

ippi
ippi

Reputation: 10167

The spec is here: I'd check §3.4.4 https://www.w3.org/TR/css-transitions-1/#starting

Otherwise, implementations must cancel the running transition and start a new transition whose:

...

  • reversing-adjusted start value is the same as the start value, and

So if you return to the original value, then it's the same transition, but going to a new value it starts a new transition from the current value.

So one way to bypass this could be by passing in an intermediary value, just to trigger a new transition:

// go to any value (that is not the current value or the original value)
document.getElementById("boxA").style.width = "999px"; 

// Trigger reflow  (See @Kaiido's comment below)
 document.body.offsetWidth;

// and then immediately go back to the original value
document.getElementById("boxA").style.width = "100px";

Now both boxes will have two transitions that will have the same duration, but one will end at 100px, and the other at 101px.


Also I think the rationale explained is relevant: §3.1 Faster reversing of interrupted transitions

Upvotes: 2

Pranav Rustagi
Pranav Rustagi

Reputation: 2731

It is not a perfect answer, as I can not tell you what value would be right for every different element, but it works :

box1 = document.getElementById("boxA");
box2 = document.getElementById("boxB");

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

box1.style.width = "100px";
box2.style.width = "100px";

(async() => {
  await sleep(1000)

  box1.style.width = "200px";
  box2.style.width = "200px";

  await sleep(500)

  box1.style.width = "100px";
  box2.style.transitionDuration = "0.5s";
  box2.style.width = "101px";

})()
#reference {
  width: 100px;
  height: 100px;
  background-color: rgba(0, 204, 255, 0.5);
}

#boxA {
  background-color: rgba(166, 255, 0, 0.5);
  height: 100px;
  transition: width 1s linear;
}

#boxB {
  background-color: rgba(255, 136, 0, 0.5);
  height: 100px;
  transition: width 1s linear;
}
<div id="reference"></div>
<div id="boxA"></div>
<div id="boxB"></div>

Explanation : Both box elements increase to 200px, and after that, the first box goes back to 100px, whereas, the second box element reduces just by 99px. As it has to cover lesser distance than the first box, its transition duration has to be reduced as well.

Upvotes: 0

Related Questions