Change of pictures over and over by using setInterval - javascript

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>

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>

Thanks for all your support to this question, it is highly believe as one of the answer here (from trincot) mention the statement: "getComputedStyle(abc).getPropertyValue("opacity")" provide some time interval that allows the paint of the removal of "fade-in" class here to be rendered. To prove this, I try to replace the statement by using setTimeout statement: "setTimeout(()=>abc.classList.add("fadein"), 3000);" and the job is done.
setInterval(changepcs,3000);
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"
];
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");
setTimeout(()=>abc.classList.add("fadein"), 3000);
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;
}
<body>
<div id="picture"></div>
</body>

Related

Why does website cause most elements to be recalculated upon small change after being hosted?

I decided to make a Pac-Man game and after I did it and everything was working somewhat fine on local document I pushed my website on Github pages and decrease in fps was enormous. It turned out page was making recalculation for hundreds elements which caused 20ms+ delay.
Here's a small part of the code that still has performance difference between local and github-pages hosted website.
const gameBoard = document.getElementById("game-board");
const root = document.documentElement.style;
let elements;
let characterNode;
let position = 658;
makeLevel();
function makeLevel() {
for (let i = 0; i < 868; i++) {
const element = document.createElement("DIV");
element.style.backgroundPosition = `0 0`;
let character = document.createElement("DIV");
character.className = "yellow";
element.append(character);
gameBoard.append(element);
}
elements = Array.from(gameBoard.children);
characterNode = elements[658].children[0];
changePosition();
}
function changePosition() {
root.setProperty(`--yellow-sprite-y`, `-32px`);
characterNode.style.transform = `translateX(-20px)`;
setTimeout(() => {
characterNode.style.transform = "";
characterNode.classList.remove(`yellow-visible`);
position = position - 1;
characterNode = elements[position].children[0];
characterNode.classList.add(`yellow-visible`);
changePosition()
}, 200)
}
:root {
--yellow-sprite-y: -32px;
}
#game-board {
width: 560px;
height: 620px;
display: grid;
grid-template-columns: repeat(28, 20px);
background-color: #000000;
}
#game-board > * {
position: relative;
width: 20px;
height: 20px;
}
.yellow {
position: absolute;
top: -4px;
left: -5.5px;
width: 30px;
height: 28px;
z-index: 10;
}
.yellow-visible {
background-image: url("https://i.imgur.com/SphNpH6.png");
background-position: -32px var(--yellow-sprite-y);
transition: transform 200ms linear;
}
<div id="game-board">
</div>
The exact problem in this code is line 29 which on local document performs like this:
while after hosting it on Github performs this way:
Why is it working this way and what can I do to lessen the performance decrease on hosted page?
Amazingly everything works well and bug doesn't exist on CodePen, yet on Github it still persists.
After getting some feedback that my site works well for other users I shared it on CodePen and it also worked fine, day later somebody said there could be an extension that could do something like that and indeed Adblocker Ultimate caused the slow performance.

How to change opacity of element when scrolled to bottom within media query

I just want to ask. I want to make the product image thumbnail in shopify disappear when I scrolled down to bottom of the page, and I want a bit of transition with it.. I really can't figure out how to do this..
Here's my code..
https://jsfiddle.net/vsLdz4qb/1/
function myFunction(screenWidth) {
if (screenWidth.matches) { // If media query matches
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
document.getElementByClass("product-single__thumbnails").style.transition = "0.65s";
document.getElementByClass("product-single__thumbnails").style.opacity = 0;
}
};
}
}
let screenWidth = window.matchMedia("(min-width: 750px)");
myFunction(screenWidth); // Call listener function at run time
screenWidth.addListener(myFunction)
Thank you so much in advance!
The correct document method is document.getElementsByClassName and since it returns an array you need the first element of it so change this:
document.getElementByClass("product-single__thumbnails").style.transition = "0.65s";
document.getElementByClass("product-single__thumbnails").style.opacity = 0;
to:
document.getElementsByClassName("product-single__thumbnails")[0].style.transition = "0.65s";
document.getElementsByClassName("product-single__thumbnails")[0].style.opacity = 0;
You can read more about the method here
You should use getElementsByClassName in place of getElementByClass(This is not correct function)
and this will return an array like structure so you need to pass 0 index, if only one class available on page.
or you can try querySelector(".product-single__thumbnails");
and for transition, you can define that in your .product-single__thumbnails class like: transition: opacity .65s linear; - use here which property, you want to animate.
<!-- [product-image] this is for product image scroll down disappear -->
function myFunction(screenWidth) {
if (screenWidth.matches) { // If media query matches
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
document.getElementsByClassName("product-single__thumbnails")[0].style.opacity = 0;
}
};
}
}
let screenWidth = window.matchMedia("(min-width: 350px)");
myFunction(screenWidth); // Call listener function at run time
screenWidth.addListener(myFunction)
body {
margin:0;
height: 1000px;
}
.product-single__thumbnails {
background-color: red;
color: white;
width: 50px;
height: 50px;
position: fixed;
transition: opacity .65s linear;
border-radius: 4px;
margin: 20px;
text-align: center;
}
<div class="product-single__thumbnails">
<p>red</p>
</div>

How to reset rotation after onclick execution and make it possible to call it gain

I have a spinning arrows image. On click, it refreshes a captcha. I have it defined in a short css, with a rotation function called on click.
These are the CSS, the HTML and the Javascript:
function rotatearrow() {
arrowreload.style.webkitTransform = 'rotate(360deg)';
arrowreload.style.MozTransform = 'rotate(360deg)';
arrowreload.style.msTransform = 'rotate(360deg)';
}
function reloadcaptcha() {
//does what's needed and plays perfectly
}
.reload {
max-width: 32px;
height: auto;
transform: rotate(0);
transition: all 0.8s;
}
<img id="arrowreload" class="reload" src="../images/reload.png" onclick="rotatearrow(); reloadcaptcha();">
Now the point is: rotatearrow plays at the first click and rotate the arrows, but it never plays after the first time. What am I doing wrong? What changes I have to do to the code?
Every time you click you need to increment the degree:
let degree = 0;
function rotatearrow() {
degree += 360;
arrowreload.style.webkitTransform = `rotate(${degree}deg)`;
}
.reload {
max-width: 32px;
height: auto;
transform: rotate(0);
transition: all 0.8s;
}
<img id="arrowreload" class="reload" src="http://pluspng.com/img-png/triangle-png-triangle-png-clipart-2400.png" onclick="rotatearrow();">
This will work, we just need to keep incrementing the rotation value in multiples i.e -- 360,720,1080.....
function rotatearrow() {
var transformValue = document.getElementById('arrowreload').style.webkitTransform;
var transformInteger = transformValue ? ((transformValue.match(/\d+/g).map(Number)[0]/360)+1) : 1;
document.getElementById('arrowreload').style.webkitTransform = 'rotate('+(360*transformInteger)+'deg)';
}
Hope this is helpful, also this doesn't require external variable declaration...

Why animation play state is always running?

I couldn't get the animation play state to be paused with css3 animation even after using animation-iteration-count to 1 and animation-fill-mode to forwards:
var isRunning = window.getComputedStyle(
document.querySelector('div')
).getPropertyValue('animation-play-state');
setInterval(function(){
console.log(isRunning);
},1000)
#keyframes foo {
0% {
width: 0;
}
100% {
width: 50%;
}
}
div {
animation: foo 2s linear 0s 1 normal forwards;
background-color: #f00;
height: 3px;
}
<div></div>
I should get animation-play-state to be paused when it finishes the animation.
Actually, the following answer that I provided doesn't work for me. In fact, I was working with a pseudo element and pseudo element doesn't accept addEventListener. So, my final solution would be to use this only way.
var isRunning = window.getComputedStyle(
document.querySelector('div'), ':after'
).getPropertyValue('animation-play-state');
Sadly, CSS doesn't seem to set play state to paused when animation is finished. To conclude this question has no findings or solution?
The value of animation-play-state doesn't change once an animation finishes all its iterations.
You'll need to listen for the animationend event and change the value manually:
document.querySelector('div').addEventListener('animationend', function() {
this.style.animationPlayState = 'paused';
console.log(window.getComputedStyle(this).getPropertyValue('animation-play-state'));
});
#keyframes foo {
0% {
width: 0;
}
100% {
width: 50%;
}
}
#foo {
animation: foo 2s linear 0s 1 normal forwards;
background-color: #f00;
height: 3px;
}
<div id=foo></div>
... though I don't really see any reason to do so when your animation-fill-mode is already set to forwards.
I don't know why css3 keeps animation play state to running but go with the animationend event:
var x = document.getElementById("myDIV");
x.addEventListener("animationend", function(){
console.log('paused'); // animation is end
});
Here's the reference for animationend: https://developer.mozilla.org/en-US/docs/Web/Events/animationend

Potential function overloading in javascript: naming issue

I am new to Javascript but I was able to piece together something to create a random background image on page load. This was successfully used for a Div object on the page.
Since this worked well, I wanted to use this command again for a second Div object on the same page. Both Divs had separate CSS style names so I thought this would be fine. However as soon as I use both commands together, only one will work.
I assumed it was an overloading problem, but I tried renaming everything I could and it still hasn't solved it. Is there something else I need to rename that I'm missing or do I need to frame the two separate commands differently?
Below is the JS code, CSS and HTML:
Thanks in advance!
/*! random background image 2*/
window.onload = function frontimage() {
var thediv2 = document.getElementById("topimg");
var imgarray2 = new Array("f1.svg", "f2.svg");
var spot2 = Math.floor(Math.random()* imgarray2.length);
thediv2.style.background = "url(img/f-img/"+imgarray2[spot2]+")";
thediv2.style.backgroundSize = "70%";
thediv2.style.backgroundAttachment = "fixed";
thediv2.style.backgroundRepeat = "no-repeat";
thediv2.style.zIndex = "2";
thediv2.style.backgroundColor = "rgba(255,204,255,0.5)";
}
/*! random background image 1*/
window.onload = function backimage() {
var thediv = document.getElementById("imgmain");
var imgarray = new Array("b1.jpg", "b2.jpg", "b3.jpg", "b4.jpg", "b5.jpg");
var spot = Math.floor(Math.random()* imgarray.length);
thediv.style.background = "url(img/b-img/"+imgarray[spot]+")";
thediv.style.backgroundSize = "100%";
thediv.style.backgroundAttachment = "fixed";
thediv.style.backgroundRepeat = "no-repeat";
thediv.style.zIndex = "1";
}
#bigimg {
clear: both;
float: left;
margin-left: 0;
width: 100%;
display: block;
}
#imgmain {
background: 50% 0 no-repeat fixed;
z-index: 1;
width: 100%;
}
#topimg {
z-index: 2;
position: absolute;
background-image: url(../img/f-img/f2.svg);
background-repeat: no-repeat;
background-position: 50% -25%;
background-size:contain;
width: 100%;
margin: auto;
padding: 0;
}
<div id="bigimg">
<section id="imgmain"></section>
<section id="topimg"></section>
</div>
With addEventListener, you can add as many event handlers as you want.
window.addEventListener('load', function frontimage() {
// ...
});
window.addEventListener('load', function backimage() {
// ...
});
You are overriding your first window.onload by reassigning the callback function.
Try this:
window.onload = function() {
frontimage();
backimage();
}

Categories