简体   繁体   中英

Fade-in fade-out exactly once

I want to make it so that elements with the class fade fade in when they are first visible on the screen then fade out when they leave the screen.

I want this animation to only happen once.

Below is what I have tried.

.fade {
   /* transition: opacity 0.9s ease-in;*/
    opacity: 0;   
  }

.fade.visible {
  transition: opacity 0.9s ease-in;
  opacity: 1;
}
window.addEventListener('scroll', fade)
function fade()
{
  let animation=document.querySelectorAll('.fade');
   for (let i=0; i<animation.length; i++)
    {
     let windowheight=window.innerHeight;
     let top=animation[i].getBoundingClientRect().top;
     if (top < windowheight)
     {
       animation[i].classList.add('visible');
     }
     else
     {
       animation[i].classList.remove('visible');
     }    
    }
}

Use the IntersectionObserver API instead of expensive scroll listeners!

Here's an example that triggers a classList change when an Element is in viewport - based on this answer , with the only difference that this one uses classList.add instead of classList.toggle :

 const inViewport = (entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add("is-inviewport"); } }); }; const Obs = new IntersectionObserver(inViewport); const obsOptions = {}; //See: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options // Attach observer to every [data-inviewport] element: const ELs_inViewport = document.querySelectorAll('[data-inviewport]'); ELs_inViewport.forEach(EL => { Obs.observe(EL, obsOptions); });
 [data-inviewport] { /* FOR THIS DEMO ONLY */ width:100px; height:100px; background:#0bf; margin: 150vh 0; } /* inViewport */ [data-inviewport="fade"] { transition: opacity 2s; opacity: 0; } [data-inviewport="fade"].is-inviewport { transform: rotate(180deg); opacity: 1; }
 Scroll down... <div data-inviewport="fade"></div> <div data-inviewport="fade"></div>

Other than removing the event listener (which seems like doesn't work for you for some reason) a simple change would be to add a global boolean flag which you toggle once after performing the animation. If the flag is true, just return early from the fade function

Something like this should work or at least be pretty close

let animationHappened = false; // flag to keep track of animation

function fade() {
  // only add the visible class if the animation hasn't happened yet, else return early
  if (animationHappened) return;

  let animation = document.querySelectorAll('.fade');

  for (let i = 0; i < animation.length; i++) {
    let windowheight = window.innerHeight;
    let top = animation[i].getBoundingClientRect().top;
    if (top < windowheight) {
      animation[i].classList.add('visible');
    } else {
      animation[i].classList.remove('visible');
    }
  }
  animationHappened = true;
}

This way fade should only animate once so long as animationHappened isn't set to false again anywhere else in your code.

First things first, it's pretty dang inefficient to do querySelectorAll on every scroll, so assuming all those elements are always there, let's just grab them all right away.

We can turn the node list into an array so we can easily remove items as we go. When an element is visible, we just add the visible class and remove it from the array. Then when the array is empty just remove the listener.

To make the items invisible again, we can push the visible items to a second array and perform a similar process. We only remove that listener after all items have faded in then faded out.

 const fadeInElements = Array.from(document.querySelectorAll('.fade')); const makeInvisible = []; let fadeInEmpty = false; window.addEventListener('scroll', fadeIn); window.addEventListener('scroll', fadeOut); function fadeIn() { const windowheight = window.innerHeight; for (let i = 0; i < fadeInElements.length; i++) { const element = fadeInElements[i]; const topOfElement = element.getBoundingClientRect().top; if (topOfElement < windowheight) { element.classList.add('visible'); makeInvisible.push(element); fadeInElements.splice(i, 1); if (fadeInElements.length === 0) { window.removeEventListener('scroll', fadeIn); fadeInEmpty = true; } } } } function fadeOut() { const windowheight = window.innerHeight; for (let i = 0; i < makeInvisible.length; i++) { const element = makeInvisible[i]; const topOfElement = element.getBoundingClientRect().top; if (topOfElement >= windowheight) { element.classList.remove('visible'); makeInvisible.splice(i, 1); if (makeInvisible.length === 0 && fadeInEmpty) window.removeEventListener('scroll', fadeOut); } } }
 div { margin-top: 100px; }.fade { opacity: 0; }.visible { transition: opacity 0.9s ease-in; opacity: 1; }
 <div>1</div> <div class="fade">2</div> <div class="fade">3</div> <div class="fade">4</div> <div class="fade">5</div> <div class="fade">6</div> <div class="fade">7</div> <div class="fade">8</div> <div class="fade">9</div> <div class="fade">10</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM