Vue自定义指令--基于vue2

26 阅读1分钟

v-lazyload


const getScrollParent = (el) => {
    const parent = el.parentNode
    while(parent){
        if(/scroll|auto/.test(getComputedStyle(parent).overflow)){
            return parent
        }
        parent = parent.parentNode
    }
    return parent
}

const loadImgAsync = (src, resolve, reject) => {
    const img = new Image()
    img.src = src
    img.onload = resolve
    img.onerror = reject
}

class ReactiveListener {
    constructor({el, src, options, elRender}){
        this.el = el
        this.src = src
        this.options = options
        this.elRender = elRender
        this.state = {
            loading: false,
            error: false
        }
    }
    checkInView() { // 检测这个图片是否在可视区域内
        const { top } = this.el.getBoundingClientRect()
        return top < window.innerHeight * (this.options.preload || 1.3) 
    }
    load(){
        this.elRender(this, 'loading')
        loadImgAsync(this.src, () => {
            this.state.loading = true
            this.elRender(this, 'finish')
        }, () => {
            this.state.error = true
            this.elRender(this, 'error')
        })
    }
}

const lazy = (Vue) => {
    return class {
        constructor(options) {
            this.options = options
            this.bindHandle = false
            this.listenerQueue = []
        }
        handleLazyload(){
            // 这里看下是否显示这个图片
            this.listenerQueue.forEach(listener => {
                let canIn = listener.checkInView()
                if(canIn){
                    listener.load()
                }
            })
        }
        add(el, bindings, vnode){
            Vue.$nextTick(() => {
                let scrollParent = getScrollParent(el)
                if(scrollParent && !this.bindHandle){
                    this.bindHandle = true
                    scrollParent.addEventListener('scroll', this.handleLazyload.bind(this))
                }
                const listener = new ReactiveListener({
                    el,
                    src: bindings.value,
                    options:this.options,
                    elRender: this.elRender.bind(this)
                })
                this.listenerQueue.push(listener)
                this.handleLazyload()
            })
        }
        elRender(listener, 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){
        let LazyClass = lazy(Vue)
        let lazy = new LazyClass(options)
        Vue.directive('lazyload', {
            bind: lazy.add.bind(lazy)
        })
    }
}