vue实现图片懒加载组件

1,660 阅读1分钟

懒加图片载

为避免页面刚加载时一次加载太多的图片,占用太多的网络资源,可使用图片懒加载技术,仅当图片出现在可视区域时才请求图片。

原理

监听页面滚动事件,做防抖处理,计算图片是否在可视区域,如果在则设置图片src属性,并监听图片加载完成事件,图片加载成功后移除滚动事件监听即可。

  • 使用
<!-- 如果滚动的容器是window -->
<LazyLoadImg src="123.png"/>
<!-- 如果滚动的容器是页面内部某个容器 -->
<LazyLoadImg src="123.png" :container="() => $refs.container"/>
  • 源码
<template>
    <img
        ref="image"
        v-if="selfSrc"
        v-show="loaded"
        class="lazy-load-img"
        :src="selfSrc"
        @load="loadImage"
    />
    <div
        ref="contentDom"
        v-else-if="!loaded"
        class="lazy-load-img-container"
    >
        <div class="lazy-load-img-content"><div class="loading"></div></div>
    </div>
</template>

<script>
export default {
    name: 'LazyLoadImg',
    props: {
        src: {
            type: String,
            default: ''
        },
        offset: {
            type: Number,
            default: 50
        },
        container: {
            type: Function,
            default: () => (undefined)
        }
    },
    data() {
        return {
            loaded: false, // 已加载
            isVisible: false, // 是否在可视区域
            selfSrc: '',
        };
    },
    mounted() {
        this.$container = this.container() || document.documentElement || document.body;
        this.contentDom = this.$refs.contentDom;
        this.target = this.container() || window;
        this.target.addEventListener('scroll', this.scrollHandle);
    },
    beforeDestroy() {
        this.removeEvent();
    },
    methods: {
        scrollHandle() {
            // console.log(this.isVisible);
            clearTimeout(this.timeout);
            this.timeout = setTimeout(() => {
                const top = this.contentDom.offsetTop;
                const height = this.contentDom.clientHeight;
                const clientHeight = this.$container.clientHeight;
                const scrollTop = this.$container.scrollTop;
                const viewTop = top - (scrollTop + clientHeight);
                this.isVisible = viewTop < this.offset && (top + height + this.offset > scrollTop);
                // console.log(viewTop, top + height - scrollTop, this.isVisible);
                if (this.isVisible) {
                    this.selfSrc = this.src;
                    this.removeEvent();
                }
            }, 100);
        },
        removeEvent() {
            this.target.removeEventListener('scroll', this.scrollHandle);
        },
        loadImage() {
            this.loaded = true;
        }
    }
}
</script>
<style>
.lazy-load-img-container{
    display: inline-block;
    width: 600px;
    height: 350px;
    background-color: #eee;
    color: #aaa;
}
.lazy-load-img-container .lazy-load-img-content{
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lazy-load-img-container .loading{
    width: 15px;
    height: 15px;
    border-radius: 100%;
    margin: 2px;
    -webkit-animation-fill-mode: both;
    animation-fill-mode: both;
    border: 3px solid #ddd;
    border-bottom-color: transparent;
    height: 25px;
    width: 25px;
    background: transparent !important;
    display: inline-block;
    -webkit-animation: loadingRotate 0.75s 0s linear infinite;
    animation: loadingRotate 0.75s 0s linear infinite;
}
@keyframes loadingRotate {
    0% {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    50% {
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg); 
    }
}

</style>