在网站上做一点动画可以增加一些亮点,给用户留下深刻印象,并引起他们的注意。你可以让它们运行,不管它们在页面上的什么位置,当页面加载时立即运行。但如果你的网站相当长,所以用户花了一些时间向下滚动到那个元素呢?他们可能会错过它。
你可以让它们一直运行,但也许动画的设计最好是让你肯定能看到它的开始。诀窍是在用户向下滚动到那个元素时开始动画--滚动触发的动画,如果你愿意的话。
为了解决这个问题,我们使用滚动触发器。当用户向下滚动到任何特定元素时,我们可以使用该事件来做一些事情。它可以是任何东西,甚至是一个动画的开始。它甚至可以是滚动触发的图片的懒惰加载或懒惰加载整个评论部分。这样一来,我们就不会强迫用户在初始页面加载时下载不在视口中的元素。许多用户可能根本就不会向下滚动,所以我们真的为他们(和我们)节省了带宽和加载时间。
滚动触发器是非常有用的。有很多库可以用来实现它们,比如Greensock的流行的ScrollTrigger插件。但你不必使用第三方库,特别是对于相当简单的想法。事实上,你可以只用一小撮虚无缥缈的JavaScript来自己实现它。这就是我们在这篇文章中要做的。
下面是我们如何制作我们的滚动触发事件的方法
- 创建一个叫做
scrollTrigger的函数,我们可以应用于某些元素 - 当一个元素进入视口时,在该元素上应用一个
.active类 - 用CSS对该.
active类进行动画处理
有的时候,仅仅添加一个.active 类是不够的。例如,我们可能想执行一个自定义函数。这意味着我们应该能够传递一个自定义函数,在元素可见时执行。像这样。
scrollTrigger('.loader', {
cb: function(el) {
el.innerText = 'Loading ...'
loadContent()
}
})
我们还将尝试处理旧的不支持的浏览器的滚动触发器。
但首先,IntersectionObserver API
我们要使用的主要JavaScript功能是交叉观察者。这个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)
}
})
这个小旅程就到此结束了!希望你喜欢并学到了一些东西。我希望你喜欢它,并在这个过程中学到一些新东西。