【问题标题】:Why doesn't Progress Bar dynamically change unlike Text?为什么进度条不像文本那样动态变化?
【发布时间】:2015-07-28 21:27:31
【问题描述】:

我在 setTimeout() 函数之后动态更新了一些元素。 jQuery 函数.text() 似乎在处理时随着数组索引的每次变化而动态更新。但是通过.css().attr() 更改的引导进度条似乎没有动态更新。这是我的页面:http://imdbnator.com/process.php?id=f144caf0843490c0d3674113b03da0c5&redirect=false

您可以看到文本已更改,但进度条仅在整个 setTimeout() 函数完成后完成。另外,如果我设置delay = 1000。有用。但它会因应用程序而减慢。因此,我需要delay = 0。但是为什么进度条没有变化呢?

这是我的sn-p

function launch(a) {
    var inc = 0;
    var maxc = a.length;
    var delay = 0; // delay milliseconds
    var iID = setInterval(function () {    
        var index = inc;
        var movie = a[inc];    
        //start processing function    
        //styling while processing                
        var markerPer = ((index + 1) / rawListNum) * 100; // marker percentage
        $("#procNum").text("(" + (index + 1) + "/" + rawListNum + ")"); //Processing number
        $("#procMovie").text(movie); //Processing Name
        $("div[role='progressbar']").css("width", markerPer + "%").attr("aria-valuenow", markerPer); // progress bar -> DOES NOT WORK    
        if (++inc >= maxc) clearInterval(iID);    
    },
    delay);
}

【问题讨论】:

标签: javascript jquery css twitter-bootstrap settimeout


【解决方案1】:

文本和进度条动态变化,但使用delay === 0 非常快:(http://jsfiddle.net/sergdenisov/mh4o1wxz/1/):

HTML:

<div class="text">Text</div>
<div class="progress">
    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0;">
    </div>
</div>

Javascript:

var delay = 0;
var percent = 0;

var iID = setInterval(function() {
    percent += 10;
    $('.text').text('Text ' + percent + '/100');
    $('.progress-bar').css('width', percent + '%').attr('aria-valuenow', percent);    
    if (percent === 100) {
        clearInterval(iID);
    }
}, delay);

【讨论】:

    【解决方案2】:

    作为解释,很多答案都会指出大多数浏览器在 UI 线程上运行 JavaScript,因此在执行 JavaScript 时它们无法更新界面。相反,浏览器将wait for the Javascript to finish and return control to the UI thread

    虽然记住这一点很重要,但它不会影响您的代码。如果您正在更新进度条,请执行以下操作:

    for (i = 0; i < len; i++) {
        updateProgressBar(i)
    };
    

    那么它肯定会阻止 UI 更新到最后。

    但是您已经在使用setTimeoutsetInterval 做正确的事情,它们在代码中具有异步回调。这意味着 JavaScript 能够暂停足够长的时间以输出任何 UI 消息。正如文本能够动态更新的事实所证明的那样。

    正如您所问的,为什么如果 UI 正在更新,进度条也没有更新?

    答案在于Bootstrap applies the following CSSprogress bar

    .progress-bar {
      -webkit-transition: width .6s ease;
           -o-transition: width .6s ease;
              transition: width .6s ease;
    }
    

    当调用之间的延迟为0ms时,浏览器确实有时间更新UI,但没有有时间完成600ms的CSS过渡。因此,直到最后您才能看到动画。

    作为OhAuth points out,你可以通过这样的CSS移除过渡效果来防止浏览器延迟宽度更新:

    .progress .progress-bar {
      -webkit-transition: none;
           -o-transition: none;
              transition: none;
    }
    

    如果您循环浏览大量项目,增量更新将提供某种动画。如果您想为通用用例保留样式,您可以在代码中关闭和打开转换。从jQuery 1.8 you can update CSS properties without the vendor prefix 开始,您只需致电.css("transition","none")

    当然,根据您为每部电影所做的工作量,JavaScript 可能会在您直观地检测到所有 UI 更改之前完成,在这种情况下,延长延迟不会造成任何影响。如果您想测试运行时间更长的进程,可以使用以下sleep function

    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>

    【讨论】:

    • 感谢您的清晰解释。我想最初给予赏金 ro OAuth。
    • @SaiKrishnaDeep。好点子!很高兴他们能把功劳归功。
    【解决方案3】:

    这可能是重绘问题。由于延迟设置为 0,因此直到最后一次更新才会绘制 CSS 更改。

    强制重绘的最简单方法是读取元素高度:

    $("div[role='progressbar']").css("width", markerPer + "%").attr("aria-valuenow", markerPer);
    $("div[role='progressbar']").height();
    

    【讨论】:

      【解决方案4】:

      如果您确实需要计算的延迟为零,那么我会将计算和进度条值的设置分开,以便您在一个setInterval 中计算值,然后运行一个单独的setInterval每 100 毫秒将条更新为该值。

      这样你的计算是最新的,你的 UI 也有时间更新。

      如果您希望它们都采用相同的方法,那么我认为您需要添加至少 100 毫秒的延迟。

      示例:https://jsfiddle.net/dk7g51g4/3/

      Javascript:

      var _ValueToSet = 0;
      $(document).ready(function () {
      
          // SET VALUE EXTREMELY FAST (your current code)
           setInterval(function () {
              _ValueToSet = Math.round(Math.random() * 100); // get value we are going to set
          }
          , 0); // run as fast as possible
      
          // RUN UI UPDATE AT 100MS (separate thread for ui updates).
          setInterval(function () {
              setRandom()
          }
          , 100); // give ui 100ms to do its thing
      });
      
      function setRandom() {
          var elem = $('.progress-bar'); // what is our target progress bar
      
          // set both style width and aria-valuenow
          elem.css('width', _ValueToSet + '%');
          elem.attr('aria-valuenow', _ValueToSet);
      }
      

      HTML:

      <div class="progress progress-striped active">
          <div class="progress-bar" 
              style="width:0%" 
              role="progressbar" 
              aria-valuenow="0" 
              aria-valuemin="30" 
              aria-valuemax="100">
          </div>
      </div>
      

      【讨论】:

      • 投反对票时,请说明原因。我很想听听一个很好的合乎逻辑的论据,说明为什么这是一个坏主意:)
      【解决方案5】:

      另一种解决方法 (with reference to my previous answer):

      如果您必须保留动画,您可以使用保守的间隔延迟,例如 100 毫秒。用户不应该注意到的延迟。

      【讨论】:

        【解决方案6】:

        这是由于引导程序对进度条状态变化的动画方式。如果超时间隔小于动画时间,它将排队重绘。

        尝试将此添加到进度条的 CSS:

        -webkit-transition: none;
        transition: none;
        

        Check my fiddle

        【讨论】:

        • @SaiKrishnaDeep 抱歉,我链接到了错误的小提琴,请参阅我的编辑。
        • 完美!但是我如何确保动画仍然存在?据我了解你的回答,如果我没记错的话,过渡时间应该比延迟时间短。
        • 你的小提琴对我有用,也不需要添加transition: none;
        • 当我进行更改时,它也可以在我的网站上运行。我会再次交叉检查。同时@OhAuth你能在上面的评论中回答我的疑问吗
        • @SaiKrishnaDeep 我的解决方案是使用答案中的 CSS 标签删除动画,因为将超时与动画时间相匹配可能会导致问题。
        猜你喜欢
        • 2014-02-01
        • 2010-11-06
        • 2019-06-12
        • 2012-01-25
        • 2015-07-07
        • 2011-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多