Preload图片预加载(jQuery插件)
背景
我们在做页面的时候,从用户体验的角度出发,肯定是希望用户以最快的速度看到完整的页面信息,但在实际情况中经常会遇到些问题。
比如受网速影响,页面加载素材的时间比较长,页面会出现短时间的错乱或者闪屏;
或者页面素材比较多、比较大,需要一定的加载时间,特别有时候的活动页面,我们一般会把首屏做的更多多样化或者传达比较丰富的氛围的时候,我们的首屏素材上都会比较大,有时候也会给元素加上动画来传达信息,比如我之前做的WeGame新春活动页就是这样的情况;
一般我们的处理方案是在页面素材加载完成之前显示一个有趣(不那么枯燥)的loading页,那么从重构的角度出发,我们这个素材加载状态该怎么配合loading页做处理呢,于是想到了可以监测一下浏览器加载图片的状态,并同时响应处理,比如加载完时隐藏loading页,每加载完一张图片就更新加载的百分比进度条等。
当然,值得注意的是,我们应该尽量的让用户等待loading页的时间变短,因为不要忘了,loading页也只是向后兼容处理的方案,它本身不是必须存在的。
方案分析
**场景:**除了首屏loading页加载必须的图片之外(无序加载),可能还有其他情景比如漫画网站,需要加载完前一张图片再加载下一张(有序加载)
原理:通过image 的 onload 事件判断浏览器加载图片的状态。
实现:一般我们会这样处理:
1 2 3 4 5 | var img = new Image(); img.src = "logo.jpg" ; img.onload = function () { alert( "image is loaded" ); }; |
但是这样处理有点问题,首先我们来认识两点:
1.一旦Image对象设置了src值,浏览器就会向服务器发起请求,并缓存返回的图片。
2.浏览器请求是异步的。也就是说这段代码会遍历数组,每张图片几乎同时发起请求,并不需要等待服务器返回结果后顺序发起请求。就是说,js不会傻傻地在原地等待图片的加载,而是继续读代码,直到图片加载完成,触发onload事件,js才会回来执行onload里面的内容。
基于以上两点,如果我们先赋值了图片地址,浏览器已经加载完成的时候,onload绑定事件还没有赋值上去,这样就造成了一定的误差,所以改进如下:
1 2 3 4 5 | var img = new Image(); img.onload = function () { alert( "image is loaded" ); }; img.src = "logo.jpg" ; |
先绑定事件,在赋值图片地址,确保浏览器发起请求前,图片已经绑定了onload事件。
Preload插件实现
GitHub地址:https://github.com/xiangshuo1992/preload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /** * 图片预加载插件Preload * * @param array imgs 预加载的图片地址数组列表 * @param Object options 配置参数 */ ( function ($) { function Preload(imgs, options) { this .imgs = ( typeof imgs === 'string' ) ? [imgs] : imgs; this .options = { order: false , //默认值false,代表无序加载 minTimer: 0, //完成加载的最少时间,单位ms,默认为0,一般展示类型的loading动画会需要设置 each: null , //单张图片加载完执行的方法,一般是修改进度状态 end: null //所有图片加载完执行的方法,一般是隐藏loading页 }; this .timer = Date.now(); this .init(options); }; //插件初始化 Preload.prototype.init = function (options) { //配置参数合并 this .options = $.extend( this .options, options); if ( this .options.order) { this .ordered(); //有序加载 } else { this .unordered(); //无序加载 } }; // 有序加载 Preload.prototype.ordered = function () { var that = this , imgs = this .imgs, len = imgs.length, count = 0, options = this .options; load(); function load() { var img = new Image(); $(img).on( 'load error' , function () { options.each && options.each(count); if (count >= len-1) { //所有图片加载完毕,检查是否满足最小loading时间 var timerCount = Date.now() - that.timer; if (timerCount < options.minTimer) { var timeout = options.minTimer - timerCount; setTimeout( function () { options.end && options.end(); }, timeout); } else { options.end && options.end(); } } else { load(); } count++ }); // onload函数要写在改变src前,这样确保了onload函数一定会被调用 img.src = imgs[count]; } }; // 无序加载 Preload.prototype.unordered = function () { var that = this , imgs = this .imgs, len = imgs.length, count = 0, options = this .options; for ( var i = 0; i < len; i++) { var img = new Image(); $(img).on( 'load error' , function () { options.each && options.each(count); if (count >= len-1) { //所有图片加载完毕,检查是否满足最小loading时间 var timerCount = Date.now() - that.timer; if (timerCount < options.minTimer) { var timeout = options.minTimer - timerCount; setTimeout( function () { options.end && options.end(); }, timeout); } else { options.end && options.end(); } } count++; }) img.src = imgs[i]; } }; //扩展到jQuery对象上 $.extend($,{ preload: function (imgs, options) { new Preload(imgs, options); } }); })(jQuery); |
有序加载和无序加载
无序加载:前面我们说了,浏览器请求是异步的,所以我们的资源请求有可能是同时在加载过程中。
我用chrome浏览器的开发者工具,查看了无序加载时,图片的加载状态如下:
可以看到,会出现同时请求多张图片的情况,也就是说当我们希望所有的网速集中在一张图片上时,这个方案是不行的,于是就有了有序加载。
有序加载:前面我们是通过循环来实现无序加载的,我们要先实现图片资源有序加载,就需要在当前图片加载完成时才去加载下一张图片,这里我们用函数回调实现,虽然整个资源的加载时间变长了,但是却也保证了图片是有序出现的,在上面说的漫画网站的场景下非常适用。
我用chrome浏览器的开发者工具,查看了无序加载时,图片的加载状态如下:
总结
上面我们分析和实现了一个看起来非常简单,但是也包含了不少内容的图片预加载插件,从业务场景、方案分析、代码实现、说明文档、测试使用、总结输出这一系列的流程,我知道了,实现一个插件可简单,可复杂,可轻量,可丰富,不管怎样,任何的实现都是基于满足自己和团队的使用,合适的就是最好的。
最后,也希望能给大家提供一点帮助,如有建议,欢迎留言!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!