Below is a program that is to loop pictures with fade-in effect.
It includes this statement:
opacity = Number(window.getComputedStyle(abc).getPropertyValue("opacity"));
I understand that this statement assigns the opacity value of a window object to a variable opacity
, but there is no such opacity
variable declaration of any elements in the program!
When I remove this statement, it only shows the first picture… And when I keep that statement, the program loops well with a fade-in effect.
Can anyone explain to me why such phenomenon happens?
Compare these two snippets:
- With assignment to
opacity
(fading is working as expected):
var opacity = 0; var arrindex = 0; var arr1 = [ "https://via.placeholder.com/400x400?text=p1", "https://via.placeholder.com/400x400?text=p2", "https://via.placeholder.com/400x400?text=p3", "https://via.placeholder.com/400x400?text=p4", "https://via.placeholder.com/400x400?text=p5" ]; setInterval(changepcs, 3000); function changepcs() { if (arrindex == arr1.length) { arrindex = 0 }; let abc = document.getElementById("picture"); let address = arr1[arrindex]; abc.style.backgroundImage = `url(${address})`; abc.classList.remove("fadein"); opacity = window.getComputedStyle(abc).getPropertyValue("opacity"); abc.classList.add("fadein"); arrindex++; }
body { height: 100vh; } #picture { background-repeat: no-repeat; background-position: center; background-size: 400px 400px; width: 80%; margin: auto; height: 80%; opacity: 0; } #picture.fadein { opacity: 1; transition: opacity 2s linear; }
<div id="picture"></div>
- Without assignment to
opacity
(no fading effect for second image):
var opacity = 0; var arrindex = 0; var arr1 = [ "https://via.placeholder.com/400x400?text=p1", "https://via.placeholder.com/400x400?text=p2", "https://via.placeholder.com/400x400?text=p3", "https://via.placeholder.com/400x400?text=p4", "https://via.placeholder.com/400x400?text=p5" ]; setInterval(changepcs, 3000); function changepcs() { if (arrindex == arr1.length) { arrindex = 0 }; let abc = document.getElementById("picture"); let address = arr1[arrindex]; abc.style.backgroundImage = `url(${address})`; abc.classList.remove("fadein"); // opacity = window.getComputedStyle(abc).getPropertyValue("opacity"); abc.classList.add("fadein"); arrindex++; }
body { height: 100vh; } #picture { background-repeat: no-repeat; background-position: center; background-size: 400px 400px; width: 80%; margin: auto; height: 80%; opacity: 0; } #picture.fadein { opacity: 1; transition: opacity 2s linear; }
<div id="picture"></div>
Advertisement
Answer
The difference is not related to the variable opacity
: you are right; it’s value is never used after that assignment and is therefore unnecessary.
However, the call of getComputedStyle(abc).getPropertyValue("opacity")
still makes the difference — you can omit the call of Number
and the assignment of this expression without any negative consequence, but the expression must stay.
This getComputedStyle(abc).getPropertyValue("opacity")
is a complex API: it needs to render the document to know exactly what the opacity value is, since in general such CSS values can be influenced by a lot of CSS rules. My hunch is that the engine performs a paint cycle in order to compute the real, rendered value of this property. That means the execution of this call takes some time — at least until after the next paint cycle. The side effect of this paint job is that the removal of the fadein
class has now also been rendered. And that means that when that fadein
class is added again, it really triggers the animation.
Without this getComputedStyle(abc).getPropertyValue("opacity")
evaluation, the engine does not wait for a paint cycle. That means it has not yet effectuated the class removal when it adds the fadein
class again. By consequence, nothing really changes — the fadein
class was still applied on the rendering when it is already “added” again. That means there is no animation.
I was a bit puzzled by this effect of that expression too. I would have explicitly waited for a paint cycle to happen with the call to requestAnimationFrame
so to execute code right before the next paint cycle, and then schedule a callback again to get code to execute after the next paint job (before the next one). That would be the moment I would be sure the CSS class fadein
had been removed in the rendered document and it would be the right time to add it again — so triggering the animation.
Like so:
var arrindex = 0; var arr1 = [ "https://via.placeholder.com/400x400?text=p1", "https://via.placeholder.com/400x400?text=p2", "https://via.placeholder.com/400x400?text=p3", "https://via.placeholder.com/400x400?text=p4", "https://via.placeholder.com/400x400?text=p5" ]; setInterval(changepcs, 3000); function changepcs() { if (arrindex == arr1.length) { arrindex = 0 }; let abc = document.getElementById("picture"); let address = arr1[arrindex]; abc.style.backgroundImage = `url(${address})`; abc.classList.remove("fadein"); // Need to wait for a paint cycle to occur before // adding the CSS class again requestAnimationFrame(() => requestAnimationFrame(() => abc.classList.add("fadein")) ); arrindex++; }
body { height: 100vh; } #picture { background-repeat: no-repeat; background-position: center; background-size: 400px 400px; width: 80%; margin: auto; height: 80%; opacity: 0; } #picture.fadein { opacity: 1; transition: opacity 2s linear; }
<div id="picture"></div>