Javascript 中的 img onload-event 时没有页面呈现
No page rendering while img onload-event in Javascript
我有一个 vanilla Javascript,它将 img 的 src 属性设置为 data-uri。
在分配给 onload
事件的函数中,我尝试在页面上显示一些可能需要一段时间的准备工作的进度(生成了多个 canvas 元素并设置了一些设置)。
这是应该呈现进度条的函数:
let progress = document.querySelector('.progress');
function showProgress(message, percent) {
console.log(message + ' (' + (percent * 100).toFixed(0) + '%)');
progress.querySelector('.progress--message').innerHTML = message;
progress.querySelector('.progress--bar-container--bar').style.width = (percent * 100).toFixed(0) + '%';
}
代码是运行这样的:
img.onload = function() {
showProgress('Preparing image...' .1);
// Some preparations...
showProgress('Preparing tools...', .2);
// etc...
showProgress('Done...', 1);
}
控制台日志记录按预期工作,但 DOM 元素的呈现停止直到函数结束,并在一切就绪后显示“完成”状态。
onload
事件处理中是否存在渲染阻塞,是否可以规避?或者我错过了什么?代码本身对结果没有任何影响。即使函数的第一行也有这种奇怪的行为。
除非你使用后台 Web Workers 按摩(更复杂,特别是在你的情况下),你的进度条 呈现 只能在完成后发生return 来自您的脚本;因此,在每次 showProgress
调用之后。现在,如何 return,渲染然后重新启动剩余的?好吧,这就是 0 毫秒的 setTimeout
派上用场的地方。有了它,以下应该起作用:
img.onload = function() {
showProgress('Preparing image...' .1);
setTimeout(function() {
// Some preparations...
showProgress('Preparing tools...', .2);
setTimeout(function() {
// etc...
showProgress('Done...', 1);
}, 0);
}, 0);
}
另一种嵌套代码的方法是递归调用——但每次调用都将在以下步骤开始之前完成——在一系列步骤中,如下所示:
steps = [
function() { // first step (img loading just completed so, show Preparing it right away)
showProgress('Preparing image...', .1);
},
function() { // second step
// Some preparations...
showProgress('Preparing tools...', .2);
}, // other steps...
function() { // last step
// Last things to do...
showProgress('Done...', 1);
}
];
// function that processes "steps" - array of at least 1 function - until empty
function displayAfterEachStep() {
steps.shift()(); // Pull out the first step and run it
if(steps.length > 0) // if there's still steps
setTimeout(displayAfterEachStep, 0); // Recall the process after page rendering
}
img.onload = displayAfterEachStep;
在 here 及周围可以找到许多其他解决 HTML 中缺少渲染事件的好主意。
我在完成其他一些项目后回到了这个问题,并使用 Promises
解决了我的问题,如下所示:
showProgress("Loading image...", 0.0) // Reset progress bar
const img = document.createElement("img") // Get image element
const promise = new Promise((resolve, reject) => { // Instantiate a Promise
img.onload = resolve // Set onload callback to resolve (this triggers the chain)
img.onerror = reject
img.src = "/path/to/image.jpg" // Set src attribute to start loading image
})
.then(() => { // When promise is resolved: Start step 2
showProgress("Preparing image...", 0.3)
// ...
})
.then(() => { // When promise is resolved: Start step 3
showProgress("Preparing tools...", 0.6)
// ...
})
.then(() => { // When promise is resolved: Finish up
showProgress("Ready...", 1)
// ...
})
使用 Promises
,您可以将异步任务按顺序排列,同时仍保持可读性。
进一步阅读:Promises on MDN
我有一个 vanilla Javascript,它将 img 的 src 属性设置为 data-uri。
在分配给 onload
事件的函数中,我尝试在页面上显示一些可能需要一段时间的准备工作的进度(生成了多个 canvas 元素并设置了一些设置)。
这是应该呈现进度条的函数:
let progress = document.querySelector('.progress');
function showProgress(message, percent) {
console.log(message + ' (' + (percent * 100).toFixed(0) + '%)');
progress.querySelector('.progress--message').innerHTML = message;
progress.querySelector('.progress--bar-container--bar').style.width = (percent * 100).toFixed(0) + '%';
}
代码是运行这样的:
img.onload = function() {
showProgress('Preparing image...' .1);
// Some preparations...
showProgress('Preparing tools...', .2);
// etc...
showProgress('Done...', 1);
}
控制台日志记录按预期工作,但 DOM 元素的呈现停止直到函数结束,并在一切就绪后显示“完成”状态。
onload
事件处理中是否存在渲染阻塞,是否可以规避?或者我错过了什么?代码本身对结果没有任何影响。即使函数的第一行也有这种奇怪的行为。
除非你使用后台 Web Workers 按摩(更复杂,特别是在你的情况下),你的进度条 呈现 只能在完成后发生return 来自您的脚本;因此,在每次 showProgress
调用之后。现在,如何 return,渲染然后重新启动剩余的?好吧,这就是 0 毫秒的 setTimeout
派上用场的地方。有了它,以下应该起作用:
img.onload = function() {
showProgress('Preparing image...' .1);
setTimeout(function() {
// Some preparations...
showProgress('Preparing tools...', .2);
setTimeout(function() {
// etc...
showProgress('Done...', 1);
}, 0);
}, 0);
}
另一种嵌套代码的方法是递归调用——但每次调用都将在以下步骤开始之前完成——在一系列步骤中,如下所示:
steps = [
function() { // first step (img loading just completed so, show Preparing it right away)
showProgress('Preparing image...', .1);
},
function() { // second step
// Some preparations...
showProgress('Preparing tools...', .2);
}, // other steps...
function() { // last step
// Last things to do...
showProgress('Done...', 1);
}
];
// function that processes "steps" - array of at least 1 function - until empty
function displayAfterEachStep() {
steps.shift()(); // Pull out the first step and run it
if(steps.length > 0) // if there's still steps
setTimeout(displayAfterEachStep, 0); // Recall the process after page rendering
}
img.onload = displayAfterEachStep;
在 here 及周围可以找到许多其他解决 HTML 中缺少渲染事件的好主意。
我在完成其他一些项目后回到了这个问题,并使用 Promises
解决了我的问题,如下所示:
showProgress("Loading image...", 0.0) // Reset progress bar
const img = document.createElement("img") // Get image element
const promise = new Promise((resolve, reject) => { // Instantiate a Promise
img.onload = resolve // Set onload callback to resolve (this triggers the chain)
img.onerror = reject
img.src = "/path/to/image.jpg" // Set src attribute to start loading image
})
.then(() => { // When promise is resolved: Start step 2
showProgress("Preparing image...", 0.3)
// ...
})
.then(() => { // When promise is resolved: Start step 3
showProgress("Preparing tools...", 0.6)
// ...
})
.then(() => { // When promise is resolved: Finish up
showProgress("Ready...", 1)
// ...
})
使用 Promises
,您可以将异步任务按顺序排列,同时仍保持可读性。
进一步阅读:Promises on MDN