图片懒加载及Vue自定义图片懒加载指令


学习链接:
前端必会的图片懒加载
vue自定义指令实现图片懒加载

监听滚动的方式

  • img的src先都用一张默认的图片,data-src属性为真实图片地址
  • 当图片出现在可视区范围内时,把src属性换成data-src属性,就完成了
  • 缺点:一当发生滚动事件时,就发生了大量的循环和判断操作判断图片是否可视区里
  • 补充一点:当把img的src从默认的图片换成真实的图片url后,在真实图片未加载完成前,还会一直是默认图片

在这里插入图片描述

<!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 - 200
                    
                        imgs[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.target
                        img.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 = null
        return (...args) => {
            if (timeout) return
            timeout = setTimeout(() => {
                fn.apply(this, args)
                timeout = null
            }, delay);
        }

    }
    function loadImage(el) {//加载图片
        return () => {
            let top = el.getBoundingClientRect().top
            let bottom = el.getBoundingClientRect().bottom
            if (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.src
            el.src = defaultImage
            el.setAttribute('data-src', realSrc) 
        },
        inserted(el) {
            loadImage(el)()//显示初始(首屏)图片
            window.addEventListener('scroll', throttle(loadImage(el), 500))//每滚动500毫秒加载一次图片
        }

    }
}


const install = {
    install(vue, defaultImage) {
        //v-lazy
        vue.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>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值