Prevent requestAnimationFrame from running all the time

前端 未结 2 1640
北荒
北荒 2021-01-07 12:20

I\'d like to know how to call the animate function through requestAnimationFrame only when it\'s realy needed. Currently the animate i

2条回答
  •  一向
    一向 (楼主)
    2021-01-07 12:59

    You need to implement a condition so you can break the loop, for example (adopt as needed):

    var isRunning = true;
    
    function loop() {
    
        ... funky stuff here ...
    
        /// test condition before looping
        if (isRunning) requestAnimationFrame(loop);
    }
    

    Now when you set isRunning to false the loop will break. For convenience it's recommended that you have a method to start and stop the loop:

    function startLoop(state) {
    
        if (state && !isRunning) {
            isRunning = true;
            loop();             /// starts loop
    
        } else if (!state && isRunning) {
            isRunning = false;
        }
    }
    

    The condition can be set by anything you need it to be set by, for example on a callback after an animation has finished etc. The important part is that the condition flag is available to both scopes using it (ie. most commonly in the global scope).

    UPDATE:

    More specific in this case is that your condition (radius) will never reach the condition required to eventually stop the loop.

    Here is what you can do to fix this:

    DEMO

    var isPlaying = false;
    
    function animate(){
        /**
         * To make sure you will reach the condition required you need
         * to either make sure you have a fall-out for the steps or the
         * step will become 0 not adding/subtracting anything so your
         * checks below won't trigger. Here we can use a simple max of
         * the step and a static value to make sure the value is always > 0
        */
        if(mouseover){
            radius += Math.max( ((targetRadius-radius)/ease)*speed, 0.5);
        } else {
            radius -= Math.max( ((radius-baseRadius)/ease)*speed,   0.5);
        }
    
        /**
         * Now the checks will trigger properly and we can use the
         * isPlaying flag to stop the loop when targets are reached.
        */
        if(radius >= targetRadius) {
            radius = targetRadius;
            isPlaying = false;              /// stop loop after this
        } else if (radius <= baseRadius) {
            radius = baseRadius;
            isPlaying = false;              /// stop loop after this
        }
        
        drawDday(radius);
    
        /// loop?
        if (isPlaying === true) requestAnimationFrame(animate);
    }
    

    In order to trigger the loop we use a method that will check if the loop is running, if not it will reset the isPlaying flag and start the loop. We do this inside both mouseover and mouseout:

    canvas.on('mouseover', function(e){
        mouseover = true;
        startAnim();
    
    }).on('mouseout', function(){
        mouseover = false;
        startAnim();
    });
    

    The method is simply checking isPlaying and if not set it set it to true and starts the loop - this so that the loop is only started once:

    function startAnim() {
        if (!isPlaying) {
            isPlaying = true;
            requestAnimationFrame(animate);
        }
    }
    

    In the demo I added console logging to show when the loop is running and when targets are hit.

    Hope this helps.

提交回复
热议问题