angular实现图片懒加载

1.package.json中引入

"imgcache.js": "^1.0.0"

2.yarn install 保证插件的加入
3.verdor.js 加入引用

"imgcache.js/js/imgcache.js"

4.开始实现功能,一共自定义两个文件
5.新建image-loader.js

/**
 * 图片加载,已加载图片会缓存在应用本地,非应用环境使用浏览器缓存
 */
angular.module('app.services').factory('imageLoader', function($ionicPlatform, $cmApi, $cmLocalStorage) {
    // 本地存储预加载列表特征值
    const PRELOAD_HASH = 'preloadHash';
    // 本地存储预加载列表已加载计数
    const PRELOAD_COUNT = 'preloadCount';
    // 加载并行请求限制
    const LOAD_REQUEST_LIMIT = 5;
    // 预加载并行请求限制
    const PRELOAD_REQUEST_LIMIT = 3;
    // 图片缓存区容量
    const CACHE_QUOTA = 100 * 1024 * 1024;
    // 正在进行中的图片请求计数
    let requestCount = 0;
    // 请求队列
    const requestQueue = [];

    let imageCache = null;

    // 等待执行的预加载任务
    let preloadTask = null;

    let startPreloadWithDelay = null;

    // 初始化图片缓存
    $ionicPlatform.ready(function() {
        if (window.cordova && window.ImgCache) {
            ImgCache.options.chromeQuota = CACHE_QUOTA;
            // if (ionic.Platform.isIOS()) {
            //     ImgCache.options.useDataURI = true;
            // }
            window.ImgCache.init(function() {
                imageCache = window.ImgCache;
                // 如果有等待执行的预加载任务,则开始预加载
                if (preloadTask) {
                    preloadTask();
                    preloadTask = null;
                }
            }, angular.noop);
        }
    });

    // 处理请求队列中第一个请求
    function processRequestInQueue() {
        if (requestQueue.length) {
            requestQueue.shift()();
        }
    }

    // 生成对象特征值
    function generateHashCode(obj) {
        const str = JSON.stringify(obj);
        let hash = 0,
            i,
            chr,
            len;
        if (str.length === 0) return hash;
        for (i = 0, len = str.length; i < len; i++) {
            chr = str.charCodeAt(i);
            hash = (hash << 5) - hash + chr;
            hash |= 0;
        }
        return hash;
    }

    return {
        // 加载图片
        load: function($image, src, successCallback) {
            const deferred = $cmApi.defer();

            // 使用 web 环境直接加载图片
            const useOnlineImage = function() {
                const img = document.createElement('img');
                requestCount++;
                img.onload = function() {
                    $image[0].src = src;
                    if (successCallback) {
                        successCallback();
                    }
                    deferred.resolve();
                    requestCount--;
                };
                img.onerror = function() {
                    deferred.reject();
                    requestCount--;
                };
                img.src = src;
            };

            const loadHandler = function() {
                if (imageCache) {
                    // 加载图片,图片没有本地缓存时先缓存至本地
                    ImgCache.useCachedFileWithSource(
                        $image,
                        src,
                        function() {
                            if (successCallback) {
                                successCallback();
                            }
                            deferred.resolve();
                        },
                        function() {
                            useOnlineImage();

                            // 使用 wifi 时缓存图片
                            const connection = navigator.connection;
                            if (connection && connection.type == Connection.WIFI) {
                                ImgCache.cacheFile(src);
                            }
                        }
                    );
                } else {
                    useOnlineImage();
                }
            };

            if (requestCount <= LOAD_REQUEST_LIMIT) {
                loadHandler();
            } else {
                requestQueue.push(loadHandler);
            }

            deferred.promise.finally(processRequestInQueue);

            return deferred.promise;
        },
        // 加载本地图片
        loadLocalImage: function($image, src, successCallback) {
            const deferred = $cmApi.defer();
            if (imageCache) {
                ImgCache.useCachedFileWithSource(
                    $image,
                    src,
                    function() {
                        if (successCallback) {
                            successCallback();
                        }
                        deferred.resolve();
                    },
                    function() {
                        deferred.reject();
                    }
                );
            } else {
                deferred.reject();
            }
            return deferred.promise;
        },
        // 获取当前已缓存图片容量
        getCurrentSize: function() {
            if (imageCache) {
                return (imageCache.getCurrentSize() / 1024 / 1024).toFixed(1) + 'MB';
            }
        },
        // 清除本地缓存
        clearCache: function() {
            const deferred = $cmApi.defer();
            if (window.ImgCache) {
                ImgCache.clearCache(
                    function() {
                        // 清除本地预加载信息
                        $cmLocalStorage.remove(PRELOAD_HASH);
                        $cmLocalStorage.remove(PRELOAD_COUNT);

                        deferred.resolve({
                            status: 200,
                            data: {},
                        });
                    },
                    function() {
                        deferred.reject();
                    }
                );
            } else {
                deferred.reject();
            }
            return deferred.promise;
        },
        // 预加载图片
        preload: function(imageList) {
            const hash = generateHashCode(imageList);
            let i = 0;
            let loaded = 0; // 已完成计数
            let interval = null;

            // 检查上一次是否有部分预加载已完成
            if (hash == $cmLocalStorage.get(PRELOAD_HASH, 0)) {
                // 加载未完成内容
                loaded = +$cmLocalStorage.get(PRELOAD_COUNT, 0);
            } else {
                // 设置新预加载信息
                $cmLocalStorage.set(PRELOAD_HASH, hash);
                $cmLocalStorage.set(PRELOAD_COUNT, 0);
            }

            // 记录已加载数量
            const recordLoadedCount = function(index) {
                if (index > loaded) {
                    loaded = index;
                    // 每加载10张图或加载完成时记录一次加载进度
                    if (index % 10 === 0 || index === imageList.length - 1) {
                        $cmLocalStorage.set(PRELOAD_COUNT, index);
                    }
                }
            };

            // 预加载图片
            const preloadImage = function(index) {
                const src = imageList[index];
                ImgCache.isCached(src, function(src, cached) {
                    if (cached) {
                        recordLoadedCount(index);
                    } else {
                        requestCount++;
                        ImgCache.cacheFile(
                            src,
                            function() {
                                requestCount--;
                                recordLoadedCount(index);
                                processRequestInQueue();
                            },
                            function() {
                                requestCount--;
                                processRequestInQueue();
                            }
                        );
                    }
                });
            };

            // 停止预加载
            const stopPreload = function() {
                if (interval) {
                    clearInterval(interval);
                    interval = null;
                }
            };

            // 开始预加载
            const startPreload = function() {
                const connection = navigator.connection;
                if (!connection || connection.type != Connection.WIFI || interval) return;
                i = loaded;
                if (i < imageList.length) {
                    interval = setInterval(function() {
                        if (connection.type != Connection.WIFI) {
                            stopPreload();
                            return;
                        }
                        // 限制并发请求数量
                        if (requestCount > PRELOAD_REQUEST_LIMIT) return;

                        if (i < imageList.length) {
                            preloadImage(i);
                            i++;
                        }
                        if (i >= imageList.length) {
                            stopPreload();
                            document.removeEventListener('online', startPreloadWithDelay);
                            document.removeEventListener('offline', stopPreload);
                        }
                    }, 1000);
                }
            };

            // 网络恢复后延时1秒开始预加载
            startPreloadWithDelay = function() {
                setTimeout(function() {
                    startPreload();
                }, 1000);
            };

            document.addEventListener('online', startPreloadWithDelay, false);
            document.addEventListener('offline', stopPreload, false);
            // 检查图片缓存是否已初始化,预加载延迟至图片缓存初始化后开始
            if (imageCache) {
                startPreload();
            } else {
                preloadTask = startPreload;
            }
        },
    };
});

2.新建lazyload.js

/**
 * 延迟图片加载,图片进入可视区时才进行加载
 * preLoadVertical  <Integer>  控制预加载高度,默认 50
 * preLoadHorizontal  <Integer>  控制预加载宽度,默认 30
 * loadImmediate  <Boolean>  不检测是否在可视范围内直接开始加载,默认 false
 * lazyloadWatch  <Boolean>  是否监视图片url变更更新图片,默认 false
 */
angular.module('app.directives').directive('cmLazyload', function($document, imageLoader) {
    // 延迟加载样式类
    const CLASS_LAZY_LOAD = 'lazyload';
    const CLASS_LOADED = 'lazyload-loaded';
    const CLASS_FINISHED = 'lazyload-finished';
    let ANIMATIONEND_EVENT = '';

    if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
        ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
    } else {
        ANIMATIONEND_EVENT = 'animationend';
    }
    // 占位图片
    const SPACER =
        'data:image/gif;base64,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';
    // 加载失败后可重试次数上限
    const RETRY_LIMIT = 3;
    // 指令自增索引
    let count = 0;

    // 所有需要监听的滚动区
    const scrollMap = {};

    let clientHeight = $document[0].documentElement.clientHeight;
    // var clientWidth = $document[0].documentElement.clientWidth;
    window.addEventListener('resize', function() {
        clientHeight = $document[0].documentElement.clientHeight;
        // clientWidth = $document[0].documentElement.clientWidth;
    });

    // 添加滚动监听
    function addScrollHandler(scrollPanel, image) {
        const key = scrollPanel[0].$$hashKey;
        let scrollDelegate = scrollMap[key];
        if (!scrollDelegate) {
            const removeImageFromDelegate = function(id) {
                delete scrollDelegate.images[id];
                scrollDelegate.length--;

                // 滚动区内所有图片已加载时,不再监听滚动
                if (scrollDelegate.length === 0) {
                    scrollPanel.off('scroll', scrollDelegate.handler);
                    delete scrollMap[key];
                }
            };

            const imageLoadSuccessHandler = function(image) {
                return function() {
                    removeImageFromDelegate(image.id);
                    image.loading = false;
                };
            };

            const imageLoadErrorHandler = function(image) {
                return function() {
                    image.retryCount++;
                    if (image.retryCount >= RETRY_LIMIT) {
                        removeImageFromDelegate(image.id);
                    }
                    image.loading = false;
                };
            };

            // 滚动处理
            const scrollHandler = function(e) {
                let scrollTop = 0;
                const event = e.originalEvent;
                if (event.detail && event.detail.scrollTop) {
                    scrollTop = event.detail.scrollTop;
                } else {
                    scrollTop = event.target.scrollTop;
                }

                for (const id in scrollDelegate.images) {
                    const image = scrollDelegate.images[id];
                    if (!image.loading && isInView(image, scrollTop)) {
                        image.loading = true;
                        loadImage(image)
                            .success(imageLoadSuccessHandler(image))
                            .error(imageLoadErrorHandler(image));
                    }
                }
            };

            scrollDelegate = {
                scrollPanel: scrollPanel,
                images: {},
                length: 0,
                handler: ionic.throttle(scrollHandler, 200),
            };
            scrollPanel.on('scroll', scrollDelegate.handler);
            scrollMap[key] = scrollDelegate;
        }
        scrollDelegate.images[image.id] = image;
        scrollDelegate.length++;
    }

    // 检查元素是否在可视区垂直高度内
    function isInView(element, scrollTop) {
        const preLoadVertical = element.preLoadVertical;
        if (scrollTop !== undefined) {
            const top = element.offsetTop - scrollTop,
                bottom = top + element.height;

            return top <= clientHeight + preLoadVertical && bottom >= -preLoadVertical;
        } else {
            const $element = element.$element;
            const imageRect = $element[0].getBoundingClientRect();
            return (
                imageRect.top <= clientHeight + preLoadVertical &&
                imageRect.bottom >= -preLoadVertical
            );
        }
    }

    // 加载图片
    function loadImage(image) {
        const $element = image.$element;
        // 加载并缓存图片
        return imageLoader.load($element, image.src, function functionName() {
            $element.removeClass(CLASS_LAZY_LOAD).addClass(CLASS_LOADED);
        });
    }

    // 淡入动画结束后修改 class
    function animationEnd() {
        $(this)
            .removeClass(CLASS_LOADED)
            .addClass(CLASS_FINISHED);
    }

    // 获取元素坐标和尺寸
    function getElementRect($element) {
        let element = $element,
            offsetLeft = 0,
            offsetTop = 0,
            rect = $element[0].getBoundingClientRect();

        if ($element.is(':visible')) {
            do {
                const offset = ionic.DomUtil.getPositionInParent(element[0]);
                offsetLeft += offset.left;
                offsetTop += offset.top;
                element = element.offsetParent();
                if (element.is('html')) {
                    break;
                }
            } while (!element.hasClass('scroll-content'));
        }

        return {
            width: rect.width,
            height: rect.height,
            offsetLeft: offsetLeft,
            offsetTop: offsetTop,
        };
    }

    return {
        restrict: 'A',
        scope: false,
        link: function($scope, $element, $attr) {
            const scrollPanel = $element.parents('.scroll-content').eq(0);
            const preLoadVertical = parseInt($attr.preLoadVertical) || 50;
            const preLoadHorizontal = parseInt($attr.preLoadHorizontal) || 30;
            const loadImmediate = $attr.loadImmediate;
            let image = null;

            // 初始化延迟加载
            function init() {
                const src = $scope.$eval($attr.cmLazyload);
                if (!src) {
                    return;
                }
                // 处理 watch 导致图片重加载
                if (image) {
                    $element.off(ANIMATIONEND_EVENT, animationEnd);
                    $element.removeClass(CLASS_LOADED).removeClass(CLASS_FINISHED);
                }
                $element[0].src = SPACER;
                $element.addClass(CLASS_LAZY_LOAD);
                $element.on(ANIMATIONEND_EVENT, animationEnd);
                image = {
                    id: count++,
                    $element: $element,
                    loading: false,
                    retryCount: 0,
                    src: src,
                    preLoadVertical: preLoadVertical,
                    preLoadHorizontal: preLoadHorizontal,
                };

                // 释放时间片,等元素渲染完毕后获取元素坐标和尺寸
                setTimeout(function() {
                    angular.extend(image, getElementRect($element));
                }, 0);

                // 检查图片是否已缓存
                imageLoader
                    .loadLocalImage($element, src, function() {
                        $element.removeClass(CLASS_LAZY_LOAD).addClass(CLASS_LOADED);
                    })
                    .error(function() {
                        // 释放时间片,等元素渲染完毕后检查元素状态
                        setTimeout(function() {
                            if (loadImmediate || isInView(image)) {
                                loadImage(image);
                            } else {
                                angular.extend(image, getElementRect($element));
                                addScrollHandler(scrollPanel, image);
                            }
                        }, 0);
                    });
            }

            if ($attr.lazyloadWatch) {
                const listener = $scope.$watch($attr.cmLazyload, init);
                $scope.$on('$destroy', listener);
            } else {
                init();
            }
        },
    };
});

6.在对应的图片加载中可以添加使用,实现懒加载
将之前的

<img ng-src="{{item.picUrl}}" />

改为

<img cm-lazyload="item.picUrl" />

如果对您有帮助,请点个❤️吧😄

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值