項(xiàng)目背景:當(dāng)進(jìn)入頁面滾動的時(shí)候,頁面中的元素出現(xiàn)在視野的時(shí)候,展示一下動畫效果。
技術(shù)棧:vue
實(shí)現(xiàn)方法:主要是給需要動畫的元素添加animation+X/Y/L/R class樣式
解決什么問題:
- 網(wǎng)上的好多滾動事件沒有添加防抖函數(shù),這樣會導(dǎo)致在滾動過程中會有白屏現(xiàn)象。
- 另外就是沒有移除事件監(jiān)聽,在離開當(dāng)前頁面的時(shí)候,需要在銷毀組件之前移除滾動事件,否則跳轉(zhuǎn)路由之后,事件仍然會被調(diào)用,浪費(fèi)資源
- 為什么不直接
window.addEventListener('scroll', throttle(this.handleScroll, 0, 500))這樣寫?因?yàn)槲野l(fā)現(xiàn)這樣在remove的時(shí)候移除不了。所以監(jiān)聽處理寫個(gè)聲明式函數(shù)、或者用函數(shù)表達(dá)式賦值給變量比較好些,這樣移除事件監(jiān)聽方便些。
開始吧
/**
* 在 mounted() 的時(shí)候(el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用該鉤子。)
* 添加事件監(jiān)聽
*/
this.throttleLoad = throttle(this.handleScroll, 0, 500)
window.addEventListener('scroll', this.throttleLoad)
this.handleScroll() // 并首次調(diào)用動畫(可能有些元素在不同尺寸的屏幕下,有可能需要執(zhí)行動畫)
methods 中監(jiān)聽調(diào)用的方法
// 不同頁面調(diào)用不同的方法
handleScroll(e) {
const clientH = window.innerHeight
const boxListL = [...document.querySelectorAll('.animationL')]
boxListL.forEach((item, index) => {
const Rect = item.getBoundingClientRect()
if (Rect.top + 130 < clientH) {
handleAddClass(item, 'animation-activeL')
} else {
handleRemoveClass(item, 'animation-activeL')
}
})
const boxListR = [...document.querySelectorAll('.animationR')]
boxListR.forEach((item, index) => {
const Rect = item.getBoundingClientRect()
if (Rect.top + 130 < clientH) {
handleAddClass(item, 'animation-activeR')
} else {
handleRemoveClass(item, 'animation-activeR')
}
})
}
}
主動銷毀
beforeDestroy() {
window.removeEventListener('scroll', this.throttleLoad)
}
公共的方法抽離出來放到公共js中
// 因?yàn)楫?dāng)時(shí)考慮到兼容問題,在IE低版本中沒有classList.add / remove 這些方法
/**
* Add class
* @param {DOM} item
* @param {className} name
*/
export function handleAddClass(item, name) {
if (item.classList) {
item.classList.add(name)
} else {
const classes = item.className.split(/\s+/g)
if (classes.indexOf(name) < 0) {
classes.push(name)
}
item.className = classes.join(' ')
}
}
/**
* Remove class
* @param {DOM} item
* @param {className} name
*/
export function handleRemoveClass(item, name) {
if (item.classList) {
item.classList.remove(name)
} else {
const classes = item.className.split(/\s+/g)
const n = classes.indexOf('animation-activeL')
if (n > -1) {
classes.splice(n, 1)
}
item.className = classes.join(' ')
}
}
/**
* 防抖
* @param fn 執(zhí)行的函數(shù)
* @param delay 延時(shí)時(shí)間
* @param atleast 達(dá)到強(qiáng)制執(zhí)行的條件
*/
export function throttle(fn, delay, atleast) {
let timer = null
let previous = null
return function () {
const now = +new Date()
if (!previous) previous = now
if (atleast && now - previous > atleast) {
fn()
previous = now
clearTimeout(timer)
} else {
clearTimeout(timer)
timer = setTimeout(function () {
fn()
previous = null
}, delay)
}
}
}