1

So I'm making an image changer (you could call it like that), the point is that an image changes after a certain time. I have coded the system already, my problem is that I'm adding a fade-in animation through a class, so what I want is for the image to fade in when it appears. Just look at my code:

The HTML:

    <figure id="changing-image"></figure>

The CSS:

.fade-in{
    animation: fadeIn 1.5s;
}

@keyframes fadeIn {
    0% { opacity: 0; }
    100% { opacity: 1; }
}


#changing-image{
      background-image: url(../images/asides/Interior.jpg);
      background-position: center;
      background-repeat: no-repeat;
      background-size: cover;
      width:  60rem;
      height: 50rem;
      overflow: hidden;
      border-radius: 2px;
  }

The JavaScript:

 const imagesArray = ["url(img1)", "url(img2)", "url(img3)", "url(img4)"];
const changeImage = i => {
    if (i < imagesArray.length) {
        setTimeout(function() {
            document.getElementById('changing-image').style.backgroundImage = imagesArray[i];
            document.getElementById('changing-image').classList.add("fade-in")
            changeImage(++i);
        }, 2600);
    } else if (i === imagesArray.length) {
        changeImage(0);
    }
    document.getElementById('changing-image').classList.remove("fade-in")
}
  • Try moving the `classList.remove` immediately before the `classList.add` – Brett Donald Jun 21 '22 at 01:39
  • Does this answer your question? [How can I change an element's class with JavaScript?](https://stackoverflow.com/questions/195951/how-can-i-change-an-elements-class-with-javascript) – cSharp Jun 21 '22 at 01:39
  • you have a strange way of using the figure tag, it prevents me from answering you! https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure – Mister Jojo Jun 21 '22 at 01:40
  • @BrettDonald read the PO title : **after the action has happened** – Mister Jojo Jun 21 '22 at 01:42
  • Add the fade-in class in the element itself. Just change the image url. it should work. OR maybe insert an img tag and change it's url with fade-in fixed. – Rohit Mahto Jun 21 '22 at 02:17
  • @MisterJojo the code contains a loop, so all you need to do is remove the class right before you need to add it back in. – Brett Donald Jun 22 '22 at 01:32
  • @BrettDonald actuly his code made a flash and then a fade in animation... and the point of his question is how to detect the end of his animation and not re solve his bad code example – Mister Jojo Jun 22 '22 at 01:36

2 Answers2

1

You are lookimg for the animationend event
but you missed the issue of initial load time of each new image which may exceed your setInterval() timeout

I've chosen here to just use a promise.all to take care of this

const
  imageArray = 
    [ { path:'https://picsum.photos/id/237/200/300', status:'none', img:null }
    , { path:'https://picsum.photos/id/238/200/300', status:'none', img:null }
    , { path:'https://picsum.photos/id/239/200/300', status:'none', img:null }
    , { path:'https://picsum.photos/id/240/200/300', status:'none', img:null }
    ]
, loadImage = ref => new Promise( resolve =>
    {
    ref.img = new Image();
    ref.img.onload  =_=>{ ref.status='OK';  resolve() };
    ref.img.onerror =_=>{ ref.status='bad'; resolve() };
    ref.img.src = ref.path;
    })
, changingImage = document.querySelector('#changing-image')
  ;
// *******************************************************
// animationend event 
changingImage.onanimationend =_=>
  {
  changingImage.classList.replace('fade-in','opa1')
  }
// *******************************************************


// main code...
Promise
.all(imageArray.map(el=>loadImage(el) )) // load all imgs
.then(()=>
  {
  for(let i=imageArray.length;i--;)    // remove 
    {                                  // unloaded images
    if (imageArray[i].status==='bad') 
      imageArray.splice(i, 1)
    }
  if (imageArray.length===0)
    {
    console.log('problemo! no image are loaded!')
    return
    }

  let currentImg = 0
  setImage(currentImg)

  setInterval(()=>
    {
    currentImg = ++currentImg % imageArray.length
    setImage(currentImg)
    }
    , 5000)
  })

function setImage(indx)
  {
  changingImage.className = 'opa0' 
  changingImage.src = imageArray[indx].path 
  changingImage.classList.replace('opa0','fade-in')
  }
.opa0 { opacity: 0; }
.opa1 { opacity: 1; }

.fade-in {
  animation: fadeIn 3s;
  }
@keyframes fadeIn {
  0%   { opacity: 0; }
  100% { opacity: 1; }
  }

#changing-image {
  display : block;
  width   : 200px;
  height  : 300px;
  padding : 5px;
  border  : 1px solid lightseagreen;
  margin  : 1em;
  }
<img src="" alt="" id="changing-image" class="opa0">
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
1

I asked myself the question of the implementation of adding a fade-out before the arrival of the next image in fade-in.

Changing the image just by modifying the src attribute makes the previous image appear a second time in "flash", because browsers use their cache

So I opted for a second strategy with an appendChild() method

const
  gApp = 
    { imgIndx : 0
    , cImg    : null
    , intvRef : null
    }
, imageArray = 
    [ { path:'https://picsum.photos/id/237/200/200', status:'none', img:null }
    , { path:'https://picsum.photos/id/238/200/200', status:'none', img:null }
    , { path:'https://picsum.photos/id/239/200/200', status:'none', img:null }
    , { path:'https://picsum.photos/id/240/200/200', status:'none', img:null }
    ]
, loadImage = ref => new Promise( resolve =>
    {
    ref.img = new Image();
    ref.img.onload  =_=>{ ref.status='OK';  resolve() };
    ref.img.onerror =_=>{ ref.status='bad'; resolve() };
    ref.img.src = ref.path;
    })
, changingImage = document.querySelector('#changing-image')
  ;
  

/* -------------------  animationend event ------------------ */

changingImage.onanimationend =_=>
  {
  gApp.cImg.classList.replace('fade-in','opacityOne')

  if (gApp.cImg.classList.replace('fade-out','opacityZero'))
    {
    changingImage.innerHTML = ''
    gApp.imgIndx            = ++gApp.imgIndx % imageArray.length
    gApp.cImg               = imageArray[ gApp.imgIndx ].img
    changingImage.appendChild( gApp.cImg )
    gApp.cImg.classList.replace('opacityZero','fade-in')
    }
  }

/* -------------------  animationend event ------------------ */


// main code...
Promise
.all(imageArray.map(el=>loadImage(el) )) // load all imgs
.then(()=>
  {
  for(let i=imageArray.length;i--;)    // remove 
    {                                  // unloaded images
    if (imageArray[i].status==='bad')
      imageArray.splice(i, 1)
    else
      imageArray[i].img.className  = 'opacityZero'
    }
  if (imageArray.length===0)
    {
    console.log('problemo! no image are loaded!')
    return
    }

  gApp.cImg               = imageArray[ gApp.imgIndx ].img
  changingImage.innerHTML = ''

  changingImage.appendChild( gApp.cImg )
  gApp.cImg.classList.replace('opacityZero','fade-in')

  gApp.intvRef = setInterval(()=>
    { gApp.cImg.classList.replace('opacityOne', 'fade-out') }
    , 4000) 
  })

StartStopAnim.onclick=_=>
  {
  if (StartStopAnim.classList.toggle('stopped'))
    {
    clearInterval( gApp.intvRef )
    }
  else
    {
    gApp.cImg.className = 'opacityZero'
    gApp.cImg.classList.replace('opacityZero','fade-in')

    gApp.intvRef = setInterval(()=>
      { gApp.cImg.classList.replace('opacityOne', 'fade-out') }
      , 4000) 
    }
  }
.opacityZero { opacity: 0; }
.opacityOne  { opacity: 1; }

.fade-in  { animation: fadeIn  2s; }
.fade-out { animation: fadeOut 1s; }

@keyframes fadeIn {
  0%   { opacity: 0; }
  100% { opacity: 1; }
  }
@keyframes fadeOut {
  0%   { opacity: 1; }
  100% { opacity: 0; }
  }
#changing-image {
  display : block;
  width   : 200px;
  height  : 200px;
  padding : 5px;
  border  : 1px solid lightseagreen;
  margin  : 1em;
  }
#StartStopAnim::after         { content : 'Stop animation' }
#StartStopAnim.stopped::after { content : '...Restart animation' }
<figure id="changing-image"></figure>
<button id="StartStopAnim"></button>
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40