admin管理员组

文章数量:1331457

I have a site and I want each section to take up the screen and as you scroll down the page the sections fade out as the next section es into view. I'm struggling on how this math needs to work.

For one section it's easy which is basically the height of the element and it's distance from the top to get the opacity percentage. However, I just can't quite figure out the math for other sections. I need section 2 to start fading in as section 1 is fading out.

I do not want to scrolljack. I just want a scrolling effect. Rather than override default scrolling behavior I just want to animate in/out as you scroll where a certain Y scroll value = a certain CSS value rather than a sort of "breakpoint" where it animates to a new "slide".

For the proper effect, for example, my math divides the height of the row by 2 so that it's 50% faded out as the next section should be fading in 50%. But, as of now, the math just doesn't work for the other sections.

This site is using vanilla JS as it's supposed to be a simple one pager.

Example below:

    (function () {
        window.addEventListener('scroll', () => {
          const rows = document.querySelectorAll('.row');

          rows.forEach(function (row, index) {
            const distanceToTop = window.pageYOffset;
            const elementHeight = row.offsetHeight * (index + 1) / 2;
            row.style.opacity = ((elementHeight - distanceToTop) / elementHeight * 100) / 100;
          });
        });
      })();
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      padding: 0 0 0 0;
      font-family: sans-serif;
      font-family: 'Manrope', helvetiva, sans-serif;
      transition: background-color 1s;
      color:#fff;
      background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
    }

    header {
      position: fixed;
      top: 0;
      width: 100%;
      background: #fff;
      z-index: 999;
    }

    header h1 {
      font-family: 'Lack';
      font-size: 4em;
      text-align: center;
      padding: 10px;
      color: #100521;
    }

    .row-container .row:first-child {
      opacity: 1;
    }

    .row {
      opacity: 0;
      height: 100vh;
      display: flex;
      flex-direction:column;
    }

    .row-inner {
      display: grid;
      grid-template-columns: 1fr 1fr;
      position: sticky;
      top: 0;
      /* border: 1px solid yellow; */
      padding: 0;
      height: 100vh;
    }

    .column {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-content: center;
      flex: 50%;
      padding: 10px;
    }

    .left-column {
      background-color: transparent;
    }

    .right-column {
      background-color: transparent;
    }

    h1 {
      font-family: 'Unbounded', helvetica, sans-serif;
    }

    h1 {
      margin: 0;
      font-size: 36px;
    }

    h2 {
      margin: 0;
      font-size: 24px;
    }

    p {
      margin: 10px 0;
      font-size: 18px;
      line-height: 1.5;
    }

    img {
      width: 100%;
      height: auto;
    }
<!DOCTYPE html>
<html>
<head>
  <link rel="preconnect" href=""><link rel="preconnect" href="" crossorigin><link href=";family=Unbounded&family=Lack&display=swap" rel="stylesheet">
</head>
  <body>
    <header>
      <h1>My App</h1>
    </header>
    <!-- First row -->
    <div class="row-container">
      <div class="row section-1">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 1</h1>
              <h2>Subhead 1</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src=".jpg" alt="Image 1">
            </div>
          </div>
        </div>
      </div>

      <!-- Second row -->
      <div class="row section-2">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 2</h1>
              <h2>Subhead 2</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src=".jpg" alt="Image 2">
            </div>
          </div>
        </div>
      </div>

      <!-- Third row -->
      <div class="row section-3">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 3</h1>
              <h2>Subhead 3</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src=".jpg" alt="Image 3">
            </div>
          </div>
        </div>
      </div>

      <!-- Fourth row -->
      <div class="row section-4">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 4</h1>
              <h2>Subhead 4</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src=".jpg" alt="Image 4">
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

I have a site and I want each section to take up the screen and as you scroll down the page the sections fade out as the next section es into view. I'm struggling on how this math needs to work.

For one section it's easy which is basically the height of the element and it's distance from the top to get the opacity percentage. However, I just can't quite figure out the math for other sections. I need section 2 to start fading in as section 1 is fading out.

I do not want to scrolljack. I just want a scrolling effect. Rather than override default scrolling behavior I just want to animate in/out as you scroll where a certain Y scroll value = a certain CSS value rather than a sort of "breakpoint" where it animates to a new "slide".

For the proper effect, for example, my math divides the height of the row by 2 so that it's 50% faded out as the next section should be fading in 50%. But, as of now, the math just doesn't work for the other sections.

This site is using vanilla JS as it's supposed to be a simple one pager.

Example below:

    (function () {
        window.addEventListener('scroll', () => {
          const rows = document.querySelectorAll('.row');

          rows.forEach(function (row, index) {
            const distanceToTop = window.pageYOffset;
            const elementHeight = row.offsetHeight * (index + 1) / 2;
            row.style.opacity = ((elementHeight - distanceToTop) / elementHeight * 100) / 100;
          });
        });
      })();
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      padding: 0 0 0 0;
      font-family: sans-serif;
      font-family: 'Manrope', helvetiva, sans-serif;
      transition: background-color 1s;
      color:#fff;
      background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
    }

    header {
      position: fixed;
      top: 0;
      width: 100%;
      background: #fff;
      z-index: 999;
    }

    header h1 {
      font-family: 'Lack';
      font-size: 4em;
      text-align: center;
      padding: 10px;
      color: #100521;
    }

    .row-container .row:first-child {
      opacity: 1;
    }

    .row {
      opacity: 0;
      height: 100vh;
      display: flex;
      flex-direction:column;
    }

    .row-inner {
      display: grid;
      grid-template-columns: 1fr 1fr;
      position: sticky;
      top: 0;
      /* border: 1px solid yellow; */
      padding: 0;
      height: 100vh;
    }

    .column {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-content: center;
      flex: 50%;
      padding: 10px;
    }

    .left-column {
      background-color: transparent;
    }

    .right-column {
      background-color: transparent;
    }

    h1 {
      font-family: 'Unbounded', helvetica, sans-serif;
    }

    h1 {
      margin: 0;
      font-size: 36px;
    }

    h2 {
      margin: 0;
      font-size: 24px;
    }

    p {
      margin: 10px 0;
      font-size: 18px;
      line-height: 1.5;
    }

    img {
      width: 100%;
      height: auto;
    }
<!DOCTYPE html>
<html>
<head>
  <link rel="preconnect" href="https://fonts.googleapis."><link rel="preconnect" href="https://fonts.gstatic." crossorigin><link href="https://fonts.googleapis./css2?family=Manrope&family=Unbounded&family=Lack&display=swap" rel="stylesheet">
</head>
  <body>
    <header>
      <h1>My App</h1>
    </header>
    <!-- First row -->
    <div class="row-container">
      <div class="row section-1">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 1</h1>
              <h2>Subhead 1</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1">
            </div>
          </div>
        </div>
      </div>

      <!-- Second row -->
      <div class="row section-2">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 2</h1>
              <h2>Subhead 2</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src="https://avatars.dicebear./api/adventurer/team-member-2.jpg" alt="Image 2">
            </div>
          </div>
        </div>
      </div>

      <!-- Third row -->
      <div class="row section-3">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 3</h1>
              <h2>Subhead 3</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src="https://avatars.dicebear./api/adventurer/team-member-3.jpg" alt="Image 3">
            </div>
          </div>
        </div>
      </div>

      <!-- Fourth row -->
      <div class="row section-4">
        <div class="row-inner">
          <div class="column left-column">
            <div class="column-inner">
              <h1>Headline 4</h1>
              <h2>Subhead 4</h2>
              <p>Lorem Ipsum dolar Gamet</p>
            </div>
          </div>
          <div class="column right-column">
            <div class="column-inner">
              <img src="https://avatars.dicebear./api/adventurer/team-member-4.jpg" alt="Image 4">
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

Share edited Jan 18, 2023 at 5:53 Oscar Godson asked Jan 9, 2023 at 23:21 Oscar GodsonOscar Godson 32.8k42 gold badges125 silver badges206 bronze badges 3
  • 1 Do you mean like this? – l'L'l Commented Jan 17, 2023 at 23:15
  • 1 You want to use IntersectionObserver, not scroll event listener. – Kosh Commented Jan 18, 2023 at 1:32
  • 1 @I'L'I that is just a faded out styling at the top and bottom of the container. I'm looking for the effect that if you had say <DIV 1> and <DIV 2> where DIV 1 is 100% opacity and DIv 2 is 0%. As you scroll down, based on the viewport, DIV 1 slowly goes to 0% and DIV 2 goes to 100%. Almost like a "crossfade" or "dissolve" except the layer arent on top of each other. – Oscar Godson Commented Jan 18, 2023 at 5:58
Add a ment  | 

4 Answers 4

Reset to default 3

As suggested in one of the ments to your question, it's more effective to use IntersectionObserver rather than the 'scroll' event listener.

If I'm correct, this snippet should help you achieve what you're looking for:

window.addEventListener("load", (event) => {
  let options = {
    rootMargin: "0px",
    threshold: 0.8
  };

  const intersectionHandler = function(entries, observer) {
    entries.forEach((entry) => {
      if (entry.isIntersecting && entry.intersectionRatio > 0.8) {
        entry.target.classList.remove("fadeOut");
        entry.target.classList.add("fadeIn");
      } else {
        entry.target.classList.remove("fadeIn");
        entry.target.classList.add("fadeOut");
      }
    });
  };

  let observer = new IntersectionObserver(intersectionHandler, options);
  document.querySelectorAll(".section").forEach((row, index) => {
    observer.observe(row);
  });
});
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0 0 0 0;
  font-family: sans-serif;
  font-family: "Manrope", helvetiva, sans-serif;
  transition: background-color 1s;
  color: #fff;
  background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
}

header {
  position: fixed;
  top: 0;
  width: 100%;
  background: #fff;
  z-index: 999;
}

header h1 {
  font-family: "Lack";
  font-size: 4em;
  text-align: center;
  padding: 10px;
  color: #100521;
}

.row-container .row:first-child {
  opacity: 1;
}

.row {
  opacity: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.row-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  position: sticky;
  top: 0;
  /* border: 1px solid yellow; */
  padding: 0;
  height: 100vh;
}

.column {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  flex: 50%;
  padding: 10px;
  height: 100vh;
  box-sizing: border-box;
}

.left-column {
  background-color: transparent;
}

.right-column {
  background-color: transparent;
}

h1 {
  font-family: "Unbounded", helvetica, sans-serif;
}

h1 {
  margin: 0;
  font-size: 36px;
}

h2 {
  margin: 0;
  font-size: 24px;
}

p {
  margin: 10px 0;
  font-size: 18px;
  line-height: 1.5;
}

img {
  width: 100%;
  height: auto;
}

.fadeIn {
  animation-name: fadeIn;
  animation-duration: 1s;
  animation-fill-mode: forwards;
}

.fadeOut {
  animation-name: fadeOut;
  animation-duration: .5s;
  animation-fill-mode: forwards;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
<!-- First row -->
<div class="row-container">
  <div class="row section">
    <div class="row-inner">
      <div class="column left-column">
        <div class="column-inner">
          <h1>Headline 1</h1>
          <h2>Subhead 1</h2>
          <p>Lorem Ipsum dolar Gamet</p>
        </div>
      </div>
      <div class="column right-column">
        <div class="column-inner">
          <img src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1">
        </div>
      </div>
    </div>
  </div>

  <!-- Second row -->
  <div class="row section">
    <div class="row-inner">
      <div class="column left-column">
        <div class="column-inner">
          <h1>Headline 2</h1>
          <h2>Subhead 2</h2>
          <p>Lorem Ipsum dolar Gamet</p>
        </div>
      </div>
      <div class="column right-column">
        <div class="column-inner">
          <img src="https://avatars.dicebear./api/adventurer/team-member-2.jpg" alt="Image 2">
        </div>
      </div>
    </div>
  </div>

  <!-- Third row -->
  <div class="row section">
    <div class="row-inner">
      <div class="column left-column">
        <div class="column-inner">
          <h1>Headline 3</h1>
          <h2>Subhead 3</h2>
          <p>Lorem Ipsum dolar Gamet</p>
        </div>
      </div>
      <div class="column right-column">
        <div class="column-inner">
          <img src="https://avatars.dicebear./api/adventurer/team-member-3.jpg" alt="Image 3">
        </div>
      </div>
    </div>
  </div>

  <!-- Fourth row -->
  <div class="row section">
    <div class="row-inner">
      <div class="column left-column">
        <div class="column-inner">
          <h1>Headline 4</h1>
          <h2>Subhead 4</h2>
          <p>Lorem Ipsum dolar Gamet</p>
        </div>
      </div>
      <div class="column right-column">
        <div class="column-inner">
          <img src="https://avatars.dicebear./api/adventurer/team-member-4.jpg" alt="Image 4">
        </div>
      </div>
    </div>
  </div>
</div>

You'll notice I've made some minor modifications to your HTML and CSS, but the magic happens in the Javascript.

I've also used the window "load" event listener to create the observer options, object and callback function when the page is loaded.

You can check the IntersectionObserver API docs for more details on how it works and how to tweak the options or the callback to suit your needs.

Let me know if you have any questions for modifying my code. (I've also had to change your css a bit - the content of the row element was higher than the row element itself what led to some weird issues.)

I have rewritten most of it and it should now work for large row heights. The math for it is just a quadratic formula (Opacity of offset from center of the screen) with it's roots at the height of the row and a maximum at 0 offset with a value of 1.

/** @format */

const calculateOpacity = () => {
   const ROW_CONTAINER = document.querySelector(".row-container");
   const rows = ROW_CONTAINER.children;
   const rowContainerBBox = ROW_CONTAINER.getBoundingClientRect();
  console.log("calc")
   for (const row of rows) {
      const rowBBox = row.getBoundingClientRect();
      const top = rowBBox.top - rowContainerBBox.top;
      const bottom = rowBBox.bottom - rowContainerBBox.bottom;
      if (rowBBox.height > rowContainerBBox.height) {
         const opacityOfOffset = (offset) => {
            const a = -1 / Math.pow(rowBBox.height, 2);
            return Math.max(0, a * Math.pow(offset, 2) + 1);
         };
         row.style.opacity = opacityOfOffset(top + bottom);
         continue;
      }
      const unvisibleHeightTop = -Math.min(0, top);
      const unvisibleHeightBottom = Math.max(0, bottom);
      const visibleHeight = rowBBox.height - unvisibleHeightTop - unvisibleHeightBottom;
      row.style.opacity = visibleHeight / rowBBox.height;
   }
};
document.querySelector(".row-container").addEventListener("scroll", calculateOpacity);
/** @format */
body {
   height: 100%;
   margin: 0;
   padding: 0;
   font-family: sans-serif;
   font-family: "Manrope", helvetiva, sans-serif;
   color: #fff;
}
header {
   height: max(70px, 10vh);
   position: fixed;
   background-color: black;
   width: 100%;
   display: flex;
   align-items: center;
}
header h1 {
   margin: 0;
}

main {
   padding-top: max(70px, 10vh);
   height: 100%;
   box-sizing: border-box;
}

.row-container {
   background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
   height: calc(100vh - max(70px, 10vh));
   overflow-y: auto;
}

.row {
   height: 150vh;
   display: flex;
   align-items: center;
   padding: 10vh 0;
}

.row .row-inner {
   width: 100%;
   height: 100%;
   display: grid;
   grid-template-columns: 1fr 1fr;
}

.row .row-inner .col {
   margin-inline: 10px;
   display: flex;
   align-items: center;
   height: 100%;
}

.row img {
   width: 100%;
   height: auto;
}

.row h2 {
   margin: 0;
   font-family: "Unbounded", helvetica, sans-serif;
   font-size: 36px;
}
.row h3 {
   margin: 0;
   font-size: 24px;
}

.row p {
   margin: 10px 0;
   font-size: 18px;
   line-height: 1.5;
}
<!-- @format -->

<html>
   <head>
      <link rel="preconnect" href="https://fonts.googleapis." />
      <link rel="preconnect" href="https://fonts.gstatic." crossorigin />
      <link href="https://fonts.googleapis./css2?family=Manrope&family=Unbounded&family=Lack&display=swap" rel="stylesheet" />
   </head>
   <body>
      <header>
         <h1>My App</h1>
      </header>
      <main>
         <div class="row-container">
            <div class="row row-1">
               <div class="row-inner">
                  <div class="col left">
                     <div class="col-inner">
                        <h2>Headline 1</h2>
                        <h3>Subhead 1</h3>
                        <p>Lorem ipsum dolor sit</p>
                     </div>
                  </div>
                  <div class="col right">
                     <img src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1" />
                  </div>
               </div>
            </div>
            <div class="row row-1">
               <div class="row-inner">
                  <div class="col left">
                     <div class="col-inner">
                        <h2>Headline 1</h2>
                        <h3>Subhead 1</h3>
                        <p>Lorem ipsum dolor sit</p>
                     </div>
                  </div>
                  <div class="col right">
                     <img src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1" />
                  </div>
               </div>
            </div>

            <div class="row row-1">
               <div class="row-inner">
                  <div class="col left">
                     <div class="col-inner">
                        <h2>Headline 1</h2>
                        <h3>Subhead 1</h3>
                        <p>Lorem ipsum dolor sit</p>
                     </div>
                  </div>
                  <div class="col right">
                     <img src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1" />
                  </div>
               </div>
            </div>
         </div>
      </main>
   </body>
</html>

You didn't really ask for a web ponent, I just got carried away. This is a fun challenge! Anyway, here's a version that is NOT a web ponent. I also realized that I missed all the css that you provided. So I added that as well. I'm not sure if tweaking the css a little is verbotten ... but I did it. I also added a single [section] tag, which wraps everything but the app header, to make it more bullet proof ... Please let me know if I need to use your code exactly as you posted it.

I had a lot of trouble finding settings that provide a dramatic enough effect on both large screens and phones without some ugly hacks, so I opted for a promise. You can optimize for either large or small by changing the rootMargin option for the IntersectionObserver. Smaller values make the fade effect more dramatic, but values that are too small cause the content to look washed out on smaller devices. As I'm posting this, rootMargin is set to "-25px". Apparantly you can use negative values, but as far as I can tell, you can only use "px" as the measurement. Other measurements don't seem to work.

Sooooo...

I used IntersectionObserver

const observer = new IntersectionObserver(fadeInOut, options);

One of the options IntersectionObserver takes is something called "threshold". I don't totally understand it, but MDN suggested that if you want to poll the viewport to determine element visibility, you should set the value of "threshold" as an array with many numbers ranging from 0 to 1. The example provided a function that generates 20 of them, so that's what I used.

function buildThresholds() {
  const steps = 20;
  const thresholds = [];

  for (let i = 0; i <= steps; i++) {
      let ratio = i/steps;
      thresholds.push(ratio);
   }

    thresholds.push(0);
    return thresholds;
  }

Then I grabbed the collection of elements to watch using querySelectorAll(".row-container").

Then I looped through the elements and added each one to the observer's watch list.

observer.observe(elem);

Then, in the callback you provide when you create a new instance of IntersectionObserver, I set each element's opacity to the the intersectionRatio, which is a property provided by the instance of the IntersectionObserverEntry which is passed to the callback.

function fadeInOut(items, observer) {
  items.forEach( (item) => {
      item.target.style.opacity = item.intersectionRatio;
  });
}

function buildThresholds() {
  const steps = 20;
  const thresholds = [];

  for (let i = 0; i <= steps; i++) {
      let ratio = i/steps;
      thresholds.push(ratio);
  }

  thresholds.push(0);
  return thresholds;
}

function fadeInOut(items, observer) {
  items.forEach( (item) => {
      item.target.style.opacity = item.intersectionRatio;
  });
}

function scrollFade() {
  const elems = document.querySelectorAll('.row-container');
  const options = {
    root: null,
    rootMargin: "-25px",
    threshold: buildThresholds()
  };

  const observer = new IntersectionObserver(fadeInOut, options);

  for (let elem of elems) {
    observer.observe(elem);
  }
}

document.addEventListener('DOMContentLoaded', scrollFade);
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0 0 0 0;
  font-family: sans-serif;
  font-family: 'Manrope', helvetiva, sans-serif;
  transition: background-color 1s;
  color:#fff;
  background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
}

header {
  position: fixed;
  top: 0;
  width: 100%;
  background: #fff;
  z-index: 999;
}

header h1 {
  font-family: 'Lack';
  font-size: 4em;
  text-align: center;
  padding: 10px;
  color: #100521;
}

section {
  margin-top: 6em;
}

h1 {
  font-family: 'Unbounded', helvetica, sans-serif;
}

h1 {
  margin: 0;
  font-size: 36px;
}

h2 {
  margin: 0;
  font-size: 24px;
}

p {
  margin: 10px 0;
  font-size: 18px;
  line-height: 1.5;
}

img {
  width: 100%;
  height: auto;
}

.row-container {
  background-color: rgb(16, 5, 33);
  min-height: 50vh;
}

.row-inner {
  align-content: center;
  align-items: center;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap-reverse;
  padding: 10px;
  gap: 10px;
}

.left-column {
  flex: 1 1 70vw;
}

.right-column {
  flex: 1 1 100px;
  text-align: center;
}
<header>
    <h1>My App</h1>
</header>

<section>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline One</h1>
                    <h2>Subhead One</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline Two</h1>
                    <h2>Subhead Two</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-2.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline Three</h1>
                    <h2>Subhead Three</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-3.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline Four</h1>
                    <h2>Subhead Four</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-4.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline Five</h1>
                    <h2>Subhead Five</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-5.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <h1>Headline Six</h1>
                    <h2>Subhead Six</h2>
                    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-6.jpg" alt="Image 1">
                </div>
            </div>
        </div>
    </div>
</div>
</section>

Will this work? I used IntersectionObserver and set the opacity of the sections based on the intersection ratio of each section with the viewport. I pretty much took the idea straight from MDN. MDN Intersection Observer API

class ScrollFade extends HTMLElement {

template;
shadow;
static observer = false;

constructor() {
    super();
    this.shadow = this.attachShadow({mode: 'open'});
    if (!ScrollFade.observer) {
        const buildThresholds = function() {
            const steps = 20;
            const thresholds = [];

            for (let i = 0; i <= steps; i++) {
                let ratio = i/steps;
                thresholds.push(ratio);
            }

            thresholds.push(0);
            return thresholds;
        }

        const options = {
            root: null,
            rootMargin: "0px",
            threshold: buildThresholds()
        };

        ScrollFade.observer = new IntersectionObserver(ScrollFade.fadeInOut, options);
    }
}

connectedCallback() {
    const tmpl = document.querySelector('#scroll-fade-template').cloneNode(true);
    const template = tmpl.content;
    ScrollFade.observer.observe(this.shadow.host);
    this.shadow.append(template);
}

disconnectedCallback() {
    ScrollFade.observer.unobserve(this.shadow.host);
}

static fadeInOut(items, observer) {
    items.forEach( (item) => {
        item.target.style.opacity = item.intersectionRatio;
    });
}
}

document.addEventListener('DOMContentLoaded', customElements.define('scroll-fade', ScrollFade));
body { margin: 0 }
img { width: 100%; }
<scroll-fade>
  <h1 slot="headline">Headline One</h1>
  <h2 slot="subhead">Subhead One</h2>
  <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-1.jpg" alt="Image 1">
  <div>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
  </div>
</scroll-fade>

<scroll-fade>
  <h1 slot="headline">Headline Two</h1>
  <h2 slot="subhead">Subhead Two</h2>
  <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-2.jpg" alt="Image 2">
  <div>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
  </div>
</scroll-fade>

<scroll-fade>
  <h1 slot="headline">Headline Three</h1>
  <h2 slot="subhead">Subhead Three</h2>
  <img slot="avatar" src="https://avatars.dicebear./api/adventurer/team-member-3.jpg" alt="Image 1"> 
  <div>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
  </div>
</scroll-fade>


<template id="scroll-fade-template">
  <style>
    .row-container {
      background-color: seagreen;
      height: 100vh;
      overflow: auto;
    }

    .row-inner {
      align-content: center;
      display: flex;
      flex-direction: row;
      flex-wrap: wrap-reverse;
      padding: 10px;
      gap: 10px;
    }

  .left-column {
    flex: 1 1 70vw;
  }

   .right-column {
    flex: 1 1 100px;
    text-align: center;
  }
</style>
<div class="row-container">
    <div class="row section">
        <div class="row-inner">
            <div class="column left-column">
                <div class="column-inner">
                    <slot name="headline">Need Headline</slot>
                    <slot name="subhead">Need Subhead</slot>
                    <slot>Need Content</slot>
                </div>
            </div>
            <div class="column right-column">
                <div class="column-inner">
                    <slot name="avatar">Need Avatar</slot>
                </div>
            </div>
        </div>
    </div>
</div>
</template>

本文标签: javascriptFade out one section and fade in the next section as you scroll on webpageStack Overflow