当从顶部或底部滚动到视图中时,使用 Intersection Observer 向元素添加不同的 类
Use Intersection Observer to add different classes to elements when scrolled into view from top or bottom
我有一段 jQuery 代码,当元素滚动到视口中时向元素添加 css class 并在滚动时删除 class在视口之外。
到目前为止,代码是这样工作的:
- 当元素滚动到视口中时,将添加 class "inview"。
- 当元素滚出视口时,class "inview" 将被删除。
到目前为止一切顺利。但我想要实现的是:
滚动到视图中:
- 当元素从页面底部滚动到视口中时,添加 class "inview-bottom"。
- 当元素从页面顶部滚动到视口中时,将添加 class "inview-top"。
滚出视图:
- 当元素从页面底部滚出视口时,添加 class "outview-bottom"。
- 当元素从页面顶部滚出视口时,添加 class "outview-top"。
清理中:
- 当元素从页面顶部或底部滚动到视口中时,所有 "outview-*" class 元素都应被删除。
- 当一个元素从页面的顶部或底部滚出视口时,所有 "inview-*" class 元素都应该被删除。
有人在评论中建议使用 Intersection Observer API,在阅读更多相关信息后,我相信它提供了满足要求的最佳方法。
这是我的代码(以整页打开 - 预览效果不佳)。您还可以找到 the same code on jsFiddle.
function inView(opt) {
if (opt.selector === undefined) {
console.log('Valid selector required for inView');
return false;
}
var elems = [].slice.call(document.querySelectorAll(opt.selector)),
once = opt.once === undefined ? true : opt.once,
offsetTop = opt.offsetTop === undefined ? 0 : opt.offsetTop,
offsetBot = opt.offsetBot === undefined ? 0 : opt.offsetBot,
count = elems.length,
winHeight = 0,
ticking = false;
function update() {
var i = count;
while (i--) {
var elem = elems[i],
rect = elem.getBoundingClientRect();
if (rect.bottom >= offsetTop && rect.top <= winHeight - offsetBot) {
elem.classList.add('inview');
if (once) {
count--;
elems.splice(i, 1);
}
} else {
elem.classList.remove('inview');
}
}
ticking = false;
}
function onResize() {
winHeight = window.innerHeight;
requestTick();
}
function onScroll() {
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(update);
ticking = true;
}
}
window.addEventListener('resize', onResize, false);
document.addEventListener('scroll', onScroll, false);
document.addEventListener('touchmove', onScroll, false);
onResize();
}
inView({
selector: '.viewme', // an .inview class will get toggled on these elements
once: false, // set this to false to have the .inview class be toggled on AND off
offsetTop: 180, // top threshold to be considered "in view"
offsetBot: 100 // bottom threshold to be considered "in view"
});
.box {
width: 100%;
height: 50vh;
margin-bottom: 10px;
background: blue;
opacity: 0;
transition: opacity .2s ease;
}
.inview {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
您提供的 fiddle 只需稍作改动即可正常工作。您需要将观察器应用于所有元素才能正常工作。
看这个例子:
const config = {
root: null,
rootMargin: '0px',
threshold: [0.1, 0.5, 0.7, 1]
};
let previousY = 0;
let previousRatio = 0;
let observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
const currentY = entry.boundingClientRect.y
const currentRatio = entry.intersectionRatio
const isIntersecting = entry.isIntersecting
const element = entry.target;
element.classList.remove("outview-top", "inview-top", "inview-bottom", "outview-bottom");
// Scrolling up
if (currentY < previousY) {
const className = (currentRatio >= previousRatio) ? "inview-top" : "outview-top";
element.classList.add(className);
// Scrolling down
} else if (currentY > previousY) {
const className = (currentRatio <= previousRatio) ? "outview-bottom" : "inview-bottom";
element.classList.add(className);
}
previousY = currentY
previousRatio = currentRatio
})
}, config);
const images = document.querySelectorAll('.box');
images.forEach(image => {
observer.observe(image);
});
.box {
width: 100%;
height: 50vh;
margin-bottom: 10px;
background: lightblue;
transition: opacity .2s ease;
display: flex;
justify-content: center;
align-items: center;
font-size: 3em;
}
[class*='inview'] {
opacity: 1;
}
[class*='outview'] {
opacity: 0.6;
}
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
我有一段 jQuery 代码,当元素滚动到视口中时向元素添加 css class 并在滚动时删除 class在视口之外。
到目前为止,代码是这样工作的:
- 当元素滚动到视口中时,将添加 class "inview"。
- 当元素滚出视口时,class "inview" 将被删除。
到目前为止一切顺利。但我想要实现的是:
滚动到视图中:
- 当元素从页面底部滚动到视口中时,添加 class "inview-bottom"。
- 当元素从页面顶部滚动到视口中时,将添加 class "inview-top"。
滚出视图:
- 当元素从页面底部滚出视口时,添加 class "outview-bottom"。
- 当元素从页面顶部滚出视口时,添加 class "outview-top"。
清理中:
- 当元素从页面顶部或底部滚动到视口中时,所有 "outview-*" class 元素都应被删除。
- 当一个元素从页面的顶部或底部滚出视口时,所有 "inview-*" class 元素都应该被删除。
有人在评论中建议使用 Intersection Observer API,在阅读更多相关信息后,我相信它提供了满足要求的最佳方法。
这是我的代码(以整页打开 - 预览效果不佳)。您还可以找到 the same code on jsFiddle.
function inView(opt) {
if (opt.selector === undefined) {
console.log('Valid selector required for inView');
return false;
}
var elems = [].slice.call(document.querySelectorAll(opt.selector)),
once = opt.once === undefined ? true : opt.once,
offsetTop = opt.offsetTop === undefined ? 0 : opt.offsetTop,
offsetBot = opt.offsetBot === undefined ? 0 : opt.offsetBot,
count = elems.length,
winHeight = 0,
ticking = false;
function update() {
var i = count;
while (i--) {
var elem = elems[i],
rect = elem.getBoundingClientRect();
if (rect.bottom >= offsetTop && rect.top <= winHeight - offsetBot) {
elem.classList.add('inview');
if (once) {
count--;
elems.splice(i, 1);
}
} else {
elem.classList.remove('inview');
}
}
ticking = false;
}
function onResize() {
winHeight = window.innerHeight;
requestTick();
}
function onScroll() {
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(update);
ticking = true;
}
}
window.addEventListener('resize', onResize, false);
document.addEventListener('scroll', onScroll, false);
document.addEventListener('touchmove', onScroll, false);
onResize();
}
inView({
selector: '.viewme', // an .inview class will get toggled on these elements
once: false, // set this to false to have the .inview class be toggled on AND off
offsetTop: 180, // top threshold to be considered "in view"
offsetBot: 100 // bottom threshold to be considered "in view"
});
.box {
width: 100%;
height: 50vh;
margin-bottom: 10px;
background: blue;
opacity: 0;
transition: opacity .2s ease;
}
.inview {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
您提供的 fiddle 只需稍作改动即可正常工作。您需要将观察器应用于所有元素才能正常工作。
看这个例子:
const config = {
root: null,
rootMargin: '0px',
threshold: [0.1, 0.5, 0.7, 1]
};
let previousY = 0;
let previousRatio = 0;
let observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
const currentY = entry.boundingClientRect.y
const currentRatio = entry.intersectionRatio
const isIntersecting = entry.isIntersecting
const element = entry.target;
element.classList.remove("outview-top", "inview-top", "inview-bottom", "outview-bottom");
// Scrolling up
if (currentY < previousY) {
const className = (currentRatio >= previousRatio) ? "inview-top" : "outview-top";
element.classList.add(className);
// Scrolling down
} else if (currentY > previousY) {
const className = (currentRatio <= previousRatio) ? "outview-bottom" : "inview-bottom";
element.classList.add(className);
}
previousY = currentY
previousRatio = currentRatio
})
}, config);
const images = document.querySelectorAll('.box');
images.forEach(image => {
observer.observe(image);
});
.box {
width: 100%;
height: 50vh;
margin-bottom: 10px;
background: lightblue;
transition: opacity .2s ease;
display: flex;
justify-content: center;
align-items: center;
font-size: 3em;
}
[class*='inview'] {
opacity: 1;
}
[class*='outview'] {
opacity: 0.6;
}
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>