前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VUI创建日志(一)——图片懒加载指令的实现

VUI创建日志(一)——图片懒加载指令的实现

作者头像
小皮咖
发布2019-11-05 15:07:32
6080
发布2019-11-05 15:07:32
举报
文章被收录于专栏:小皮咖小皮咖

1. 项目实现介绍

vue 项目搭建参考《Webpack4 搭建 Vue 项目》

文档使用 vuepress, 官方文档 vuepress.vuejs.org

发布文档 github pages + gh-page

项目地址 github.com/zxpsuper/vu…

文档地址 zxpsuper.github.io/vui-vue

处于自我摸索阶段,期待留下您的宝贵意见!

2. v-lazy 的基本实现

图片懒加载的基本原理:

  1. 先用占位图代替目标图片的 src 属性值
  2. 当图片的 offsetTop < innerHeight + scrollTop 时,即图片出现在窗口内部,此时修改 src 值为 data-src 的值
  3. 当然,这一切需要不断地监听滚动事件

先实现一个懒加载函数

代码语言:javascript
复制
var img = document.getElementsByTagName('img');
function lazyload() {
        //监听页面滚动事件
        var seeHeight = window.innerHeight; //可见区域高度
        var scrollTop =
            document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
        for (var i = 0; i < img.length; i++) {
            if (img[i].getAttribute('data-image-show')) continue; // 如果已经加载完成,则不需走下面流程
            if (img[i].offsetTop < seeHeight + scrollTop) {
                console.log(img[i].offsetTop, seeHeight, scrollTop);
                if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) {
                    img[i].src = img[i].getAttribute('data-src');
                    img[i].setAttribute('data-image-show', 'true'); // 给个标识,表示已经加载完成
                }
            }
        }
    }
复制代码

滚动监听

滚动监听,不断滚动便会不断触发滚动监听的函数,影响性能,因此在此需要加入一个防抖函数

代码语言:javascript
复制
// 防抖函数
function debounce(event, time) {
    let timer = null;
    return function(...args) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
                timer = null;
                event.apply(this, args);
            }, time);
    };
}
复制代码

添加监听和去除监听

代码语言:javascript
复制
window.removeEventListener('scroll', debounce(lazyload, 800));
window.addEventListener('scroll', debounce(lazyload, 800));
复制代码

添加指令

这里用到了自定义指令中的三个钩子函数 bind,inserted,unbind, 我们要让指令中占位图可修改,因此写成函数形式

代码语言:javascript
复制
const lazyload = function(Vue) {
    var img = document.getElementsByTagName('img'), evenFunction;
    function lazyload() {
        //监听页面滚动事件
        var seeHeight = window.innerHeight; //可见区域高度
        var scrollTop =
            document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
        for (var i = 0; i < img.length; i++) {
            if (img[i].getAttribute('data-image-show')) continue; // 如果已经加载完成,则不需走下面流程
            if (img[i].offsetTop < seeHeight + scrollTop) {
                console.log(img[i].offsetTop, seeHeight, scrollTop);
                if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) {
                    img[i].src = img[i].getAttribute('data-src');
                    img[i].setAttribute('data-image-show', 'true'); // 给个标识,表示已经加载完成
                }
            }
        }
    }
    // vui中的默认配置,用户可通过修改 Vue.$vuiLazyLoad.img 进行修改占位图
    Vue.$vuiLazyLoad = {
        img:
            'https://github.com/zxpsuper/Demo/blob/master/images/avatar.jpg?raw=true',
        imgLength: 0, // 懒加载的图片数量,当数量为 0 的时候移除滚动监听
    };
    
    lazyload(); //页面载入完毕加载可是区域内的图片
    evenFunction = debounce(lazyload, 800);
    window.removeEventListener('scroll', evenFunction);
    window.addEventListener('scroll', evenFunction);
    return {
        name: 'lazy',
        // 绑定
        bind(el, binding) {
            el.setAttribute('src', Vue.$vuiLazyLoad.img);
            el.setAttribute('data-src', binding.value);
            Vue.$vuiLazyLoad.imgLength++;
        },
        // 插入
        inserted(el) {
            // 暂时空
        },
        // 解绑
        unbind() {
            Vue.$vuiLazyLoad.imgLength--; // 每次解绑,自减
            if (!Vue.$vuiLazyLoad.imgLength && evenFunction)
                window.removeEventListener('scroll', evenFunction);
        },
    };
}
复制代码

使用新特性 IntersectionObserver

IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。

观察元素是否与视窗交叉,若是则修改 scrdata-src 值,并解除观察状态,当然这一切的前提是你在图片创建的时候观察图片本身,因此在图片插入时的钩子函数内

代码语言:javascript
复制
inserted(el) {
    if (IntersectionObserver) lazyImageObserver.observe(el);
},
复制代码

具体使用方法:

代码语言:javascript
复制
let lazyImageObserver
if (IntersectionObserver) {
    lazyImageObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach((entry, index) => {
            let lazyImage = entry.target;
            // 如果元素可见
            if (entry.intersectionRatio > 0) {
                if (lazyImage.getAttribute('src') == Vue.$vuiLazyLoad.img) {
                    lazyImage.src = lazyImage.getAtstribute('data-src');
                }
                lazyImageObserver.unobserve(lazyImage); // 解除观察
            }
        });
    });
}
复制代码

当然我们优先使用 IntersectionObserver, 若不支持则使用传统方法

注册指令

代码语言:javascript
复制
import Vue from 'vue'
Vue.directive('lazy', lazyLoad(Vue));
复制代码

3. 完整代码

代码语言:javascript
复制
const lazyLoad = function(Vue) {
    var img = document.getElementsByTagName('img');
    function lazyload() {
        console.log('滚动lazy');
        //监听页面滚动事件
        var seeHeight = window.innerHeight; //可见区域高度
        var scrollTop =
            document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
        for (var i = 0; i < img.length; i++) {
            if (img[i].getAttribute('data-image-show')) continue;
            if (img[i].offsetTop < seeHeight + scrollTop) {
                console.log(img[i].offsetTop, seeHeight, scrollTop);
                if (img[i].getAttribute('src') == Vue.$vuiLazyLoad.img) {
                    img[i].src = img[i].getAttribute('data-src');
                    img[i].setAttribute('data-image-show', 'true');
                }
            }
        }
    }
    Vue.$vuiLazyLoad = {
        img:
            'https://github.com/zxpsuper/Demo/blob/master/images/avatar.jpg?raw=true',
        imgLength: 0,
    };

    var lazyImageObserver, evenFunction;

    if (IntersectionObserver) {
        lazyImageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach((entry, index) => {
                let lazyImage = entry.target;
                // 如果元素可见
                if (entry.intersectionRatio > 0) {
                    if (lazyImage.getAttribute('src') == Vue.$vuiLazyLoad.img) {
                        lazyImage.src = lazyImage.getAttribute('data-src');
                    }
                    lazyImageObserver.unobserve(lazyImage);
                }
            });
        });
    } else {
        lazyload(); //页面载入完毕加载可是区域内的图片
        evenFunction = debounce(lazyload, 800);
        window.removeEventListener('scroll', evenFunction);
        window.addEventListener('scroll', evenFunction);
    }
    return {
        name: 'lazy',
        bind(el, binding) {
            el.setAttribute('src', Vue.$vuiLazyLoad.img);
            el.setAttribute('data-src', binding.value);
            Vue.$vuiLazyLoad.imgLength++;
        },
        inserted(el) {
            if (IntersectionObserver) lazyImageObserver.observe(el);
        },
        unbind() {
            Vue.$vuiLazyLoad.imgLength--;
            if (!Vue.$vuiLazyLoad.imgLength && evenFunction)
                window.removeEventListener('scroll', evenFunction);
        },
    };
};

export default lazyLoad;

function debounce(event, time) {
    let timer = null;
    return function(...args) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            timer = null;
            event.apply(this, args);
        }, time);
    };
}

复制代码

总结

本文是对vue自定义指令及懒加载原理的综合实现,若有错误,望指出共同进步。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年10月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 项目实现介绍
  • 2. v-lazy 的基本实现
    • 先实现一个懒加载函数
      • 滚动监听
        • 添加指令
          • 使用新特性 IntersectionObserver
            • 注册指令
            • 3. 完整代码
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档