(vue)自定义指令实现图片懒加载插件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            width: 500px;
            height: 600px;
            overflow: scroll;
        }
        .box li {
            width: 300px;
            height: 200px;
        }
        img {
            width: 300px;
            height: 200px;
        }
    </style>
</head>
<body>
    <div class="box" id="app">
        <li v-for="(item,index) in imgs">
            <img v-lazy="item.src" alt="" srcset="">
        </li>
    </div>
    <script src="./vue-lazyload.js"></script>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
        let loading='http://127.0.0.1:5500/loading.gif'
        Vue.use(VueLazyload,{
            preLoad:1.3,
            loading
        })
        let vm=new Vue({
            el:'#app',
            data() {
                return {
                    name:'hahah',
                    imgs:[
                        {
                            src:'http://127.0.0.1:5500/1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/2.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/3.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/4.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/5.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/6.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/7.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/1.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/2.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/3.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/4.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/5.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/6.1.jpg'
                        },
                        {
                            src:'http://127.0.0.1:5500/7.1.jpg'
                        },
        
                    ]
                }
            },
        })
    </script>
    
</body>
</html>
function getscrollParent (el){
    let parent = el.parentNode;
    while(parent){
        if(/(scroll)|(auto)/ .test(getComputedStyle(parent)['overflow'])){
            return parent
        }
        parent = parent.parentNode
    }
    return parent
}

function loadImageAsync (src,resolve,reject){
    let image = new Image()
    image.src=src
    image.onload = resolve;
    image.onerror = reject;
}



const Lazy = (Vue)=>{
    class ReactveListener { // 每一个图片都构造成一个类的实例
        constructor({el,src,options,elRender}){
            this.el=el
            this.src=src
            this.options = options
            this.state = {loading:false}  // 没有加载过
            this.elRender=elRender
        }
        chechInView(){  // 检测图片是否在可视区域
            let {top} = this.el.getBoundingClientRect()
            return top < window.innerHeight * (this.options.preLoad || 1.3)

        }
        load(){  // 用来加载图片
            // 先加载loading
            this.elRender(this,'loading')
            // 如果加载OK的话 显示正常图片

            // console.log(this)

            // 懒加载的核心 就是new img
            loadImageAsync(this.src,()=>{
                
                this.state.loading = true
                this.elRender(this,'finish')
            },()=>{
                console.log('er')
                this.elRender(this,'error')
            })
        }
    }
    return class LazyClass{
        constructor(options){
            // 保存用户传的属性
            this.options= options
            this.bindHander = false

            this.listenerQueue = []

        }
        add(el,bindings,vnode){
            // 找到父亲元素
            Vue.nextTick(()=>{
                // 获取带有scroll属性的父集
                let scrollParent = getscrollParent(el)
                if(scrollParent&&!this.bindHander){
                    this.bindHander = true
                    // console.log(scrollParent)
                    scrollParent.addEventListener('scroll',this.handerLazyload.bind(this))
                }
                // 需要判断当前元素是否在容器可视区域中 ,如果不是就不用渲染
                const listener = new ReactveListener({
                    el,
                    src:bindings.value,
                    options:this.options,
                    elRender:this.elRender.bind(this)
                })

             

                // 把所有图片创建成实例放到 数组中
                this.listenerQueue.push(listener)

                // console.log(this.listenerQueue)


                this.handerLazyload()
            })
        }
        handerLazyload(){
            // 是否显示图片
            // 计算图片位置
            this.listenerQueue.forEach(listener => {
                // console.log(listener)
                if(!listener.state.loading){
                    let catIn = listener.chechInView()
                    // console.log(catIn)
                    catIn && listener.load()
                }
               
            });
        }
        elRender(listener,state){
            console.log(state)
            let el = listener.el
            let src = ''
            switch(state){
                case 'loading' :
                    src = listener.options.loading || ''
                    break;
                case 'error':
                    src = listener.options.error || ''
                    break;
                default:
                    src = listener.src
                    break;      
            }

            el.setAttribute('src',src)
        }
    }
}


const VueLazyload = {
    install(Vue,options){
        // 把所有逻辑封装到类中,把类在封装到函数中
        console.log('调用')
        const LazyClass = Lazy(Vue)
        const lazy = new LazyClass(options)
        Vue.directive('lazy',{
            bind:lazy.add.bind(lazy)
        })
    }
}

你可能感兴趣的:(常用功能与方法)