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,R0lGODlh1gDWAPMEAJeXlwEBARsbG9bW1uHh4bOzs4GBgcTExFRUVLq6ujQ0NAAAAAAAAAAAAAAAAAAAACH5BAUFAAsAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAA1gDWAAAE/nDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXAABANYdAgECFQME1QUGFQHnFAcFBdQEAgIIFOcBEwTrCdXvAuwS8xMJ6wZUM/BOwQR/Cwbc26bgXbkFCNcVEGetgD5x/hQWOEBBI8Vj/gUUKNA2AYFDC/bqAQyYDIBIBQj4HRBAMoM6iRyVIXgJUyAHiQUSfFRWYOfLDhp9PnOpgB8HpdEIPNzWAui9ocIOADDAtSsIqwuLdR07tQNYoca0ki1LVQXUaG8zJIXLksPKoFiREbiLr8PNdTmT/Q26QaFKiXGHacQ7YaIFjYETAsw7zF4BqDcFSpQgkTI0y/w2JwxbbaVP0QtMV9PYdwFq0KXXfUS94Oa4jRRoJ/TcjMBbgK3bXtjLW7jx48iTK1/OvLnz59CjS59Ovbr169iza9/Ovbv37+DDix9Pvrz58+jTq1/Pvr379/Djy59Pv779+/jz69/Pv7///wAGGyjggAQWaOCBCCao4IIMNujggxBGKOGEFNYXAQAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXQEG1h0CAQIVBAXW4RQB5RQCAADVAt7k5hIF6QHr7AcT5fMSAekE1QXsA+69IyBP2wB24/AxSJAOwDhr7NopHJCu3bMEBAjYO2eRQjwEnNEOZMyYQEICAdm0UTAwEpwClRgwtoSZQSQBkDQzpMzJc4OCAwaCCu3JAKjQoT1/Hg1KtKnTp1Bd4IxKtarVq1izat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz6BDix5NurTp0xciAAAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXAYC1h4DAgMWBtUJBxUC5RQDAQHVBATgE+XZEgbp8dPsBAnv5hIC6dYH9/TVo6etALtxDOAxUJAugLsJCqIlCJjQHIF03iYQAACgQDIFqAYO5JtgsN0FdRIKBOAIgECyBAZiHoi4kAACDQgEsARQDxmCmDFvdtjpsJmCA0ARbtjYUmLMkRwGQJWorarVq1izat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz6BDix5NurTp06hTq17NurXr17Bjy74SAQAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXAgECNYdBQQFFd/VCgkVBOYVAgLVBgbaE+YEFOnW7AYK7+cSA+nh0wnsB/DFY1Ag3YBtB9iRYwBPQjp12xTUk9CQwbwKAQIYiIZAoQUDmQIWgswYYJqBhRkGkAxwcNuFlRCjbeRQIKNLDgTu3dx5AoDPnwH68fxJFADPCUUBBD3KtKnTp1CjSp1KtarVq1izat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz6BDix5NujShCAAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXAkFCdYdBgUWB9rbFAXk4wQE4uPlEgnn6OkS5N4S7gfw8esH7vf48+7h9+QxMHBuXjwBAvghIKBgwgCEAgze0wYxIb8JFQfYu4gxIsePkyBHBBhJ0uJFkigDcEwZQICBkDAZEAAIkgAAAO9A3rwZQKJCATsBmORYIMDOnB9tAngZs6nTp1CjSp1KtarVq1izat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz1MiAAAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TVXAoHCtYdBwYHFQjbFgbkFAYFBeLm5RPoBuoT5O8S5wXh8AzyEgjo6fj57Pr9o1eOX4F5Eg4QIJAAXwKECRYu/DZQQQGJBPwNxMhw4AQErxM9VkAosqQGAShTDiAJL6VLAR5fChhgsuYEbTULBAhgc+dOASwdDvAZgCY+AgDuGRDgE98AAABgUmCKTwFUABpFIgXAs2YAqARqGrhqUwBUnCYz2lzLtq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz6BDix5NurTp06hTq17NurXr17Bjy55Nu7bt27gjRwAAIfkEBQUADAAsAAAAANYA1gCDAQEBGxsbNDQ0VFRUgYGBl5eXs7Ozurq6xMTE1tbW4eHhAAAAAAAAAAAABP6QyUmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2Kx2y+16v+CweEwum8/otHrNbrvf8Lh8Tq/b7/i8fs/v+/+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dZ0CQrXHAgGBtsaCt4H4BkH3gnlFwnj6hfeBtruFOwGCPTe8uD6DArn6PMkdIN3LyA8Awf4uauXLiA9hxA7EJhIsUBBdxQzEgiokYDFiKcgQ04wIEBAAZAlSw4ImVLAvAIBKAxIeRIcgQABVk446dIagJ8ScAb45u4ngAk4e6ozOkEAznlMkT5dCnTCgKnlolIgWi4AgJggCwCoKbKs2bNo06pdy7at27dw48qdS7eu3bt48+rdy7ev37+AAwseTLiw4cOIEytezLix48eQI0ueTLmy5cuYM2vezLmz58+gQ4seTbq06dOoU6tezbq169ewY9uIAAAh+QQFBQAMACwAAAAA1gDWAIMBAQEbGxs0NDRUVFSBgYGXl5ezs7O6urrExMTW1tbh4eH///8AAAAAAAAAAAAE/pDJSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6bz+i0es1uu9/wuHxOr9vv+Lx+z+/7/4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPkFwbn6AcK3ujtB+ztBurl3wbkCAQE7+H5/QngCgr0I1CAmwEB/xgkGLjvGQIABC4IEFDhQL6EzwQAADCAQYCPmBImUrxWYCOAdR8DSCgwsaO1ABsjegQpYcDEgtQIbFQpISUFkdVgAsA5k+eEidU2juxJcwLRaQIrCAiwFJwBo/Syat3KtavXr2DDih1LtqzZs2jTql3Ltq3bt3Djyp1Lt67du3jz6t3Lt6/fv4ADCx5MuLDhw4gTK17MuLHjx5AjS55MubLly5gza97MubPnz6BDix5NulcEACH5BAUFAAwALAAAAADWANYAgwEBARsbGzQ0NFRUVIGBgZeXl7Ozs7q6usTExNbW1uHh4f///wAAAAAAAAAAAAT+kMlJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufoFQnUCQMeCQYG69AEAAAEHQfxBgcKzQYB7NnzgGCfAQTLBAgEIABhBgQE/DFQoC/evGP17AUoICHBRguuBggQ4DghgT6JxwrYwzdhQIAA+ATIlCCSwEVnKCUAfClBpgAJB2peE8Cz50wJBUQesPbyp1GnDBIIpUY0gIEJPikkZTntpTusRyccuBntaoUBAr6Kg5qurdu3cOPKnUu3rt27ePPq3cu3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5suXLmDNr3sy5s+fPoEOLHk26tOnTqFOrXs26tevXsGPLnk27tu3bfiMAACH5BAUFAAwALAAAAADWANYAgwEBARsbGzQ0NFRUVIGBgZeXl7Ozs7q6usTExNbW1uHh4f///wAAAAAAAAAAAAT+kMlJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+TVBQEA6ere6u0A3ufu7+XlCgTkAQEF4QL5+d8D/AUYsCGBgWn+BByUoNCCQQMIohn4V0GAAAkEMkowwFEBNouQFxlkvMfg4YFrAyzuE6lRwgGOCcxZJIixJQMFHBdSs0hhJAUEHKvx7GlTQgKP5ioUILAyHAICEelJnUq1qtWrWLNq3cq1q9evYMOKHUu2rNmzaNOqXcu2rdu3cOPKnUu3rt27ePPq3cu3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5suXLmDNr3sy5s68IACH5BAUFAAwALAAAAADWANYAgwEBARsbGzQ0NFRUVIGBgZeXl7Ozs7q6usTExNbW1uHh4f///wAAAAAAAAAAAAT+kMlJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3bMGAAAE3hQE4QABBeQSAucAAgjrBQHu6xLm4vYSCeP6/v8AAwocSKGAAAH2Dgw4eHAdQ4QJvBk8OGCCAgLxth1UN6EAAQKHBhgYGNmN30cJI0Ny+0jgAEqS2w6wnJBy5ceIL1Vq+6hTJExtCXoyOGDApT4FBxQQXMq0qdOnUKNKnUq1qtWrWLNq3cq1q9evYMOKHUu2rNmzaNOqXcu2rdu3cOPKnUu3rt27ePPq3cu3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5stcIACH5BAUFAAwALAAAAADWANYAgwEBARsbGzQ0NFRUVIGBgZeXl7Ozs7q6usTExNbW1uHh4f///wAAAAAAAAAAAAT+kMlJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW13QFBNgcAAAC3BreAAbhGATeAeYYAd7b6xUF4wnmAeAUAt4D9QH25QwIALwLN6BfvwH04EkwIMCgOoUU+hWASEEBxYsUDAwQwLGjwo6fIO+t0xhSJMaTGU8eIEAAAcUELGNC1BazgEV4K1kWcLnwJjeYBA5QQGDAQEKICor+o3ig6FGFCYoKvdjUgE+IRXleVPAUpdevYMOKHUu2rNmzaNOqXcu2rdu3cOPKnUu3rt27ePPq3cu3r9+/gAMLHky4sOHDiBMrXsy4sePHkCNLnky5suXLmDNr3sy5s+fPoEOLHk26tOnTqFOrXhwBADs=';
// 加载失败后可重试次数上限
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" />
如果对您有帮助,请点个❤️吧😄