Используйте IntersectionObserver API
для наблюдателя, если элементы видны или нет. Этот API-интерфейс отслеживает указанные вами элементы и вызывает обратный вызов всякий раз, когда элемент наблюдаемый входит или выходит из области просмотра. Таким образом, вы можете создать список элементов, которые находятся в области просмотра.
Чтобы помочь с этим, создайте Set
. Наборы принимают любой тип значения и сохраняют его в списке, например массив с наддувом, но только с уникальными значениями. Поэтому ни один элемент не будет там дважды.
В intersectionCallback
проверьте, покидает ли элемент область просмотра, и добавьте его в набор. Если он покидает экран, удалите его из набора.
Тогда вместо того, чтобы зацикливаться на каждом .section
l oop над набором с разделами, которые в данный момент находятся в представлении. Содержимое этого набора постоянно изменяется в зависимости от того, где в данный момент находится позиция прокрутки.
В обратном вызове события scroll
вычисляется позиция раздела относительно верхней части экрана. Вам нужно будет смещать каждое преобразование в секцию, чтобы получить правильное положение ваших элементов параллакса.
Надеюсь, это поможет вам.
Дайте мне знать, если что-нибудь здесь неясно или я не помог вам в этом.
Приветствия!
Приложение
Я чувствовал, что представление было немного дерганным, поэтому я посмотрел код улучшения. Первым было добавление passive listener
в прослушиватель событий scroll
, который говорит слушателю не ждать event.preventDefault()
. Поскольку он не будет ждать, он будет циклически ускоряться до следующего запуска события scroll
.
Затем выполняются вычисления. Каждый раз, когда происходит событие scroll
, вычисляется смещение. Но смещение не изменяется при прокрутке, поэтому имеет смысл сначала рассчитать его, а затем использовать при необходимости. То же самое касается выбора элемента target
, который также остается неизменным.
Итак, я добавил два Map
объекта, которые содержат связь между сечением и их смещением. + цели. Таким образом, теперь функция прокрутки получит ранее рассчитанное смещение и целевое значение на основе текущего section
, над которым она зацикливается.
Все эти добавления значительно улучшат производительность прокрутки и сделают ваши эффекты параллакса более плавными. Я знаю, что это много кода, и я не ожидаю, что все сразу имеет смысл, но если вы хотите, я мог бы попытаться объяснить, что все делает и почему / как вы его используете.
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>© 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>