网站上的一点动画可以增添一些趣味,给用户留下深刻印象并吸引他们的注意力。您可以让它们在页面加载时立即运行,无论它们在页面上的哪个位置。但是,如果您的网站很长,用户需要花费一些时间才能向下滚动到该元素怎么办?他们可能会错过它。
您可以让它们一直运行,但也许动画的设计最佳方式是确保您能看到它的开始部分。诀窍是在用户向下滚动到该元素时开始动画——也就是滚动触发动画。
为了解决这个问题,我们使用 **滚动触发器**。当用户向下滚动到任何特定元素时,我们可以使用该事件来执行某些操作。它可以是任何操作,甚至可以是动画的开始。它甚至可以是图像的滚动触发延迟加载或整个评论部分的延迟加载。这样,我们不会强迫用户在初始页面加载时下载视口中不存在的元素。许多用户可能根本不会向下滚动,因此我们确实为他们(以及我们自己)节省了带宽和加载时间。
滚动触发器非常有用。有很多库可用于实现它们,例如 Greensock 的流行 ScrollTrigger 插件。但您不必使用第三方库,尤其是在处理相当简单的想法时。事实上,您只需使用少量原生 JavaScript 就可以自己实现它。这就是我们将在本文中要做的。
以下是我们如何创建滚动触发事件
- 创建一个名为
scrollTrigger
的函数,我们可以将其应用于某些元素 - 当元素进入视口时,在元素上应用
.active
类 - 使用 CSS 对
.active
类进行动画
有时添加 .active
类是不够的。例如,我们可能希望改为执行自定义函数。这意味着我们应该能够传递一个在元素可见时执行的自定义函数。像这样
scrollTrigger('.loader', {
cb: function(el) {
el.innerText = 'Loading ...'
loadContent()
}
})
我们还将尝试处理旧版不支持浏览器的滚动触发器。
IntersectionObserver
API
但首先,我们将要使用的主要 JavaScript 功能是 Intersection Observer。此 API 提供了一种以 *异步* 方式观察目标元素交集变化的方法——并且它比监视 scroll
事件的方式更有效率。我们将使用 IntersectionObserver
来监视滚动何时到达页面上某些元素可见的点。
让我们开始构建滚动触发器
我们想创建一个名为 scrollTrigger
的函数,并且此函数应将选择器作为其参数。
function scrollTrigger(selector) {
// Multiple element can have same class/selector,
// so we are using querySelectorAll
let els = document.querySelectorAll(selector)
// The above `querySelectorAll` returns a nodeList,
// so we are converting it to an array
els = Array.from(els)
// Now we are iterating over the elements array
els.forEach(el => {
// `addObserver function` will attach the IntersectionObserver to the element
// We will create this function next
addObserver(el)
})
}
// Example usage
scrollTrigger('.scroll-reveal')
现在让我们创建 addObserver
函数,我们希望使用 IntersectionObserver
附加到元素上
function scrollTrigger(selector){
let els = document.querySelectorAll(selector)
els = Array.from(els)
els.forEach(el => {
addObserver(el)
})
}
function addObserver(el){
// We are creating a new IntersectionObserver instance
let observer = new IntersectionObserver((entries, observer) => { // This takes a callback function that receives two arguments: the elements list and the observer instance.
entries.forEach(entry => {
// `entry.isIntersecting` will be true if the element is visible
if(entry.isIntersecting) {
entry.target.classList.add('active')
// We are removing the observer from the element after adding the active class
observer.unobserve(entry.target)
}
})
})
// Adding the observer to the element
observer.observe(el)
}
// Example usage
scrollTrigger('.scroll-reveal')
如果我们执行此操作并滚动到具有 .scroll-reveal
类的元素,则会将 .active
类添加到该元素。但请注意,active
类会在元素的任何一小部分可见时立即添加。
但这可能有点过头了。相反,我们可能希望在元素的 *更大* 部分可见时添加 .active
类。幸运的是,IntersectionObserver
接受一些用于此目的的选项 作为其第二个参数。让我们将其应用于我们的 scrollTrigger
函数
// Receiving options as an object
// If the user doesn't pass any options, the default will be `{}`
function scrollTrigger(selector, options = {}) {
let els = document.querySelectorAll(selector)
els = Array.from(els)
els.forEach(el => {
// Passing the options object to the addObserver function
addObserver(el, options)
})
}
// Receiving options passed from the scrollTrigger function
function addObserver(el, options) {
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
entry.target.classList.add('active')
observer.unobserve(entry.target)
}
})
}, options) // Passing the options object to the observer
observer.observe(el)
}
// Example usage 1:
// scrollTrigger('.scroll-reveal')
// Example usage 2:
scrollTrigger('.scroll-reveal', {
rootMargin: '-200px'
})

就这样,我们的前两个议程项目完成了!
让我们继续第三个项目——在滚动到目标元素时添加执行回调函数的功能。具体来说,让我们在选项对象中将回调函数作为 cb
传递
function scrollTrigger(selector, options = {}) {
let els = document.querySelectorAll(selector)
els = Array.from(els)
els.forEach(el => {
addObserver(el, options)
})
}
function addObserver(el, options){
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.isIntersecting){
if(options.cb) {
// If we've passed a callback function, we'll call it
options.cb(el)
} else{
// If we haven't, we'll just add the active class
entry.target.classList.add('active')
}
observer.unobserve(entry.target)
}
})
}, options)
observer.observe(el)
}
// Example usage:
scrollTrigger('.loader', {
rootMargin: '-200px',
cb: function(el){
el.innerText = 'Loading...'
// Done loading
setTimeout(() => {
el.innerText = 'Task Complete!'
}, 1000)
}
})

太棒了!还有一件事我们需要处理:旧版浏览器支持。某些浏览器可能缺乏对 IntersectionObserver
的支持,因此让我们在 addObserver
函数中处理这种情况
function scrollTrigger(selector, options = {}) {
let els = document.querySelectorAll(selector)
els = Array.from(els)
els.forEach(el => {
addObserver(el, options)
})
}
function addObserver(el, options) {
// Check if `IntersectionObserver` is supported
if(!('IntersectionObserver' in window)) {
// Simple fallback
// The animation/callback will be called immediately so
// the scroll animation doesn't happen on unsupported browsers
if(options.cb){
options.cb(el)
} else{
entry.target.classList.add('active')
}
// We don't need to execute the rest of the code
return
}
let observer = new IntersectionObserver((entries, observer) =>; {
entries.forEach(entry => {
if(entry.isIntersecting) {
if(options.cb) {
options.cb(el)
} else{
entry.target.classList.add('active')
}
observer.unobserve(entry.target)
}
})
}, options)
observer.observe(el)
}
// Example usages:
scrollTrigger('.intro-text')
scrollTrigger('.scroll-reveal', {
rootMargin: '-200px',
})
scrollTrigger('.loader', {
rootMargin: '-200px',
cb: function(el){
el.innerText = 'Loading...'
setTimeout(() => {
el.innerText = 'Task Complete!'
}, 1000)
}
})
这是那个实时演示
这次小小的旅程就到这里了!希望您喜欢它并在过程中学到了一些新东西。
通过本教程,重建 skrollr(我以前最喜欢的 jQuery 插件之一)应该很容易。
我认为在选项和回调相同的情况下,执行 observer.observe 而不是为每个元素创建新的 Observer 对浏览器来说更轻量级。
非常感谢,因为您的文章,我可以为登陆页面构建我自己的滚动和显示效果。