jquery插件系列之延迟加载

Fork Me On Github
zodream 编程技术 2017年10月

思路

  1. 当浏览器滚动到指定元素进行加载
  2. 可以加载多次

先上代码

ts
                                                                                                                                                                                                                                      
enum LazyMode {
    once,
    every
}

class LazyItem {
    constructor(
       public element: JQuery,
       public callback: Function,
       public mode: LazyMode = LazyMode.once,
       public diff: number|Function = 0
    ) {
       element.on('lazy-refresh', () => {
           this.refresh();
       });
    }

    private _lastHeight: number; // 上次执行的高度
    /**
     * 重新刷新
     */
    public refresh() {
        this._lastHeight = undefined;
    }
    /**
     * 判断能否执行
     * @param height 
     * @param bottom 
     */
    public canRun(height: number, bottom: number): boolean {
       if (this.mode == LazyMode.once && this._lastHeight != undefined) {
           return false;
       }
       if (this.element.parent().length < 1) {
           // 判断元素是否被移除
           return false;
       }
       if (typeof this.diff == 'function') {
           return this.diff.call(this, height, bottom);
       }
       let top = this.element.offset().top;
       return top + this.diff >= height && top < bottom;
    }

    public run(height: number, bottom: number, index: number = 0): boolean {
       // if (!this.canRun(height, bottom)) {
       //     return false;
       // }
       this.callback.call(this, this.element, height, bottom, index);
       this._lastHeight = height;
       return true;
    }
}

class Lazy {
    constructor(
       public element: JQuery,
       options ? : LazyOptions
    ) {
       this.options = $.extend({}, new LazyDefaultOptions(), options);
       let $window = $(window);
       let instance = this;
       this._init();
       $window.scroll(function () {
           instance.scrollInvote();
       });
       // 首次执行
       this.scrollInvote();
    }

    public options: LazyOptions;

    private _data: Array<LazyItem>;

    /**
     * 页面滚动触发更新
     */
    public scrollInvote() {
       let $window = $(window);
       let height = $window.scrollTop();
       let bottom = $window.height() + height;
       this.run(height, bottom);
    }

    public run(height: number, bottom: number) {
       if (!this._data) {
           return;
       }
       let index: number = 0;
       for (let i = 0, length = this._data.length; i < length; i ++) {
           let item = this._data[i];
           if (item.canRun(height, bottom)) {
               item.run(height, bottom, index ++);
           }
           // if (item.run(height, bottom) && item.mode == LazyMode.once) {
           //     this._data.splice(i, 1);
           // }
       }
    }
    // 暂时只做一次
    private _init() {
       this._data = [];
       let instance = this;
       this.element.each(function (i, ele) {
           let item = new LazyItem(
               $(ele), 
               typeof instance.options.callback != 'function' ? Lazy.getMethod(instance.options.callback) : instance.options.callback,
               instance.options.mode, 
               instance.options.diff);
           instance._data.push(item);
       });
       $.each(this.options.data, (i, item: any) => {
           if (item instanceof LazyItem) {
               this._data.push(item);
               return;
           }
           if (typeof i == 'string') {
               item['tag'] = i;
           }
           $(item.tag).each(function (i, ele) {
               let lazyItem = new LazyItem(
                   $(ele), 
                   typeof item.callback != 'function' ? Lazy.getMethod(item.callback) : item.callback, 
                   item.mode || LazyMode.once, 
                   item.diff || 0 );
               instance._data.push(lazyItem);
           })
       });
    }

    /**
     * 全局方法集合
     */
    public static methods: {[name: string]: Function} = {};

    /**
     * 添加方法
     * @param name 
     * @param callback 
     */
    public static addMethod(name: string, callback: Function) {
        this.methods[name] = callback;
    }

    /**
     * 获取方法
     * @param name 
     */
    public static getMethod(name: string): Function {
        return this.methods[name];
    }
}
/**
 * 加载图片,如需加载动画控制请自定义
 */
Lazy.addMethod('img', function (imgEle: JQuery) {
   let img = imgEle.attr('data-src');
   $("<img />")
       .bind("load", function () {
           if (imgEle.is('img') || imgEle.is('video')) {
               imgEle.attr('src', img);
               return;
           }
           imgEle.css('background-image', 'url(' + img + ')');
       }).attr('src', img);
});
/**
 * 加载模板,需要引用 template 函数
 */
Lazy.addMethod('tpl', function (tplEle: JQuery) {
   let url = tplEle.attr('data-url');
   tplEle.addClass('lazy-loading');
   let templateId = tplEle.attr('data-tpl');
   $.get(url, data => {
       let html = '';
       if (typeof data === 'object') {
            if (data.code != 200) {
                return;
            }
            html = typeof data.data != 'string' ? template(templateId, data.data) : data.data;
       } else {
           html = data;
       }
       tplEle.removeClass('lazy-loading');
       tplEle.html(html);
       tplEle.trigger('lazyLoaded');
   }, typeof templateId === 'undefined' ? null : 'json');
});
/**
 * 滚动加载模板,需要引用 template 函数
 */
Lazy.addMethod('scroll', function (moreEle: JQuery) {
   let page: number = parseInt(moreEle.attr('data-page') || '0') + 1;
   let url = moreEle.attr('data-url');
   let templateId = moreEle.attr('data-tpl');
   let target = moreEle.attr('data-target');
   $.getJSON(url, {
       page: page
   }, function (data) {
       if (data.code != 200) {
           return;
       }
       if (typeof data.data != 'string') {
           data.data = template(templateId, data.data);
       }
       $(target).html(data.data);
       moreEle.attr('data-page', page);
   });
});

interface LazyOptions {
   [setting: string]: any,
   data ? : {[tag: string]: string | Object} | Array <Object> | Array < Lazy > ,
   tag ? : string | JQuery,
   callback ? : string | Function, // 回调
   mode ? : LazyMode, //执行模式
   diff ? : number|Function, //距离可视化区域的距离
}

class LazyDefaultOptions implements LazyOptions {
    mode: LazyMode = LazyMode.once;
    diff: number = 0
}

;
(function ($: any) {
    $.fn.lazyload = function (options ? : LazyOptions) {
        return new Lazy(this, options);
    };
})(jQuery);
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230

本插件使用 typescript 编写, js 请查看 GITHUB

本插件内置两个方法:

1.简单的图片加载。可以参考增加加载动画 给 img 的 src 设置一张 加载动态图片

html
       
<img src="loading.gif" data-src="image.jpg" class="lazy">

<script>
$("img.lazy").lazyload({
    callback: 'img'
});
</script>
1234567

2.局部加载。依赖 template 函数(参考 art-template

html
  
<div class="templateLazy lazy-loading" data-url="1" data-tpl="temp_tpl">div>
12

主要有两个参数 :

data-url   请求网址

data-tpl 模板元素id

lazy-loading 为加载动画)

html
   
<script id="temp_tpl" type="text/template">
<div>{{ id }}</div>
</script>
123
ts
    
$(".templateLazy").lazyload({
    callback: 'tpl'
});
1234

请注意,本插件依赖 jquery

点击查看全文
0 632 0