2

I'm building a carousel (slideshow) effect for my website and I have little issue. At every image change I want to make fade effect. So I've added a class with animation for it. And here the problem comes.

This function is firing every 3 sec (setInterval)

let sliderInterval = setInterval(nextImg, 3000);

function nextImg() {
    imgChange(sliderNum + 1);
}

const heroImg = document.querySelector('.hero__image');

function imgChange(x) {
    heroImg.classList.remove("fade");
    sliderNum = (x + imgLocations.length) % imgLocations.length;
    heroImg.src = imgLocations[sliderNum];
    heroImg.classList.add("fade");
}

Fade effect:

.fade {
    animation: fade 1.5s ease-in-out;
}

@keyframes fade {
    from {opacity: .4}
    to {opacity: 1}
}

<div class="hero">
<img class="hero__image" src="photo1">
</div>

It works only for first image switch. Altough at the start of function it shall remove the class fade I see in function that it stays there in element and won't gone. It doesn't matter if I try to put this effect on hero section or img within it.

Travis J
  • 79,093
  • 40
  • 195
  • 263
icelandico
  • 300
  • 5
  • 19

1 Answers1

4

The problem is that css animations with keyframes will by default only run once. It is possible to alter the amount of times they run, but that will run them constantly in a loop which is undesirable.

Instead, what needs to happen is the animation needs to be reset. In order to do this, the element needs to have its animation name removed (fade in this case). This can be done with animation-name: none;, however, the rule needs to be placed on the element when fade is removed. Note the change to the selector to make the fade animation name take precedence when applied.

On top of this, it is important to note that if you remove and add the same class in a function, due to the way that browsers work, nothing will happen. In order for the browser to recognize any changes made, a page repaint must occur (here is a list of what makes that happen). In order to force the page to repaint, I chose to use offsetHeight, which is why you see heroImg.offsetHeight used in the code (note that it only needs to be read, it doesn't have to be used or assigned).

I mocked your image with a div for convenience.

let sliderInterval = setInterval(imgChange, 3000);

const heroImg = document.querySelector('.imgMock');

function imgChange() {
    heroImg.classList.remove("fade");
    heroImg.offsetHeight; // force repaint to recognize `animation-name: none;`
    heroImg.classList.add("fade");
}
.imgMock.fade {
    animation: fade 1.5s ease-in;
}

@keyframes fade {
    from {opacity: .4}
    to {opacity: 1}
}

.imgMock {
    height:50px;
    width:50px;
    background-color:black;
    animation-name: none;
}
<div class="hero">
 <div class="imgMock"></div>
</div>
Travis J
  • 79,093
  • 40
  • 195
  • 263
  • Thanks! That's kinda solution. But now for whole time of image duration (3 seconds) the animations is firing every 1.5 s. So final goal would be, how to set the animation working only for the image transition? So it will change image with animation and fire at the next slide change? – icelandico May 30 '18 at 19:59
  • 1
    @icelandico - Sorry, you were correct the infinite setting was inaccurate and did not properly maintain the animation durations as a result. Please see my edit, and an improved more accurate way of handling this scenario. I hope you find it satisfactory. – Travis J May 30 '18 at 21:17