你如何处理 html5 的 canvas 图片加载异步?
How do you deal with html5's canvas image load asynchrony?
我一直在了解 html5 的 canvas。因为图像可能需要一段时间才能加载,似乎合适的技术是使用 onload
等待图像加载后再尝试绘制它。所以:
<canvas id="fig" width="400" height="400"></canvas>
var fig = document.getElementById('fig1');
var ctx = fig.getContext('2d');
var img = new Image();
img.onload = function() { ctx.drawImage(img, 300, 100); };
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
但是,很可能在 onload 函数可以执行 drawImage()
之前执行此后的代码,可能会导致意外行为:
ctx.translate(0,400); ctx.scale(1,-1); /* switch to lower-left as origin */
/* if onload happens after here, it uses new coordinate system! */
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(290, 30);
ctx.stroke();
当然有一些明智的方法来处理这个问题(并且在 onload 函数中做所有事情似乎是不明智的)。
========================== 下面编辑 ================= =======
这里是我的代码的一个更改,使用一个承诺来尽可能简单地说明这个想法。
var img = new Image();
var promise = new Promise( // make a promise
function(resolve, reject) {
img.onload = function() {
ctx.drawImage(img, 300, 100);
resolve(); // keep the promise -- lets the "then" proceed
};
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
}
);
// .then() waits for the promise to be resolved (see also .catch for rejection)
promise.then( function() {
ctx.translate(0,400); ctx.scale(1,-1);
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(290, 30);
ctx.stroke();
});
Pre-load 使用 canvas 之前的图像。将您所有的图像 url 放入一个数组中,遍历该数组创建新图像,当它们全部加载后调用将开始您的 canvas 工作的函数。
以下代码段使用本机 JS Promises,但如果支持没有本机 Promise 的旧浏览器,您可以以类似的方式使用 Q 或 jQuery 库
var images = ['imageurl.jpg','imageurl2.jpg','imageurl3.jpg'];
var loadedImages = {};
var promiseArray = images.map(function(imgurl){
var prom = new Promise(function(resolve,reject){
var img = new Image();
img.onload = function(){
loadedImages[imgurl] = img;
resolve();
};
img.src = imgurl;
});
return prom;
});
Promise.all(promiseArray).then(imagesLoaded);
function imagesLoaded(){
//start canvas work.
//when needing to draw image, access the loaded image in loadedImages
ctx.drawImage(loadedImages['imageurl.jpg'], 300, 100);
}
另一种方法是向图像添加信号量。我为某些应用程序执行此操作,在这些应用程序中,图像加载是一个持续的过程,加载时间可能不一致且容易失败。您不能因为图像丢失或加载时间过长而停止应用程序,因此您只需检查图像是否准备就绪并在准备好时渲染它,或者使用可以在客户端上渲染且不需要的占位符已加载。
我一直在了解 html5 的 canvas。因为图像可能需要一段时间才能加载,似乎合适的技术是使用 onload
等待图像加载后再尝试绘制它。所以:
<canvas id="fig" width="400" height="400"></canvas>
var fig = document.getElementById('fig1');
var ctx = fig.getContext('2d');
var img = new Image();
img.onload = function() { ctx.drawImage(img, 300, 100); };
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
但是,很可能在 onload 函数可以执行 drawImage()
之前执行此后的代码,可能会导致意外行为:
ctx.translate(0,400); ctx.scale(1,-1); /* switch to lower-left as origin */
/* if onload happens after here, it uses new coordinate system! */
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(290, 30);
ctx.stroke();
当然有一些明智的方法来处理这个问题(并且在 onload 函数中做所有事情似乎是不明智的)。
========================== 下面编辑 ================= =======
这里是我的代码的一个更改,使用一个承诺来尽可能简单地说明这个想法。
var img = new Image();
var promise = new Promise( // make a promise
function(resolve, reject) {
img.onload = function() {
ctx.drawImage(img, 300, 100);
resolve(); // keep the promise -- lets the "then" proceed
};
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
}
);
// .then() waits for the promise to be resolved (see also .catch for rejection)
promise.then( function() {
ctx.translate(0,400); ctx.scale(1,-1);
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(290, 30);
ctx.stroke();
});
Pre-load 使用 canvas 之前的图像。将您所有的图像 url 放入一个数组中,遍历该数组创建新图像,当它们全部加载后调用将开始您的 canvas 工作的函数。
以下代码段使用本机 JS Promises,但如果支持没有本机 Promise 的旧浏览器,您可以以类似的方式使用 Q 或 jQuery 库
var images = ['imageurl.jpg','imageurl2.jpg','imageurl3.jpg'];
var loadedImages = {};
var promiseArray = images.map(function(imgurl){
var prom = new Promise(function(resolve,reject){
var img = new Image();
img.onload = function(){
loadedImages[imgurl] = img;
resolve();
};
img.src = imgurl;
});
return prom;
});
Promise.all(promiseArray).then(imagesLoaded);
function imagesLoaded(){
//start canvas work.
//when needing to draw image, access the loaded image in loadedImages
ctx.drawImage(loadedImages['imageurl.jpg'], 300, 100);
}
另一种方法是向图像添加信号量。我为某些应用程序执行此操作,在这些应用程序中,图像加载是一个持续的过程,加载时间可能不一致且容易失败。您不能因为图像丢失或加载时间过长而停止应用程序,因此您只需检查图像是否准备就绪并在准备好时渲染它,或者使用可以在客户端上渲染且不需要的占位符已加载。