通过解释,很多答案都会指出这样一个事实:大多数浏览器在 UI 线程上运行 JavaScript,因此当 JavaScript 执行时他们无法更新界面。相反,浏览器将等待 Javascript 完成并将控制权返回给 UI 线程 https://stackoverflow.com/a/5743526/1366033.
尽管记住这一点很重要,但它对您的代码不起作用。如果您要更新进度条,请执行以下操作:
for (i = 0; i < len; i++) {
updateProgressBar(i)
};
那么它肯定会阻止 UI 更新直到最后。
但你已经通过使用做了正确的事情setTimeout https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout or setInterval https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval,其中具有对代码的异步回调。这意味着 JavaScript 能够暂停足够长的时间来输出任何 UI 消息。文本能够动态更新这一事实就证明了这一点。
正如您的问题所问,为什么如果用户界面正在更新,进度条却没有更新?
答案在于这样一个事实:Bootstrap应用以下CSS https://github.com/twbs/bootstrap/blob/master/less/progress-bars.less#L46 to the 进度条 http://getbootstrap.com/components/#progress:
.progress-bar {
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
当调用之间的延迟为0ms时,浏览器does有时间更新UI,但是does not有时间完成 600ms CSS 转换。因此,直到最后您才看到动画。
As OhAuth指出 https://stackoverflow.com/a/30321740/1366033,您可以通过使用CSS删除过渡效果来防止浏览器延迟宽度更新,如下所示:
.progress .progress-bar {
-webkit-transition: none;
-o-transition: none;
transition: none;
}
如果您循环浏览很多项目,增量更新将提供某种动画。如果您想保留通用用例的样式,您可以在代码中关闭和打开转换。作为jQuery 1.8 你可以更新 CSS 属性而无需供应商前缀 http://www.richardfawcett.net/demos/jquery_1.8.0_testing.html,所以你只需要打电话.css("transition","none")
.
当然,根据您为每部电影所做的工作量,JavaScript 可能能够在您可以直观地检测到所有 UI 更改之前完成,在这种情况下,延长延迟不会有什么坏处。如果您想测试运行时间更长的进程,可以使用以下命令睡眠功能 http://www.phpied.com/css-animations-off-the-ui-thread/:
function sleep(sleepyTime) {
var start = +new Date;
while (+new Date - start < sleepyTime){}
}
这是一个您可以玩的演示,其中每个设置都是一个变量,您可以配置该变量以查看其反应如何,但最好的选择是有条件地删除过渡。
堆栈片段中的演示
var delay, numMovies, throttledMovies, sleepTime, removeTransition;
$("#delay").change(function(){ delay = this.value }).change()
$("#movies").change(function(){ numMovies = this.value }).change()
$("#sleep").change(function(){ sleepTime = this.value }).change()
$("#transition").change(function(){ removeTransition = this.checked }).change()
$("#loadMovies").click(loadMovies);
function loadMovies() {
var i = 0;
throttledMovies = Movies.slice(0, numMovies)
if (removeTransition) {
$('#progress-bar-movie').css("transition","none");
}
var interval = setInterval(function () {
loadMovie(i)
if (++i >= numMovies) {
clearInterval(interval);
$('#progress-bar-movie').css("transition","width .6s ease");
}
}, delay);
};
function loadMovie(i) {
var movie = throttledMovies[i];
var percentComplete = ((i + 1) / numMovies) * 100;
sleep(sleepTime);
// update text
$("#procNum").text("(" + (i + 1) + "/" + numMovies + ")");
$("#procMovie").text(movie.Title);
// update progress bar
$('#progress-bar-movie')
.css('width', percentComplete+'%')
.attr('aria-valuenow', percentComplete);
};
function sleep(sleepyTime) {
var start = +new Date;
while (+new Date - start < sleepyTime){}
}
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/js/bootstrap.js"></script>
<script src="http://kylemitofsky.com/libraries/libraries/movies.js"></script>
<!-- params -->
<label for="delay">Delay (ms)</label>
<input type="number" value="0" min=0 max=1000 id="delay"/>
<label for="movies"># Movies </label>
<input type="number" value="250" min=0 max=250 id="movies"/>
<label for="sleep">Sleep time (ms)</label>
<input type="number" value="0" min=0 max=1000 id="sleep"/>
<label for="transition">Remove transition? </label>
<input type="checkbox" checked="true" id="transition"/><br/><br/>
<button class="btn btn-primary btn-lg" id="loadMovies">
Load Movies
</button><br/><br/>
<p>
<b>Current Title</b>: <span id="procMovie"></span><br/>
<b>Progress</b>: <span id="procNum"></span>
</p>
<div class="progress">
<div class="progress-bar" role="progressbar" id="progress-bar-movie"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>