Search code examples
javascriptjquerycssanimationscroll

On scroll down images are moving up


I want on scroll down, that the images are moving up a bit, like on this website:

example site (digital does)

Images example

Down the website on digitaldoes you can see what i mean. The images are moving up a bit if you scroll down. Is it maby a lazy-load with images that are fading in or an animation where the images moving for example 5px up?

Here is a: jsfiddle updated!!!

$(document).ready(function () {
    
    $(".img-scroll").css("display", "none");
    
    $(window).scroll(function() {
        $( ".img-scroll" ).addClass("animate");
        $( ".img-scroll" ).css("display", "block");
        console.log("Hey");
    });
    
});
.img-scroll {
  -moz-transition: top 10s;
  transition: top 10s;
}


.img-scroll.animate {
  top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"/>

<div class="row">
  <header class="arbeiten">
    <h2>{ Logos }</h2>
  </header>
</div>

<div id="links">

  <div class="row">

    <div class="col-xxl-1"></div>

    <div class="col-12 col-md-6 col-lg-4 col-xxl-5">
      <div class="card shadow-sm first-image">
        <a href="https://www.fillmurray.com/1000/1000" title="Test - Logo #1">
          <img src="https://www.fillmurray.com/300/150" alt="Test - Logo #1" class="card-img-top logo-hover">
        </a>

        <div class="card-body">
          <p class="card-text">Test - Logo #1</p>
          <div class="d-flex justify-content-between align-items-center"></div>
        </div>
      </div>
    </div>

    <div class="col-12 col-md-6 col-lg-4 col-xxl-5">
      <div class="card shadow-sm">
        <a href="https://www.fillmurray.com/1000/1000" title="Test - Logo #2">
          <img src="https://www.fillmurray.com/300/150" alt="Test - Logo #2" class="card-img-top logo-hover">
        </a>
        <div class="card-body">
          <p class="card-text">Test - Logo #2</p>
          <div class="d-flex justify-content-between align-items-center"></div>
        </div>
      </div>


      <div class="col-12 col-md-6 col-lg-4 col-xxl-5 img-scroll">
        <div class="card shadow-sm first-image">
          <a href="https://www.fillmurray.com/1000/1000" title="Test - Logo #3">
            <img src="https://www.fillmurray.com/300/150" alt="Test - Logo #3" class="card-img-top logo-hover">
          </a>

          <div class="card-body">
            <p class="card-text">Test - Logo #3</p>
            <div class="d-flex justify-content-between align-items-center"></div>
          </div>
        </div>
      </div>

      <div class="col-xxl-1"></div>
      <div class="col-xxl-1"></div>

    </div>
    
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js"></script>

I hope, someone can help me...


Solution

  • The most important step is to check if an <img> element is entering the current viewport.

    We can use the Intersection Observer API.

    const options = {
      rootMargin: "0px",
      threshold: 0.5
    };
    
    const inViewPortObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          let target = entry.target;
          let dataSrc = target.getAttribute('data-src');
          target.src = dataSrc;
          target.classList.replace('lazyload', 'lazyloaded');
        }
      });
    }, options);
    

    By adjusting the threshold value we can define when the intersecting value returns true:
    E.g treshold:0.5 => element is intersection if 50% of it's height is in viewport.

    Now we can define a callback:
    if(entry.isIntersecting){}
    If element is in viewport – set the src attribute from data-src and toggle css classes from 'lazyload' to 'lazyloaded'.

    Most lazyload libraries use a similar syntax or api concept like this:

    <img data-src="https://www.fillmurray.com/480/480" class="lazyload">
    

    The actual image src/URL is defined by a data attribute: data-src.
    Lazy loading can significantly improve the overall performance and user experience by loading images only if they are really required.

    The animation is done by toggling between css classes:
    E.g .lazyload (not loaded yet) and lazyloaded (loaded)

    .lazyload {
        opacity: 0;
        transform: translateY(100%);
    }
    
    .lazyloaded {
        opacity: 1;
        transform: translateY(0%);
    }
    

    Simple Example: using js IntersectionObserver()

    const options = {
      rootMargin: "0px",
      threshold: 0.5
    };
    
    const inViewPortObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          let target = entry.target;
          let dataSrc = target.getAttribute('data-src');
          target.src = dataSrc;
          target.classList.replace('lazyload', 'lazyloaded');
        }
      });
    }, options);
    
    const images = document.querySelectorAll('.lazyload');
    images.forEach(function(img, i) {
      inViewPortObserver.observe(img);
    })
    * {
      box-sizing: border-box
    }
    
    header {
      margin-bottom: 40vmin;
    }
    
    main {
      padding-bottom: 50em;
    }
    
    section {
      margin-bottom: 20em;
    }
    
    .row {
      display: flex;
    }
    
    .row>* {
      flex: 1;
    }
    
    .card-img-top {
      transition: 2s;
    }
    
    .lazyload {
      opacity: 0;
      transform: translateY(100%);
    }
    
    .lazyloaded {
      opacity: 1;
      transform: translateY(0%);
    }
    
    img {
      aspect-ratio: 2/1;
      display: block;
      background-color: #ccc;
      width: 100%;
      max-width: 100%;
      object-fit: cover;
      object-position: 50% 50%;
    }
    <div class="row">
      <header class="arbeiten">
        <h2>{ Logos }</h2>
        <p>Please scroll down …</p>
      </header>
    </div>
    
    <main>
      <section>
        <div class="row">
          <figure>
            <img data-src="https://www.fillmurray.com/480/480" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
          <figure>
            <img data-src="https://www.fillmurray.com/300/150" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
        </div>
    
        <div class="row">
          <figure>
            <img data-src="https://www.fillmurray.com/300/200" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
          <figure>
            <img data-src="https://www.fillmurray.com/200/300" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
          <figure>
            <img data-src="https://www.fillmurray.com/400/240" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
        </div>
      </section>
    
      <section>
        <div class="row">
          <figure>
            <img data-src="https://www.fillmurray.com/320/240" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
    
          <figure>
            <img data-src="https://www.fillmurray.com/300/150" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
          <figure>
            <img data-src="https://www.fillmurray.com/333/111" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
          <figure>
            <img data-src="https://www.fillmurray.com/444/333" alt="Test - Logo #1" class="lazyload card-img-top logo-hover">
          </figure>
        </div>
      </section>
    </main>

    Example 2: observe a parent element and lazyload images

    The main difference: we're observing viewport intersections for parent elements like the .card class.

    const options = {
      rootMargin: "0px",
      threshold: 0.1
    };
    
    const inViewPortObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          let target = entry.target;
          let img = target.querySelector('img');
          let dataSrc = img.getAttribute('data-src');
          img.src = dataSrc;
          target.classList.add('inViewPort');
        }
      });
    }, options);
    
    const cards = document.querySelectorAll('.card');
    cards.forEach(function(card, i) {
      inViewPortObserver.observe(card);
    })
    * {
      box-sizing: border-box
    }
    
    header {
      margin-bottom: 60vmin;
    }
    
    main {
      padding-bottom: 50em;
    }
    
    section {
      margin-bottom: 20em;
    }
    
    .row {
      display: flex;
      gap: 1em;
    }
    
    .row>* {
      flex: 1;
    }
    
    .card {
      transition: 2s;
      transform: translateY(100%);
      border: 2px solid red;
      opacity: 0;
    }
    
    
    /** optional sibling delay */
    
    .row .card:nth-of-type(2) {
      transition-delay: 0.5s;
    }
    
    .row .card:nth-of-type(3) {
      transition-delay: 0.75s;
    }
    
    .row .card:nth-of-type(4) {
      transition-delay: 1s;
    }
    
    .row .card:nth-of-type(5) {
      transition-delay: 1.25s;
    }
    
    .row .card:nth-of-type(6) {
      transition-delay: 1.5s;
    }
    
    .inViewPort {
      opacity: 1;
      transform: translateY(0%);
    }
    
    img {
      aspect-ratio: 2/1;
      display: block;
      background-color: #ccc;
      width: 100%;
      max-width: 100%;
      object-fit: cover;
      object-position: 50% 50%;
    }
    <div class="row">
      <header class="arbeiten">
        <h2>{ Logos }</h2>
        <p>Please scroll down …</p>
      </header>
    </div>
    
    <main>
      <section>
        <div class="row row1">
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/480/480" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/150" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/222" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/247" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          
          
        </div>
      </section>
    
    
      <section>
        <div class="row row2">
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/320/240" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/111" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/112" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
        </div>
      </section>
    
    
      <section>
        <div class="row row2">
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/320/240" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/111" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
          <div class="card">
            <a href="#">
              <img data-src="https://www.fillmurray.com/300/112" alt="" class="lazyload card-img-top logo-hover">
            </a>
          </div>
        </div>
      </section>