Search code examples
jquerycssfrontendtranslate-animation

Jquery translate elements based on scroll


I am trying to reproduce the effect found on this page. So the idea is that when the user scroll the tabs reveal themselves. My take on this was to use jquery to translate them along the scroll. My problem is that I gave them specific scroll area during which they will be translated but if the scroll 'jumps' out of the area they will remain out of place.

I cannot find how to fix that, any help is welcome.

Also I'm sure there are smarter ways to achieve this, feel free to point me towards something you'd rather have done.

Here is my code or a codepen to test it

Thank you for reading me and have a nice day.

var hDoc = 0;
$(".classic, .holder, .holder__item").each(function () {
  hDoc += $(this).height();
});
$("body").height(hDoc);

// since both our classic and holder are 100vh, once we scrolled more than classic.height we are passed the sticky element
var hPrev = $(".classic").height();
var itemHeight = $(".item1").height();
// console.log($(window).height());

$(function () {
  $(window).scroll(function () {
    var pageScrolled = $(window).scrollTop();
    console.log("we scrolled: " + pageScrolled);
    //   if we scrolled the length of hPrev(.classic) but not more then hPrev and the size of one item
    if (pageScrolled > hPrev && pageScrolled < hPrev + itemHeight) {
      //     we translate the first item by the length scrolled minus the .classic section divided by the window height
      //       this calculus cant go all the way up TO FIX
      // var translate = ((pageScrolled - hPrev) / $(".classic").height()) * 100;
      $(".item1").css({
        transform: "translateY(-" + (pageScrolled - hPrev) + "px"
      });
      // "transform": "translateY("+ transform + $(window).scrollTop() +"px)"
    } else if (
      pageScrolled > hPrev + itemHeight &&
      pageScrolled < hPrev + itemHeight * 2
    ) {
      // var translate = ((pageScrolled - hPrev ) / $(window).height()) * 100;
      $(".item2").css({
        transform: "translateY(-" + (pageScrolled - (hPrev + itemHeight)) + "px"
      });
    }
  });
});

// This function could be a for each
// bumper avant chaque item, quand on scroll dans son range le titre translate sur la droite
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html,
body {
  height: 100%;
  scroll-behavior: smooth;
}
section {
  height: 100vh;
}
.classic {
  background-color: pink;
}
.holder {
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  //   sets it as sticky so that we can have all the scroll space we need but still visually end on this section
  position: sticky;
  top: 0px;
  overflow: hidden;
  & h2 {
    font-size: 6rem;
  }
  &__item {
    height: 100vh;
    width: 100vw;
    position: absolute;
    &.item1 {
      height: calc(100vh - 80px) !important;
      top: calc(100vh - 80px) !important;
      border: 1px solid black;
      background-color: teal;
    }
    &.item2 {
      height: calc(100vh - 60px) !important;
      top: calc(100vh - 60px) !important;
      border: 1px solid red;
      background-color: crimson;
    }
    &.item3 {
      height: calc(100vh - 40px) !important;
      top: calc(100vh - 40px) !important;
      border: 1px solid purple;
      background-color: DarkSalmon;
    }
    &.item4 {
      height: calc(100vh - 20px) !important;
      top: calc(100vh - 20px) !important;
      background-color: tomato;
      border: 1px solid blue;
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="classic">

</section>
<section class="holder">
  <div class="holder__placeholder">
    <h2>Hello world.</h2>
    <p>Nice to see you again.</p>
  </div>
  <div class="holder__item item1">
    <p>hello hello</p>
  </div>
  <div class="holder__item item2"></div>
  <div class="holder__item item3"></div>
  <div class="holder__item item4"></div>
</section>


Solution

  • Sticky tabbed articles using Pure HTML and CSS

    You don't need any JavaScript at all in order to achieve a similar effect:

    make use CSS custom properties (variables) in HTML like style="--b:3; --t:0;" (t meaning the top index, and b for bottom index), as multiplicator indexes for your sticky elements.
    See how the CSS variables are used in CSS within calc()

    /*QuickReset*/ *, *::before, *::after { margin: 0; box-sizing: border-box; }
    
    html, body { scroll-behavior: smooth; }
    body { font: clamp(11px, 2.5vmin, 18px)/1.4 sans-serif; color: #252525; }
    
    article {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      font-size: 1.2em;
    }
    
    .holder {} /* Whatever you like */
    
    .holder__head {
      padding: 0.4em 2em;
      position: sticky;
      z-index: 1;
      bottom: calc(var(--b) * 2em);
      top: calc(var(--t) * 2em);
    }
    
    .holder__item {
      padding: 2rem;
      position: sticky;
      height: 100vh;
      top: calc((var(--t) + 1) * 1.6em);
      padding-bottom: 8em;
    }
    
    /* Just some colors... */
    .bg-0 { background: #000000; color: #b3ab9d;}
    .bg-1 { background: #817466; }
    .bg-2 { background: #a2a093; }
    .bg-3 { background: #a5882a; }
    .bg-4 { background: #b29d73; }
    <article class="bg-0"><h1>How We Work</h1></article>
    <article><h2>Welcome</h2><p>Scroll down to find out more</p></article>
    
    <section class="holder">
    
      <header class="holder__head bg-3" style="--t:0; --b:3;">1 STEP &mdash; IDENTIFYING STRENGTHS AND WEAKNESSES</header>
      <article class="holder__item bg-3" style="--t:0;">
        <h2>IDENTIFYING STRENGTHS AND WEAKNESSES</h2>
        <p>OUR MULTIDISCIPLINARY TEAM IS HERE TO IDENTIFY BOTH YOUR STRENGTHS AND WEAKNESSES, WITH THE AMBITION TO UNDERSTAND HOW TO BEST ASSIST IN GROWING YOUR BRAND AND REACHING YOUR COMMUNITY.</p>
      </article>
    
      <header class="holder__head bg-0" style=" --t:1; --b:2;">2 STEP &mdash; PUTTING THE PIECESTOGETHER</header>
      <article class="holder__item bg-0" style="--t:1;">
        <h2>PUTTING THE PIECESTOGETHER</h2>
        <p>BASED ON RESEARCH AND DISCUSSIONS, WE WILL SINGLE OUT THE BEST MEDIUM OR MEDIUMS TO TRANSLATE YOUR BRAND.</p>
      </article>
    
      <header class="holder__head bg-2" style="--t:2; --b:1;">3 STEP &mdash; FINE-TUNING YOUR CONTENT</header>
      <article class="holder__item bg-2" style="--t:2;">
        <h2>FINE-TUNING YOUR CONTENT</h2>
        <p>BY PRESENTING OUR WORK TO YOU THROUGH OPEN DIALOGUE, WE WILL FINE-TUNE YOUR CONTENT BASED ON FEEDBACK AND OUR COLLECTIVE EXPERTISE.</p>
      </article>
    
      <header class="holder__head bg-1" style="--t:3; --b:0;">4 STEP &mdash; COMPLETING THE PICTURE</header>
      <article class="holder__item bg-1" style="--t:3;">
        <h2>COMPLETING THE PICTURE</h2>
        <p>WHETHER PRODUCING PHOTOGRAPHY, FILM, CURATING YOUR DIGITAL PLATFORMS OR CREATING A COHESIVE BRAND IDENTITY AND STRATEGY, WE WILL PACKAGE UP ALL OUR WORK AND ASSETS INTO AN ORGANISED LIBRARY A PACKAGE THAT IS SIMPLE FOR YOU TO ENGAGE WITH.</p>
      </article>
    
    </section>
    
    <article class="bg-4"><h2>First footer</h2></article>
    <article class="bg-0"><h2>Second footer</h2></article>

    Using JavaScript

    Since adding manually those 0..N and N..0 index multipliers can be tedious — see: Staggered animations using CSS on how such a task can be achieved using JavaScript's Element.style.setProperty():

    Example:

    const els = (sel, el) => (el || document).querySelectorAll(sel);
    
    els(".holder").forEach((elHolder) => {
    
      const elsHead = els(".holder__head", elHolder);
      const elsItem = els(".holder__item", elHolder);
      const tot = elsHead.length;
      
      elsHead.forEach((elHead, i) => {
        elHead.style.setProperty("--t", i);
        elHead.style.setProperty("--b", tot - 1 - i);
      });
      
      elsItem.forEach((elItem, i) => {
        elItem.style.setProperty("--t", i);
        elItem.style.paddingBottom = `${tot * 1.2}em`
      });
    });
    /*QuickReset*/ *, *::before, *::after { margin: 0; box-sizing: border-box; }
    
    html, body { scroll-behavior: smooth; }
    body { font: clamp(11px, 2.5vmin, 18px)/1.4 sans-serif; color: #252525; }
    
    article {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      font-size: 1.2em;
    }
    
    .holder {} /* Whatever you like */
    
    .holder__head {
      padding: 0.4em 2em;
      position: sticky;
      z-index: 1;
      bottom: calc(var(--b) * 2em);
      top: calc(var(--t) * 2em);
    }
    
    .holder__item {
      padding: 2rem;
      position: sticky;
      height: 100vh;
      top: calc((var(--t) + 1) * 1.6em);
    }
    
    /* Just some colors... */
    .bg-0 { background: #000000; color: #b3ab9d;}
    .bg-1 { background: #817466; }
    .bg-2 { background: #a2a093; }
    .bg-3 { background: #a5882a; }
    .bg-4 { background: #b29d73; }
    <article class="bg-0"><h1>How We Work</h1></article>
    <article><h2>Welcome</h2><p>Scroll down to find out more</p></article>
    
    <section class="holder">
    
      <header class="holder__head bg-3">1 STEP &mdash; IDENTIFYING STRENGTHS AND WEAKNESSES</header>
      <article class="holder__item bg-3">
        <h2>IDENTIFYING STRENGTHS AND WEAKNESSES</h2>
        <p>OUR MULTIDISCIPLINARY TEAM IS HERE TO IDENTIFY BOTH YOUR STRENGTHS AND WEAKNESSES, WITH THE AMBITION TO UNDERSTAND HOW TO BEST ASSIST IN GROWING YOUR BRAND AND REACHING YOUR COMMUNITY.</p>
      </article>
    
      <header class="holder__head bg-0">2 STEP &mdash; PUTTING THE PIECESTOGETHER</header>
      <article class="holder__item bg-0">
        <h2>PUTTING THE PIECESTOGETHER</h2>
        <p>BASED ON RESEARCH AND DISCUSSIONS, WE WILL SINGLE OUT THE BEST MEDIUM OR MEDIUMS TO TRANSLATE YOUR BRAND.</p>
      </article>
    
      <header class="holder__head bg-2">3 STEP &mdash; FINE-TUNING YOUR CONTENT</header>
      <article class="holder__item bg-2">
        <h2>FINE-TUNING YOUR CONTENT</h2>
        <p>BY PRESENTING OUR WORK TO YOU THROUGH OPEN DIALOGUE, WE WILL FINE-TUNE YOUR CONTENT BASED ON FEEDBACK AND OUR COLLECTIVE EXPERTISE.</p>
      </article>
    
      <header class="holder__head bg-1">4 STEP &mdash; COMPLETING THE PICTURE</header>
      <article class="holder__item bg-1">
        <h2>COMPLETING THE PICTURE</h2>
        <p>WHETHER PRODUCING PHOTOGRAPHY, FILM, CURATING YOUR DIGITAL PLATFORMS OR CREATING A COHESIVE BRAND IDENTITY AND STRATEGY, WE WILL PACKAGE UP ALL OUR WORK AND ASSETS INTO AN ORGANISED LIBRARY A PACKAGE THAT IS SIMPLE FOR YOU TO ENGAGE WITH.</p>
      </article>
    
    </section>
    
    <article class="bg-4"><h2>First footer</h2></article>
    <article class="bg-0"><h2>Second footer</h2></article>