ホームページ >ウェブフロントエンド >jsチュートリアル >Javascript イメージの preloading_javascript スキルを理解する
画像のプリロードは、ユーザー エクスペリエンスを向上させる優れた方法です。ブラウザに画像が事前に読み込まれているため、訪問者はスムーズにサイトを閲覧し、驚くほど速い読み込み時間を楽しむことができます。これは、画像が大きな割合を占める画像ギャラリーや Web サイトにとって非常に有益です。これにより、画像が迅速かつシームレスに公開され、Web サイトのコンテンツを閲覧する際のユーザー エクスペリエンスが向上します。この記事では、Web サイトのパフォーマンスと使いやすさを向上させるための 3 つの異なるプリロード手法を紹介します。
方法 1: CSS と JavaScript を使用してプリロードを実装する
プリロードされた画像を実装するには、CSS、JavaScript、およびその 2 つのさまざまな組み合わせの使用など、さまざまな方法があります。これらのテクノロジーは、さまざまな設計シナリオに従って対応するソリューションを設計でき、非常に効率的です。
CSS を使用して画像を簡単かつ効率的にプリロードするだけです。コードは次のとおりです。
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; } #preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; } #preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
これら 3 つの ID セレクターを (X)HTML 要素に適用すると、CSS の背景属性を通じてオフスクリーンの背景に画像をプリロードできます。これらの画像へのパスが同じである限り、ブラウザは、Web ページの他の場所で呼び出されたときに、レンダリング プロセス中にプリロードされた (キャッシュされた) 画像を使用します。シンプルかつ効率的で、JavaScript は必要ありません。
この方法は効率的ですが、まだ改善の余地があります。このメソッドを使用して読み込まれた画像は、ページ上の他のコンテンツと一緒に読み込まれるため、ページの全体的な読み込み時間が長くなります。この問題を解決するために、ページが読み込まれるまでプリロード時間を遅らせるための JavaScript コードを追加しました。コードは次のとおりです:
function preloader() { if (document.getElementById) { document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px"; document.getElementById("preload-02").style.background = "url(http://domain.tld/image-02.png) no-repeat -9999px -9999px"; document.getElementById("preload-03").style.background = "url(http://domain.tld/image-03.png) no-repeat -9999px -9999px"; } } function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { if (oldonload) { oldonload(); } func(); } } } addLoadEvent(preloader);
スクリプトの最初の部分では、クラス セレクターを使用して要素を取得し、その要素に背景属性を設定して、さまざまな画像をプリロードします。
スクリプトの 2 番目の部分では、addLoadEvent() 関数を使用して、ページが読み込まれるまで preloader() 関数の読み込み時間を遅らせます。
ユーザーのブラウザで JavaScript が適切に実行されない場合はどうなりますか?非常に簡単です。ページが画像を呼び出すと、画像はプリロードされず、通常どおり表示されます。
方法 2: JavaScript を使用してプリロードを実装する
上記の方法は非常に効率的な場合もありますが、実際の実装プロセスでは時間がかかりすぎることが徐々にわかりました。代わりに、画像のプリロードには純粋な JavaScript を使用することを好みます。以下に、最新のすべてのブラウザで美しく機能する 2 つのプリロード方法を示します。
JavaScript コード スニペット 1
必要なイメージのパスと名前を編集してロードするだけです。実装は簡単です。
<div class="hidden"> <script type="text/javascript"> var images = new Array() function preload() { for (i = 0; i < preload.arguments.length; i++) { images[i] = new Image() images[i].src = preload.arguments[i] } } preload( "http://domain.tld/gallery/image-001.jpg", "http://domain.tld/gallery/image-002.jpg", "http://domain.tld/gallery/image-003.jpg" ) </script> </div>
この方法は、大量の画像をプリロードする場合に特に適しています。私のギャラリー Web サイトでは、このテクノロジーを使用して 50 を超える画像をプリロードしています。このスクリプトをログイン ページに適用すると、ユーザーがログイン ID を入力するとすぐに、ほとんどのギャラリー画像がプリロードされます。
JavaScript コード スニペット 2
この方法は上記の方法と似ており、任意の数の画像をプリロードすることもできます。次のスクリプトを任意の Web ページに追加し、プログラムの指示に従って編集します。
<div class="hidden"> <script type="text/javascript"> if (document.images) { img1 = new Image(); img2 = new Image(); img3 = new Image(); img1.src = "http://domain.tld/path/to/image-001.gif"; img2.src = "http://domain.tld/path/to/image-002.gif"; img3.src = "http://domain.tld/path/to/image-003.gif"; } </script> </div>
ご覧のとおり、画像をロードするたびに、「img1 = new Image();」などの変数を作成し、「img3.src = 」などの画像ソース アドレスを宣言する必要があります。 ./path/to/image-003.gif";"。このパターンを使用すると、必要な数の画像をロードできます。
このメソッドを再度改良しました。このスクリプトを関数にラップし、addLoadEvent() を使用して、ページが読み込まれるまでプリロード時間を遅らせます。
function preloader() { if (document.images) { var img1 = new Image(); var img2 = new Image(); var img3 = new Image(); img1.src = "http://domain.tld/path/to/image-001.gif"; img2.src = "http://domain.tld/path/to/image-002.gif"; img3.src = "http://domain.tld/path/to/image-003.gif"; } } function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { if (oldonload) { oldonload(); } func(); } } } addLoadEvent(preloader);
方法 3: Ajax を使用してプリロードを実装する
上記の方法は十分にクールではないようです。それでは、Ajax を使用して画像のプリロードを実装する方法を見てみましょう。この方法では、DOM を使用して画像をプリロードするだけでなく、CSS、JavaScript、その他の関連するものもプリロードします。 JavaScript を直接使用する場合よりも Ajax を使用する利点は、JavaScript と CSS の読み込みが現在のページに影響を与えないことです。この方法はシンプルで効率的です。
window.onload = function() { setTimeout(function() { // XHR to request a JS and a CSS var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain.tld/preload.js'); xhr.send(''); xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain.tld/preload.css'); xhr.send(''); // preload image new Image().src = "http://domain.tld/preload.png"; }, 1000); };
上記のコードは、「preload.js」、「preload.css」、および「preload.png」をプリロードします。 1000 ミリ秒のタイムアウトは、スクリプトがハングして通常のページで機能上の問題が発生するのを防ぐためです。
次に、JavaScript を使用して読み込みプロセスを実装する方法を見てみましょう:
window.onload = function() { setTimeout(function() { // reference to <head> var head = document.getElementsByTagName('head')[0]; // a new CSS var css = document.createElement('link'); css.type = "text/css"; css.rel = "stylesheet"; css.href = "http://domain.tld/preload.css"; // a new JS var js = document.createElement("script"); js.type = "text/javascript"; js.src = "http://domain.tld/preload.js"; // preload JS and CSS head.appendChild(css); head.appendChild(js); // preload image new Image().src = "http://domain.tld/preload.png"; }, 1000); };
ここでは、DOM を通じて 3 つの要素を作成し、3 つのファイルをプリロードします。上で述べたように、Ajax では、ファイルの読み込みは読み込みページには適用されません。この観点から見ると、Ajax メソッドは JavaScript よりも優れています。
追加情報: ロード完了コールバック関数
次のコードを書きます:
function loadImage(url, callback) { var img = new Image(); img.src = url; img.onload = function(){ //图片下载完毕时异步调用callback函数。 callback.call(img); // 将callback函数this指针切换为img。 }; }
在firefox中测试一下,发现不错,果然和预想的效果一样,在图片下载后,就会弹出图片的宽度来。无论点击多少次或者刷新结果都一样。
不过,做到这一步,先别高兴太早——还需要考虑一下浏览器的兼容性,于是,赶紧到ie里边测试一下。没错,同样弹出了图片的宽度。但是,再点击load的时候,情况就不一样了,什么反应都没有了。刷新一下,也同样如此。
经过对多个浏览器版本的测试,发现ie6、opera都会这样,而firefox和safari则表现正常。其实,原因也挺简单的,就是因为浏览器的缓存 了。当图片加载过一次以后,如果再有对该图片的请求时,由于浏览器已经缓存住这张图片了,不会再发起一次新的请求,而是直接从缓存中加载过来。对于 firefox和safari,它们视图使这两种加载方式对用户透明,同样会引起图片的onload事件,而ie和opera则忽略了这种同一性,不会引 起图片的onload事件,因此上边的代码在它们里边不能得以实现效果。
怎么办呢?最好的情况是Image可以有一个状态值表明它是否已经载入成功了。从缓存加载的时候,因为不需要等待,这个状态值就直接是表明已经下载了,而从http请求加载时,因为需要等待下载,这个值显示为未完成。这样的话,就可以搞定了。
经过一些分析,终于发现一个为各个浏览器所兼容的Image的属性——complete。所以,在图片onload事件之前先对这个值做一下判断即可。最后,代码变成如下的样子:
function loadImage(url, callback) { var img = new Image(); //创建一个Image对象,实现图片的预下载 img.src = url; if (img.complete) { // 如果图片已经存在于浏览器缓存,直接调用回调函数 callback.call(img); return; // 直接返回,不用再处理onload事件 } img.onload = function () { //图片下载完毕时异步调用callback函数。 callback.call(img);//将回调函数的this替换为Image对象 }; };
虽然代码很简单,但却很实用。
附:再谈javascript图片预加载
lightbox类效果为了让图片居中显示而使用预加载,需要等待完全加载完毕才能显示,体验不佳(如filick相册的全屏效果)。javascript无法获取img文件头数据,真的是这样吗?本文通过一个巧妙的方法让javascript获取它。
这是大部分人使用预加载获取图片大小的例子:
var imgLoad = function (url, callback) { var img = new Image(); img.src = url; if (img.complete) { callback(img.width, img.height); } else { img.onload = function () { callback(img.width, img.height); img.onload = null; }; }; }; 可以看到
上面必须等待图片加载完毕才能获取尺寸,其速度不敢恭维,我们需要改进。
web应用程序区别于桌面应用程序,响应速度才是最好的用户体验。如果想要速度与优雅兼得,那就必须提前获得图片尺寸,如何在图片没有加载完毕就能获取图片尺寸?
十多年的上网经验告诉我:浏览器在加载图片的时候你会看到图片会先占用一块地然后才慢慢加载完毕,并且不需要预设width与height属性,因为浏览器能够获取图片的头部数据。基于此,只需要使用javascript定时侦测图片的尺寸状态便可得知图片尺寸就绪的状态。
当然实际中会有一些兼容陷阱,如width与height检测各个浏览器的不一致,还有webkit new Image()建立的图片会受以处在加载进程中同url图片影响,经过反复测试后的最佳处理方式:
// 更新: // 05.27: 1、保证回调执行顺序:error > ready > load;2、回调函数this指向img本身 // 04-02: 1、增加图片完全加载后的回调 2、提高性能 /** * 图片头数据加载就绪事件 - 更快获取图片尺寸 * @version 2011.05.27 * @author TangBin * @see http://www.planeart.cn/?p=1121 * @param {String} 图片路径 * @param {Function} 尺寸就绪 * @param {Function} 加载完毕 (可选) * @param {Function} 加载错误 (可选) * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () { alert('size ready: width=' + this.width + '; height=' + this.height); }); */ var imgReady = (function () { var list = [], intervalId = null, // 用来执行队列 tick = function () { var i = 0; for (; i < list.length; i++) { list[i].end ? list.splice(i--, 1) : list[i](); }; !list.length && stop(); }, // 停止所有定时器队列 stop = function () { clearInterval(intervalId); intervalId = null; }; return function (url, ready, load, error) { var onready, width, height, newWidth, newHeight, img = new Image(); img.src = url; // 如果图片被缓存,则直接返回缓存数据 if (img.complete) { ready.call(img); load && load.call(img); return; }; width = img.width; height = img.height; // 加载错误后的事件 img.onerror = function () { error && error.call(img); onready.end = true; img = img.onload = img.onerror = null; }; // 图片尺寸就绪 onready = function () { newWidth = img.width; newHeight = img.height; if (newWidth !== width || newHeight !== height || // 如果图片已经在其他地方加载可使用面积检测 newWidth * newHeight > 1024 ) { ready.call(img); onready.end = true; }; }; onready(); // 完全加载完毕的事件 img.onload = function () { // onload在定时器时间差范围内可能比onready快 // 这里进行检查并保证onready优先执行 !onready.end && onready(); load && load.call(img); // IE gif动画会循环执行onload,置空onload即可 img = img.onload = img.onerror = null; }; // 加入队列中定期执行 if (!onready.end) { list.push(onready); // 无论何时只允许出现一个定时器,减少浏览器性能损耗 if (intervalId === null) intervalId = setInterval(tick, 40); }; }; })();
调用例子:
imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () { alert('size ready: width=' + this.width + '; height=' + this.height); });
是不是很简单?这样的方式获取摄影级别照片尺寸的速度往往是onload方式的几十多倍,而对于web普通(800×600内)浏览级别的图片能达到秒杀效果。