图片懒加载及Vue自定义图片懒加载指令
学习链接:
前端必会的图片懒加载
vue自定义指令实现图片懒加载
监听滚动的方式
- img的src先都用一张默认的图片,data-src属性为真实图片地址
- 当图片出现在可视区范围内时,把src属性换成data-src属性,就完成了
- 缺点:一当发生滚动事件时,就发生了大量的循环和判断操作判断图片是否可视区里
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {padding: 0;margin: 0;}img {width: 200px;height: 200px;object-fit: cover;}</style><script>window.onload = function () {window.addEventListener('scroll', loadImg)function loadImg() {console.log('-----'); // 每一次滚动都会执行,可能滚动条只滚动了一小段距离,但这个函数执行了很多次let imgs = document.querySelectorAll('img')// console.dir(imgs[0].getAttribute('data-src'));// console.dir(imgs[0].getAttribute('src'));for (let i = 0; i < imgs.length; i++) {if(imgs[i].getBoundingClientRect().top < window.innerHeight - 200) { // 也可以这样判断:imgs[i].offsetTop <= window.scrollY + window.innerHeight - 200imgs[i].setAttribute('src', imgs[i].getAttribute('data-src'))}}}loadImg() // 刚开始的时候,也要加载一次看看有没有图片就正好满足显示条件}</script>
</head><body><img src="./img/img0.jpg" data-src="./img/post1.jpg"><img src="./img/img0.jpg" data-src="./img/post2.jpg"><img src="./img/img0.jpg" data-src="./img/post3.jpg"><img src="./img/img0.jpg" data-src="./img/post4.jpg"><img src="./img/img0.jpg" data-src="./img/post5.jpg"><img src="./img/img0.jpg" data-src="./img/post6.jpg">
</body></html>
IntersectionObserver方式
- IntersectionObserver是浏览器原生提供的构造函数
- 使用IntersectionObserver的observe方法,来观察某个元素
- 使用IntersectionObserver的unobserve方法,来取消观察某个元素
- 页面一加载的时候,就会返回所有被观察的元素它们的情况,注意是所有。此后,当某个元素出现在可视区时(从不在可视区到在可视区那个时刻),或者某个离开时可视区时(从在可视区到不在可视区的那个时刻),都会触发回调函数,回调函数传过来的参数就会告诉使用者哪些元素正在离开可视区,哪些元素正在进入可视区。
- 除了回调函数这个参数外,还可以指定一个配置对象,配置对象中可以配置threshold,它表示目标元素与根元素的交叉比例,可以是单一的 number 也可以是 number 数组,比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。详细解释:IntersectionObserver API详解
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {padding: 0;margin: 0;}img {width: 200px;height: 200px;object-fit: cover;}</style><script>window.onload = function () {function callback(entries) {console.log(entries); /* [IntersectionObserverEntry,..] */for(let i of entries) { if(i.isIntersecting) { /* 当元素出现在可视窗口时 */let img = i.targetimg.setAttribute('src',img.getAttribute('data-src'))observer.unobserve(img) /* 已经看见的图片, 取消观察 */}}}/* IntersectionObserver是浏览器原生提供的构造函数 */const observer = new IntersectionObserver(callback);let imgs = document.querySelectorAll('img')/* 可以为每一张图片绑定一个观察器 */for (let i of imgs) {observer.observe(i);}}</script>
</head><body><img src="./img/img0.jpg" data-src="./img/post1.jpg"><img src="./img/img0.jpg" data-src="./img/post2.jpg"><img src="./img/img0.jpg" data-src="./img/post3.jpg"><img src="./img/img0.jpg" data-src="./img/post4.jpg"><img src="./img/img0.jpg" data-src="./img/post5.jpg"><img src="./img/img0.jpg" data-src="./img/post6.jpg">
</body></html>
自定义图片懒加载vue指令1
<style>img {width: 200px;height: 200px;object-fit: cover;display: block;}
</style><template><div>LazyImg<br/><img src="@/assets/post1.jpg" v-lazy alt=""><img src="@/assets/post2.jpg" v-lazy alt=""><img src="@/assets/post3.jpg" v-lazy alt=""><img src="@/assets/post4.jpg" v-lazy alt=""><img src="@/assets/post5.jpg" v-lazy alt=""><img src="@/assets/post6.jpg" v-lazy alt=""></div>
</template><script>import img0 from '@/assets/img0.jpg'
console.log(img0);
console.log(require('@/assets/img0.jpg'));export default {name: 'lazyImg',directives: {lazy: {bind(el,binding) {el.setAttribute("data-src",el.getAttribute("src"))el.setAttribute("src", img0)let observer = new IntersectionObserver(entries=>{for(let i of entries) {if(i.isIntersecting) {el.setAttribute('src',el.getAttribute("data-src") )observer.unobserve(el)}}})observer.observe(el)}}},components: {}
}
</script>
自定义图片懒加载vue指令2
lazyLoadImage.js
const lazyLoadImage = defaultImage => {let windowHeight = document.documentElement.clientHeight || document.body.clientHeight//可视区域高function throttle(fn, delay) {//节流let timeout = nullreturn (...args) => {if (timeout) returntimeout = setTimeout(() => {fn.apply(this, args)timeout = null}, delay);}}function loadImage(el) {//加载图片return () => {let top = el.getBoundingClientRect().toplet bottom = el.getBoundingClientRect().bottomif (top - windowHeight < 0 && bottom > 0&&el.hasAttribute('data-src')) {//图片出现在可视区域内开始加载图片el.src = el.getAttribute('data-src')el.removeAttribute('data-src')}}}return {bind(el, binding) {console.log(defaultImage);let realSrc = el.srcel.src = defaultImageel.setAttribute('data-src', realSrc) },inserted(el) {loadImage(el)()//显示初始(首屏)图片window.addEventListener('scroll', throttle(loadImage(el), 500))//每滚动500毫秒加载一次图片}}
}const install = {install(vue, defaultImage) {//v-lazyvue.directive('lazy1', lazyLoadImage(defaultImage))}
}export default install
main.js中注册指令
import Vue from 'vue'
import App from './App.vue'
import router from './router'import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import '@/assets/css/base.css'
import '@/assets/iconfont/iconfont.css'
import 'animate.css'
Vue.config.productionTip = false
Vue.use(ElementUI);import lazyLoadImage from './utils/lazyLoadImage'
const defaultImage=require('@/assets/img0.jpg')//默认占位图片
Vue.use(lazyLoadImage,defaultImage)new Vue({router,render: h => h(App)
}).$mount('#app')
组件中使用
<style>img {width: 200px;height: 200px;object-fit: cover;display: block;}
</style><template><div>LazyImg<br/><img src="@/assets/post1.jpg" v-lazy1 alt=""><img src="@/assets/post2.jpg" v-lazy1 alt=""><img src="@/assets/post3.jpg" v-lazy1 alt=""><img src="@/assets/post4.jpg" v-lazy1 alt=""><img src="@/assets/post5.jpg" v-lazy1 alt=""><img src="@/assets/post6.jpg" v-lazy1 alt=""></div>
</template><script>export default {name: 'lazyImg',components: {}
}
</script>