代碼以下node
function isPassive() {
let supportsPassiveOption = false
try {
addEventListener("test", null, Object.defineProperty({}, 'passive', {
get: function () {
supportsPassiveOption = true
}
}));
} catch(e) {}
return supportsPassiveOption //{passive: true} 就不會調用 preventDefault 來阻止默認滑動行爲
}
const lazyLoad = {
ticking: false,
timer: null,
complete: false,
position: { // 只要其中一個位置符合條件,都會觸發加載機制
top: 0, // 元素距離頂部
right: 0, // 元素距離右邊
bottom: 0, // 元素距離下面
left: 0 // 元素距離左邊
},
list: [],
init (el, bingind, vnode) {
this.list.push(el)
this.scrollLoad()
},
/**
* 滾動加載圖片顯示替換默認圖片
*/
scrollLoad () {
if (!this.list.length && this.complete) {
this.removeLazyLoad()
} else {
this.ticking = false
this.list.forEach((el) => {
if (el.dataset.LazyLoadImgState != 'success' && this.getClientRect(el, this.position)) {
this.loadImg(el)
}
})
}
},
scrollImg () {
if(!this.ticking) {
this.ticking = true
this.timer = window.requestAnimationFrame(this.scrollLoad.bind(this))
}
},
/**
* @param {Object} el
* @param {Object} options
* @returns {Boolean}
*/
getClientRect (el,options) {
const bcr = el.getBoundingClientRect() //取得元素在可視區的位置
const mw = el.offsetWidth //元素自身寬度
const mh = el.offsetHeight //元素自身的高度
const w = window.innerWidth //視窗的寬度
const h = window.innerHeight //視窗的高度
const boolX = (!((bcr.right - options.left) <= 0 && ((bcr.left + mw) - options.left) <= 0) && !((bcr.left + options.right) >= w && (bcr.right + options.right) >= (mw + w))) //上下符合條件
const boolY = (!((bcr.bottom - options.top) <= 0 && ((bcr.top + mh) - options.top) <= 0) && !((bcr.top + options.bottom) >= h && (bcr.bottom + options.bottom) >= (mh + h))) //上下符合條件
return el.width != 0 && el.height != 0 && boolX && boolY
},
/**
* @param {Object} el
* @param {Number} index
*/
loadImg (el) {
const imgUrl = el.dataset.src
el.dataset.LazyLoadImgState = 'start'
if (imgUrl) {
const img = new Image()
img.src = imgUrl
img.addEventListener('load', () => {
setTimeout(() => {
el.style.backgroundImage = 'url('+imgUrl+')'
el.style.backgroundSize = '100% auto'
delete el.dataset.src
el.dataset.LazyLoadImgState = 'success'
el.classList.add('ui-successImg')
this.list.forEach((item, index) => {
if (item == el) {
this.list.splice(index, 1)
}
})
if (!this.list.length) {
this.complete = true
}
}, 310)
}, false)
img.addEventListener('error', () => {
delete el.dataset.src
el.dataset.LazyLoadImgState = 'error'
}, false)
} else {
delete el.dataset.src
}
},
start () {
window.addEventListener('scroll', this.scrollImg.bind(this), isPassive() ? {passive: true, capture: true} : true)
},
removeLazyLoad () {
cancelAnimationFrame(this.timer)
window.removeEventListener('scroll', this.scrollImg.bind(this), isPassive() ? {passive: true, capture: true} : true)
}
}
lazyLoad.start()
Vue.directive('lazy', {
inserted (el, binding, vnode) {
lazyLoad.init(el, binding, vnode)
}
})複製代碼
使用以下 bash
v-lazy :data-src="item"複製代碼