我有一个JavaScript方法,迭代大约200个输入,插入值,并触发onchange事件。在Chrome中大约需要2000毫秒,在IE中大约需要10000毫秒。
我想制作一个可以显示进度的进度条。
现在,问题是浏览器不会重新绘制进度条,直到流程结束时,它会立即达到100%。
在这里研究之后,我发现了三种方法并尝试了所有这些方法,但它们都不起作用:
这是我的代码:
<div class="cg-progress-bar">
<div class="cg-progress-bar-completed">
</div>
<div class="cg-inline-block cg-progress-bar-force-repaint">
</div>
</div>
JavaScript的:
var progressBarCurrent = 0;
var progressBarTotal = 236;
$.each(sqlData, function(column, value){
//doing some work here to update values in inputs
//update progress bar
progressBarCurrent++;
if (progressBarCurrent % 22 === 0) { //don't call too often
var percentageComplete = Math.floor( (progressBarCurrent/progressBarTotal)*100 ) + "%";
var bar = $(".cg-progress-bar-completed").width(percentageComplete)[0];
//hack 1
bar.style.display = 'none';
bar.offsetHeight;
bar.style.display = '';
//hack 2
setTimeout(function() { bar.style.display = 'block'}, 0);
//hack 3
$(".cg-progress-bar-completed").animate({ width: percentageComplete }, 100).height();
//hack 4 - insert empty text node
bar.appendChild(document.createTextNode(' '));
$(bar).hide().show(0);
bar.appendChild(document.createTextNode(' '));
//hack 5 - nuclear option
$(window).trigger("resize");
}
}
});
如何强制浏览器重新绘制?
答案 0 :(得分:2)
可能你的进程太重了,正在崩溃执行线程。这意味着虽然它计算浏览器会略微冻结,但在处理计算时可能无法与UI进行其他交互。
为了避免这种情况,HTML5提供了Web worker技术。这是一种在浏览器环境中模拟多任务处理的方法,允许您执行繁重的任务,避免浏览器冻结。
这里有一篇关于这项技术的优秀介绍性文章,我已成功用于创建一个文件上传器,可以上传Gb长度文件,维护一个实时进度条:
https://www.html5rocks.com/en/tutorials/workers/basics/
我希望它有所帮助。
答案 1 :(得分:1)
好的,所以我之前的anwser并没有像我希望的那样工作,所以这是一个有效的例子:
的index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>-</title>
<style type="text/css" media="screen">
div{
margin: 0px;
padding: 0px;
height: 30px;
}
#wrapper{
width: 300px;
border: solid 1px black;
}
#progressBar{
width: 0px;
background-color: black;
color: grey;
}
</style>
</head>
<body>
<div id="wrapper"><div id="progressBar"></div></div>
<script type="text/javascript">
var progressBar = document.getElementById("progressBar")
var workerThread = new Worker('worker.js');
function drawProgress(progress){
var percentageComplete = Math.floor( (progress.current/progress.maximum)*100 ) + "%";
progressBar.style.setProperty("width",percentageComplete)
}
workerThread.onmessage = function(e){
drawProgress(e.data)
}
</script>
</body>
</html>
worker.js
var sqlData = new Array( 236 );
var i = 0;
for ( let entry of sqlData ) {
console.log( 'iteration of each' );
postMessage( {
current: i,
maximum: sqlData.length
} )
for ( var n = 0; n < 200000; n++ ) {
for ( let entry of sqlData ) {
entry = Math.random()
}
}
i++;
}
我认为这可能是最好的选择,因为没有涉及黑客只是这种任务的方式。唯一的问题是在工作者中很难获得JQuery。最好的方法是在主线程中完全检索SQL数据集,然后传输到worker中。
答案 2 :(得分:0)
@ K48补充它应该与IE9 +兼容,所以这里是IE8 +支持的解决方案:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IE8 compatible</title>
<style type="text/css">
div{
margin: 0px;
padding: 0px;
height: 30px;
}
#wrapper{
width: 300px;
border: solid 1px black;
}
#progressBar{
width: 0px;
background-color: black;
color: grey;
}
</style>
</head>
<body>
<div id="wrapper"><div id="progressBar"></div></div>
<script type="text/javascript">
var continueExecution = false;
var progressBar = document.getElementById( "progressBar" );
var i = 0;
var sqlData = new Array( 236 );
var lastDraw = (new Date).valueOf();
function heavyloadFunction() {
var entry = sqlData[ i ]
// do something with the entry
for ( var n = 0; n < 2000; n++ ) {
for ( var h = 0; h < sqlData.length; h++ ) {
var cEntry = sqlData[h]
cEntry = Math.random()
}
}
// finish entry work
if(i < sqlData.length){
i++;
continueExecution = true;
}else{
console.log("finished")
continueExecution = false;
}
}
function drawProgress( progress ) {
var percentageComplete = Math.floor( ( progress.current / progress.maximum ) * 100 ) + "%";
progressBar.style.width = percentageComplete
}
function shouldReDraw(){
var dNow = (new Date).valueOf();
if(dNow - lastDraw > 16){
// around 60fps
lastDraw = dNow;
return true;
}else{
return false;
}
}
function excutionLoop() {
heavyloadFunction();
if ( continueExecution ) {
if(shouldReDraw()){
drawProgress({
current: i,
maximum: sqlData.length
})
window.setTimeout( excutionLoop, 0 )
}else{
excutionLoop()
}
}else{
drawProgress({
current: i,
maximum: sqlData.length
})
}
}
excutionLoop();
</script>
</body>
</html>
这个想法非常简单,你可以在每次循环迭代时中断执行,这样就可以进行重绘。
编辑:做了一点改进,防止绘图成为瓶颈。