简体   繁体   English

如何应用于多个元素:仅当父元素在视口中时,JavaScript 才会对元素产生滚动效果

[英]How to apply to many elements: JavaScript scroll effect on element only when parent element is in viewport

I have a set of 100vh sections with child images and text.我有一组带有child图像和文本的100vh部分。 I'd like to apply a scroll transform to the text to create a parallax effect that moves the text over the image.我想对文本应用滚动transform以创建将文本移动到图像上的视差效果。 I've gotten it to work for a single element using vanilla javascript however, I can't quite get it right to apply the same effect to multiple sections with the same class .我已经使用 vanilla javascript 让它为单个元素工作,但是,我不能完全正确地将相同的效果应用于具有相同class多个部分。 As it stands now, the effect is applied to all the divs , regardless of if their parent section is in the viewport.就目前而言,该效果适用于所有divs ,无论其父section是否在视口中。

Can you help me edit the javascript such that the child div with the class scroll only transforms if its parent section is in the viewport?您能帮我编辑 javascript 以便class scroll的子 div 仅在其父section在视口中时才转换吗?

Thanks,谢谢,

 window.addEventListener('scroll', function(e) { var section = document.querySelectorAll('.section'); var length = section.length for (var i = 0; i < length; i++) { var bounding = section[i].getBoundingClientRect(); if ( bounding.top >=0 || bounding.bottom >=0 ) { const target = section[i].querySelector('.scroll'); var rate = window.pageYOffset * -0.5; target.style.transform = 'translate3d(0px, '+rate+'px, 0px)' } } });
 /*! Theme Name: Frozen Land Author: Will Caulfield Author URI: http://caulfield.co/ Description: Description Version: 1.0.0 License: GNU General Public License v2 or later License URI: LICENSE Text Domain: frozenland.co */ body { font-family: 'Lato', sans-serif; } p, i { color: #9c5f89; } h1 { color: white; text-transform: uppercase; letter-spacing: 2px; font-weight: 800; font-size: calc(70px + 0.3vw); margin: 10px 0px 25px 0px; } h3 { color: white; text-transform: uppercase; letter-spacing: 2px; font-weight: 800; font-size: calc(30px + 0.3vw); margin: 10px 0px 25px 0px; } main { background: -webkit-gradient(linear, left top, left bottom, from(#f9c4cc), to(#f27aaa)); background: linear-gradient(#f9c4cc, #f27aaa); } footer { background-color: #c9db79; height: 500px; } section.local { height: 300px; } section.hero { height: 100vh; } @-webkit-keyframes floatIce { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-15px); transform: translatey(-15px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatIce { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-15px); transform: translatey(-15px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @-webkit-keyframes floatText { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-10px); transform: translatey(-10px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatText { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-10px); transform: translatey(-10px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @-webkit-keyframes floatLand { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-5px); transform: translatey(-5px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatLand { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-5px); transform: translatey(-5px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } .hero img { margin-top: 100px; margin-bottom: 100px; width: 500px; } div.hero { position: relative; top: -50px; } .hero-ice { width: 200px; -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatIce 6s ease-in-out infinite; animation: floatIce 6s ease-in-out infinite; } .hero-text { -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatText 6s ease-in-out infinite; animation: floatText 6s ease-in-out infinite; } .hero-land { width: 250px; -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatText 6s ease-in-out infinite; animation: floatText 6s ease-in-out infinite; } .candy-float { width: 50px; } section.cone { height: 100vh; } .cone img { width: 300px; } nav img { width: 300px; margin-top: 15px; } section.froyo { height: 100vh; margin-top: 150px; } .froyo img { width: 400px; } .froyo div { position: absolute; } section.toppings { height: 100vh; margin-top: 150px; } .toppings img { width: 400px; } .toppings div { position: absolute; } /*# sourceMappingURL=style.css.map */
 <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> <!-- Custom CSS --> <link rel="stylesheet" href="css/style.css"> <!-- Googl Fonts --> <link href="https://fonts.googleapis.com/css?family=Lato:400,900&display=swap" rel="stylesheet"> <!-- Font Awesome --> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.12.0/css/all.css" integrity="sha384-REHJTs1r2ErKBuJB0fCK99gCYsVjwxHrSU0N7I1zl9vZbggVJXRMsv/sLlOAGb4M" crossorigin="anonymous"> <!-- Scripts --> <title>Hello, world!</title> </head> <body> <main> <section class="hero section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img class="scroll" data-rate="-0.5" src="img/logo.png" /> </section> <section class="cone section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/cone.png"/> <div class="scroll" data-rate="-0.5"> <h3>delicious</h3> <h1>ice cream</h1> </div> </section> <section class="froyo section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/froyo.png"/> <div class="scroll" data-rate="-0.5"> <h3>Frozen</h3> <h1>Yogurt</h1> </div> </section> <section class="toppings section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/toppings.png" /> <div class="scroll"> <h3>and lots of</h3> <h1>toppings!</h1> </div> </section> </main> <footer class="container-fluid"> <div class="row"> <div class="col"> </div> <div class="col text-center pt-5"> <p>10911 Lindbrook Drive<br/>Los Angeles, CA 90024</p> <p>(310) 824-8191</p> <p>&copy; 2020</p> </div> <div class="col"> </div> </div> </footer> <script> // window.addEventListener('scroll', function(e) { // var section = document.querySelector('.section'); // var bounding = section.getBoundingClientRect(); // if ( // bounding.top >=0 || bounding.bottom >=0 // ) { // const target = section.querySelector('.scroll'); // var rate = window.pageYOffset * -0.5; // target.style.transform = 'translate3d(0px, '+rate+'px, 0px)' // } else { // console.log("Not in Viewport!"); // } // }); // window.addEventListener('scroll', function(e) { // const target = document.querySelectorAll('.scroll'); // //var scrolled = window.pageYOffset; // //var rate = scrolled * -0.5; // //target.style.transform = 'translate3d(0px, '+rate+'px, 0px)' // var index = 0, length = target.length; // for (index; index < length; index++) { // var pos = window.pageYOffset * target[index].dataset.rate; // target[index].style.transform = 'translate3d(0px, '+pos+'px, 0px)'; // }; // }); </script> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> <!-- Other Scripts --> <script src="/js/main.js"></script> </body> </html>

Use the IntersectionObserver API to observer if elements are in view, or not.使用IntersectionObserver API来观察元素是否在视图中。 This api monitors elements that you specify and triggers a callback whenever an observed element enters or leaves the viewport.此 API 监视您指定的元素,并在观察到的元素进入或离开视口时触发回调。 This way you can create a list of elements that are in the viewport.通过这种方式,您可以创建视口中的元素列表。

To help with that create a Set .为了帮助创建一个Set Sets take any kind of type of value and stores it in a list, like a supercharged array, but only with unique values.集合接受任何类型的值并将其存储在一个列表中,就像一个增压数组,但只有唯一的值。 So no element will be in there twice.所以没有元素会在那里两次。

In the intersectionCallback check if an element leaves the viewport and add it to the set.intersectionCallback检查元素是否离开视口并将其添加到集合中。 If it leaves the screen, remove it from the set.如果它离开屏幕,请将其从集合中移除。

Then instead of looping over each .section loop over the set with the sections that are currently in the view.然后,而不是在每个.section循环,而是使用当前在视图中的部分循环。 The contents of this set changes constantly depending on where the scroll position is currently.该集合的内容根据当前滚动位置的位置不断变化。

In the scroll event callback calculate the position of the section relative to the top of the screen.scroll事件回调中计算该部分相对于屏幕顶部的位置。 You'll need to offset each transformation to the section to get the correct position of your parallax elements.您需要将每个变换偏移到该部分以获得视差元素的正确位置。

I hope this helps you out.我希望这能够帮到你。
Let me know if anything here is unclear or if I didn't help you in any way.如果这里有任何不清楚的地方或者我没有以任何方式帮助您,请告诉我。

Cheers!干杯!

Addendum附录

I felt that the performance was a little janky so I looked into the code for improvements.我觉得性能有点不稳定,所以我查看了代码以进行改进。 The first one was adding a passive listener to the scroll event listener which tells the listener not to wait for event.preventDefault() .第一个是向scroll事件侦听器添加一个passive listener ,它告诉侦听器不要等待event.preventDefault() Because it will not wait it will cycle faster to the next time the scroll event is fired.因为它不会等待它会更快地循环到下一次触发scroll事件。

Then there are the calculations.然后是计算。 Each time the scroll event is fired the offset is calculated.每次触发scroll事件时,都会计算偏移量。 But the offset does not change when scrolling, so it would make sense to calculate it first and use it later when needed.但是滚动时偏移量不会改变,因此首先计算它并在需要时使用它是有意义的。 The same goes for selecting the target element, that one also stays the same.选择target元素也是如此,那个元素也保持不变。

So I've added two Map objects that hold a connection between the section and their offset + targets.因此,我添加了两个Map对象,它们在该部分与其偏移 + 目标之间保持连接。 So now the scroll function will get the previously calculated offset and target based on the current section it is looping over.所以现在滚动函数将根据它正在循环的当前section获取先前计算的偏移量和目标。

All theses additions will significantly improve the scroll performance and make your parallax effects feel smooth.所有这些添加将显着提高滚动性能并使您的视差效果感觉平滑。 I know it is a lot of code and I don't expect that everything makes sense right away, but if you want I could try to explain what everything does and why/how you use it.我知道这是很多代码,我不希望一切都立即有意义,但是如果您愿意,我可以尝试解释一切的作用以及为什么/如何使用它。

 const inViewSections = new Set(); const offsetMap = new Map(); const targetMap = new Map(); function calculateOffsetsOfSections(sections, map) { const bodyTop = document.body.getBoundingClientRect().top; sections.forEach(function(section) { const sectionTop = section.getBoundingClientRect().top; const offset = sectionTop - bodyTop; map.set(section, offset); }); } function mapTargetsOfSections(sections, map) { sections.forEach(function(section) { const target = section.querySelector('.scroll'); if (target !== null) { map.set(section, target); } }); } function observeSections(sections, observer) { sections.forEach(function(section) { observer.observe(section); }); } function intersectionCallback(entries) { entries.forEach(function(entry) { if (entry.isIntersecting === true) { inViewSections.add(entry.target); } else { inViewSections.delete(entry.target); } }); } const observer = new IntersectionObserver(intersectionCallback, { root: null, rootMargin: '0px', threshold: [0] }); let sections = document.querySelectorAll('.section'); calculateOffsetsOfSections(sections, offsetMap); mapTargetsOfSections(sections, targetMap); observeSections(sections, observer); window.addEventListener('resize', function(e) { calculateOffsetsOfSections(sections, offsetMap); }); window.addEventListener('scroll', function(e) { inViewSections.forEach(function(section) { if (offsetMap.has(section) && targetMap.has(section)) { const target = targetMap.get(section); const offset = offsetMap.get(section); var rate = Math.round((window.pageYOffset - offset) * -0.5); target.style.transform = 'translate3d(0px, ' + rate + 'px, 0px)'; } }); }, {passive: true});
 /*! Theme Name: Frozen Land Author: Will Caulfield Author URI: http://caulfield.co/ Description: Description Version: 1.0.0 License: GNU General Public License v2 or later License URI: LICENSE Text Domain: frozenland.co */ body { font-family: 'Lato', sans-serif; } p, i { color: #9c5f89; } h1 { color: white; text-transform: uppercase; letter-spacing: 2px; font-weight: 800; font-size: calc(70px + 0.3vw); margin: 10px 0px 25px 0px; } h3 { color: white; text-transform: uppercase; letter-spacing: 2px; font-weight: 800; font-size: calc(30px + 0.3vw); margin: 10px 0px 25px 0px; } main { background: -webkit-gradient(linear, left top, left bottom, from(#f9c4cc), to(#f27aaa)); background: linear-gradient(#f9c4cc, #f27aaa); } footer { background-color: #c9db79; height: 500px; } section.local { height: 300px; } section.hero { height: 100vh; } @-webkit-keyframes floatIce { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-15px); transform: translatey(-15px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatIce { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-15px); transform: translatey(-15px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @-webkit-keyframes floatText { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-10px); transform: translatey(-10px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatText { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-10px); transform: translatey(-10px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @-webkit-keyframes floatLand { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-5px); transform: translatey(-5px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } @keyframes floatLand { 0% { -webkit-transform: translatey(0px); transform: translatey(0px); } 50% { -webkit-transform: translatey(-5px); transform: translatey(-5px); } 100% { -webkit-transform: translatey(0px); transform: translatey(0px); } } .hero img { margin-top: 100px; margin-bottom: 100px; width: 500px; } div.hero { position: relative; top: -50px; } .hero-ice { width: 200px; -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatIce 6s ease-in-out infinite; animation: floatIce 6s ease-in-out infinite; } .hero-text { -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatText 6s ease-in-out infinite; animation: floatText 6s ease-in-out infinite; } .hero-land { width: 250px; -webkit-transform: translatey(0px); transform: translatey(0px); -webkit-animation: floatText 6s ease-in-out infinite; animation: floatText 6s ease-in-out infinite; } .candy-float { width: 50px; } section.cone { height: 100vh; } .cone img { width: 300px; } nav img { width: 300px; margin-top: 15px; } section.froyo { height: 100vh; margin-top: 150px; } .froyo img { width: 400px; } .froyo div { position: absolute; } section.toppings { height: 100vh; margin-top: 150px; } .toppings img { width: 400px; } .toppings div { position: absolute; } /*# sourceMappingURL=style.css.map */
 <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> <!-- Custom CSS --> <link rel="stylesheet" href="css/style.css"> <!-- Googl Fonts --> <link href="https://fonts.googleapis.com/css?family=Lato:400,900&display=swap" rel="stylesheet"> <!-- Font Awesome --> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.12.0/css/all.css" integrity="sha384-REHJTs1r2ErKBuJB0fCK99gCYsVjwxHrSU0N7I1zl9vZbggVJXRMsv/sLlOAGb4M" crossorigin="anonymous"> <!-- Scripts --> <title>Hello, world!</title> </head> <body> <main> <section class="hero section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img class="scroll" data-rate="-0.5" src="img/logo.png" /> </section> <section class="cone section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/cone.png" /> <div class="scroll" data-rate="-0.5"> <h3>delicious</h3> <h1>ice cream</h1> </div> </section> <section class="froyo section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/froyo.png" /> <div class="scroll" data-rate="-0.5"> <h3>Frozen</h3> <h1>Yogurt</h1> </div> </section> <section class="toppings section text-center d-flex flex-column justify-content-center align-items-center position-relative"> <img src="img/toppings.png" /> <div class="scroll"> <h3>and lots of</h3> <h1>toppings!</h1> </div> </section> </main> <footer class="container-fluid"> <div class="row"> <div class="col"> </div> <div class="col text-center pt-5"> <p>10911 Lindbrook Drive<br/>Los Angeles, CA 90024</p> <p>(310) 824-8191</p> <p>&copy; 2020</p> </div> <div class="col"> </div> </div> </footer> <script> // window.addEventListener('scroll', function(e) { // var section = document.querySelector('.section'); // var bounding = section.getBoundingClientRect(); // if ( // bounding.top >=0 || bounding.bottom >=0 // ) { // const target = section.querySelector('.scroll'); // var rate = window.pageYOffset * -0.5; // target.style.transform = 'translate3d(0px, '+rate+'px, 0px)' // } else { // console.log("Not in Viewport!"); // } // }); // window.addEventListener('scroll', function(e) { // const target = document.querySelectorAll('.scroll'); // //var scrolled = window.pageYOffset; // //var rate = scrolled * -0.5; // //target.style.transform = 'translate3d(0px, '+rate+'px, 0px)' // var index = 0, length = target.length; // for (index; index < length; index++) { // var pos = window.pageYOffset * target[index].dataset.rate; // target[index].style.transform = 'translate3d(0px, '+pos+'px, 0px)'; // }; // }); </script> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> <!-- Other Scripts --> <script src="/js/main.js"></script> </body> </html>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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