如何使选项卡处于非活动状态时,如何使进度条的setInterval有效

时间:2016-07-13 21:25:15

标签: javascript jquery html5

我可以看到这里有几个类似的问题,但不幸的是我找不到我期望的答案。 我是编程新手,并尝试使用Javascript Progress Bar。我有一个计数器倒计时,只要进度条超出宽度但我遇到了问题,当焦点中的选项卡处于非活动状态时,进度条会暂停,从而保持计数器不倒计时。 我有了使用网络工作者http://www.tutorialspoint.com/html5/html5_web_workers.htm的想法,但我无法让它工作。我很感激我到这里的任何形式的帮助。 以下是我的代码。

<!DOCTYPE html>
<html>
    <head>
        <script 
           src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/
           jquery.min.js">
       </script>
    <style>
        #progressContainer {
            position: relative;
            width: 97%;
            height: 25px;
            background-color: #ddd;
            margin-bottom: 15px;
        }
        #progressBar {
            position: absolute;
            width: 0%;
            height: 100%;
            background-color: #A9A9A9;
        }
        #container{
            width: 200px;
            height: 200px;
            border: 1px solid black;
            padding: 10px
        }
    </style>

    <script>
        $(document).ready(function(){
            $("#countDownBtn").click(function(){

                var cdNumber = $("#countDownId").val();
                var id = setInterval(frame, 100);
                var elem = document.getElementById("progressBar");
                var progressBarWidth = 101;

                function frame() {                 

                    if (progressBarWidth === 0) {
                        clearInterval(id);
                        cdNumber--;                     
                        $("#countDownId").val(cdNumber);
                        console.log(cdNumber);

                        if (cdNumber === 0) {
                            clearInterval(id);          
                        }
                        else {
                            elem.style.width = '100%';
                            progressBarWidth = 100;
                            //alert("Hi");
                        }   
                    } 
                    else {
                        progressBarWidth--;
                        elem.style.width = progressBarWidth + '%';
                    }         
                }                    
            });
        });
    </script>

   </head>
   <body>
    <div id="container">
        <div>
            <input type="text" id="countDownId" value="">
            <button id="countDownBtn" class="btn">Click</button>
        </div><br>
        <div id="progressContainer">
        <div id="progressBar"></div>
        </div>
    </div>
  </body>

5 个答案:

答案 0 :(得分:1)

根据某个区间的精确度,你总会遇到这样的问题;甚至是requestAnimationFrame。他们不精确。

更好的方法(不仅仅是在这种情况下,但几乎每次都必须转换一个值)是保存startTime并计算间隔中的传递时间(如@ Nosyara已经建议)。

当处理缩放因子和/或暂停这些东西时,事情会再次变得混乱。这是一个执行此任务的实用程序:

// The basic concept of this "Clock" is a linear equation over Date.now()
// plus the logic to make this equation pausable.
// therefore it's completely independant of **any** interval; it's just math.
function Clock(v){
    var p=true,m=1,b=+v||0,n=Clock.now;
    return Object.assign(Object.create(Clock.prototype),{
        // getter() / setter(value)
        // I don't use real getter and setter, because this syntax 
        // allows/implements method-chaining
        value(v){return arguments.length?(b=(+v||0)-(!p&&m*n()),this):b+(!p&&m*n())},
        speed(v){return arguments.length?(v=+v||0,p||v===m||(b+=n()*(m-v)),m=v,this):m},
        paused(v){return arguments.length?(((v=!!v)===p)||(b+=n()*((p=v)?m:-m)),this):p},
    });
}
Object.assign(Clock.prototype,{
    valueOf(){return this.value()},
    //aliases for setting the paused() state; doesn't matter if you call them repeatedly.
    start(){return this.paused(false)},
    stop(){return this.paused(true)},
});
Clock.now=Date.now; //the function used for timing
//Clock.now=performance&&performance.now?performance.now.bind(performance):Date.now;

现在你的代码:

$(function(){
    function frame(){
        //yes, countDown get's converted to Number
        var value = Math.max(0, countDown);
        var count = Math.ceil(value);
        var progress = value % 1;

        $progressBar.width( progress * 100 + "%" );

        //so that I don't update $countDown.val() on every frame, but only if necessary
        //on the other hand, it wouldn't be that bad.
        if(count !== lastCount) $countDown.val( lastCount = count );

        //either stop the countDown or request the next frame.
        if(value > 0) requestAnimationFrame(frame);
        else countDown.stop();
    }

    //create a Clock and set speed. Clock is paused by default.
    var countDown = Clock().speed( -1 / 10000/*ms*/ );

    var $progressBar = $("#progressBar");
    var $countDown = $("#countDownId");
    var lastCount;

    $("#countDownBtn").click(function(){
        //if !countDown.paused() then there already is a pending `requestAnimationFrame(frame)`
        //from the last call of frame()
        if(countDown.paused()) requestAnimationFrame(frame);

        countDown.value( $countDown.val() ).start();
    });
})

答案 1 :(得分:0)

我在Chrome浏览器的某个项目中遇到了类似的问题。问题的根源是,如果标签未处于活动状态,Chrome每秒最多允许1个计时器事件(setTimeout或setInterval)。如果每秒有多于1个调用 - 它创建队列,则页面行为取决于事件内部的逻辑,并且可能看起来不像预期的那样。解决方案之一是检查页面的可见性并管理间隔Check here

答案 2 :(得分:0)

您可以使用setTimeout并使用递归。它可能看起来像这样:

var stopInterval = false;
frame();

function frame() {
    if(stopInterval) return;

    // Your stuff to run in interval HERE

    setTimeout(frame, 100);
}

旧版浏览器不支持Webworkers。浏览器还指定它们如何单独处理非活动选项卡上的setTimeout和setInterval,因此行为可能不同。 Chrome似乎减慢了递归速度(每秒1次?)。

答案 3 :(得分:0)

正如@ManoDestra在评论中指出的那样,你应该使用requestAnimationFrame而不是setInterval。

我建议的最佳解决方案是利用HTML5 Visibility API检测选项卡在处于非活动状态后何时再次处于活动状态,然后相应地更新进度条。也许您可以在初始化时存储倒计时的时间戳,并且当标签再次变为活动状态时,您会查看新的时间戳并进行比较。

答案 4 :(得分:0)

特别是在您的情况下,您可以使用时钟时间来表示正确的进度。当您不希望信任浏览器关于间隔事件时。 假设您想要10秒倒计时:

var tm = new Date().getTime() + 10000; // 10 sec in milliseconds
setInterval(function(){
    var secondsPassed = (tm - new Date().getTime()) / 1000;
    // update UI 
}, 100); // You can use variable here in different visibility modes