Pedro Corchero Murga
Pedro Corchero Murga

Reputation: 505

Delay translate elements with Intersection Observer API

I'm using Intersection Observer API to show the elements when user scrolls to the content. It works well, but I want to delay the div's to be showed, if there are 4 div's, I want the first to be showed, next 0.5 sec the next is shown... not all in the same time. In the example the effect only applies on the first class too, if there are more than 1 class like there are, it didn't apply to the next img classes, only the first. You can see the example in the bottom of this page.

HTML

<section id="staff" style="padding-top: 100px;">
  <div class="col-lg-12 mx-auto mb-5">
    <div class="container">
      <div class="row icons-info">
        <div class="col-xs-12 col-sm-6 col-md-6 col-lg-3">
          <img class="floating show-bottom" src="img/Muñeco 1-08.png">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros. </p>
        </div>
        <div class="col-xs-12 col-sm-6 col-md-6 col-lg-3 ">
          <img class="floating" src="img/Muñeco 2-08.png">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros.</p>
        </div>
        <div class="col-xs-12 col-sm-6 col-md-6 col-lg-3 ">
          <img class="floating" src="img/Muñeco 3-08.png">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros.</p>
        </div>
        <div class="col-xs-12 col-sm-6 col-md-6 col-lg-3">
          <img class="floating" src="img/Muñeco 1-08.png">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros. </p>
        </div>
      </div>
    </div>
  </div>     
</section>

JS

// Instantiate a new Intersection Observer
let observer7 = new IntersectionObserver(onEntry7);
let staff = document.querySelector('.floating');

let element7 = document.querySelector("#staff p");
observer7.observe(element7);

function onEntry7(entry7) {
  if (entry7[0].isIntersecting) {
    staff.classList.add("show-bottom");
  }
}

CSS

.floating {opacity: 0; transition: 1s opacity;}
.floating.show-bottom {opacity: 1;  
  animation: movefromtop 1s alternate infinite;
  animation-iteration-count: 1;
  animation-fill-mode:  forwards;}
@keyframes movefromtop {
  from {
    transform: translateY(-5em);
  }
  to {
    transform: translateY(0em);
  }
}

Upvotes: 4

Views: 1521

Answers (1)

jo_va
jo_va

Reputation: 13963

Use querySelectorAll() to get all inner div elements, then using forEach call your observer.observe() method for all elements. Then in the observer, use the target property to query the inner image and add the show-bottom class to it.

To add a delay between each animation you have to create an animation chain by returning a Promise and using setTimeout(). Also make sure to not chain the same element more than once in the animation if the intersection triggers many times for the same element. For this, use an array of animatedElements to keep track of the elements being animated.

If you only want to animate the elements once, after begin intersected, you can call unobserve on your observer to unregister from further intersection events.

Note: I edited your HTML/CSS to make the grid work in the snippet to demonstrate the sequential animation effect when multiple elements are on the same row. I also added a with-img class to the inner divs so we can query them and pass them to the observe method.

const onEntry7 = animateSequence('.floating', 'show-bottom');
const observer7 = new IntersectionObserver(onEntry7);
const allElements7 = document.querySelectorAll('#staff div.with-img');
allElements7.forEach(e => observer7.observe(e));

function animateSequence(targetSelector, classToAdd, delay = 500) {
  const animatedElements = [];
  let chain = Promise.resolve();

  function show(e) {
    return new Promise((res, rej) => {
      setTimeout(() => {
        e.classList.add(classToAdd);
        res();
      }, delay);
    });
  }
  return function(entries) {
    entries.forEach(entry => {
      if (entry.intersectionRatio > 0) {
        const elem = entry.target.querySelector(targetSelector);
        if (!animatedElements.includes(elem)) {
          animatedElements.push(elem);
          console.clear();
          console.log('chaining', ...animatedElements.map(e => e.getAttribute('data--name')));
          chain = chain.then(() => show(elem));
          observer7.unobserve(entry.target);
        }
      }
    })
  }
}
.floating {
  opacity: 0;
  transition: 1s opacity;
  width: 157px;
  height: 220px;
}
.floating.show-bottom {
  opacity: 1;  
  animation: movefromtop 1s alternate infinite;
  animation-iteration-count: 1;
  animation-fill-mode:  forwards;
}
@keyframes movefromtop {
  from { transform: translateY(-5em); }
  to { transform: translateY(0em); }
}
section#staff {
  margin-top: 200px;
  margin-bottom: 200px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
Scroll Down
<section id="staff" style="padding-top: 100px;">
  <div class="col-lg-12 mx-auto mb-5">
    <div class="container">
      <div class="row icons-info">
        <div class="with-img col-xs-12 col-xs-6 col-sm-6 col-md-6 col-lg-3">
          <img class="floating" src="https://lagaleramagazine.es/rucab/img/Muñeco 1-08.png" data--name="1">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros. </p>
        </div>
        <div class="with-img col-xs-12 col-xs-6 col-sm-6 col-md-6 col-lg-3 ">
          <img class="floating" src="https://lagaleramagazine.es/rucab/img/Muñeco 2-08.png" data--name="2">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros.</p>
        </div>
        <div class="with-img col-xs-12 col-xs-6 col-sm-6 col-md-6 col-lg-3 ">
          <img class="floating" src="https://lagaleramagazine.es/rucab/img/Muñeco 3-08.png" data--name="3">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros.</p>
        </div>
        <div class="with-img col-xs-12 col-xs-6 col-sm-6 col-md-6 col-lg-3">
          <img class="floating" src="https://lagaleramagazine.es/rucab/img/Muñeco 1-08.png" data--name="4">
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nibh justo, tincidunt sed felis vitae, egestas scelerisque eros. </p>
        </div>
      </div>
    </div>
  </div>     
</section>

Upvotes: 4

Related Questions