简体   繁体   English

阻止requestAnimationFrame一直运行

[英]Prevent requestAnimationFrame from running all the time

I'd like to know how to call the animate function through requestAnimationFrame only when it's realy needed. 我想知道如何仅在确实需要时通过requestAnimationFrame调用animate函数。 Currently the animate is called all the time what generates an overhead I guess. 目前,我一直都在调用animate ,这会产生开销。

I already tried inside my animate function to compare targetRadius and the inital radius and return false once they are the same. 我已经尝试在animate函数内部比较targetRadius和初始radius ,并在它们相同时返回false。 Unfortunately this doesn't work at all. 不幸的是,这根本不起作用。

Can someone explain me how to solve that? 有人可以解释我该如何解决吗?

jsfiddle jsfiddle

HTML: HTML:

  <canvas id="ddayCanvas" width="288" height="288" data-image="http://www.topdesignmag.com/wp-content/uploads/2011/07/64.png">
    <div>
        <div class="product-image"></div>
        <div class="product-box">...</div>
        <a href="#" class="overlay">...</a>
    </div>    
  </canvas>

JS: JS:

// Options
var maxImageWidth = 250,
    maxImageHeight = 196;

var canvas = $('#ddayCanvas'),
    canvasWidth = canvas.width(),
    canvasHeight = canvas.height(),
    sectorColor = $('.product-box').css('background-color'),
    context = canvas[0].getContext('2d'),
    imageSrc = canvas.data('image'),
    imageObj = new Image(),
    imageWidth, imageHeight,
    mouseover = false;

    imageObj.onload = function() {
        imageWidth = this.width;
        imageHeight = this.height;

        if (imageWidth > maxImageWidth){
            imageHeight = imageHeight - (imageWidth - maxImageWidth);
            imageWidth = maxImageWidth;
        }

        if (imageHeight > maxImageHeight) {
            imageWidth = imageWidth - (imageHeight - maxImageHeight);
            imageHeight = maxImageHeight;
        }

        drawDday(90); 
    };

    imageObj.src = imageSrc;  

function drawDday (radius) {
    context.clearRect(0, 0, canvasWidth, canvasHeight);
    context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight);
    context.fillStyle = sectorColor;
    context.beginPath();
    context.rect(0, 0, canvasWidth, canvasHeight);
    context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true);
    context.closePath();
    context.fill();

    // Check out the console
    console.log('test');
}


var radius = baseRadius = 90,
    targetRadius = 110,
    ease = 50,
    speed = 2;

function animate(){
    if(mouseover){
        radius += ((targetRadius-radius)/ease)*speed;
    } else {
        radius -= ((radius-baseRadius)/ease)*speed;
    }
    if(radius > targetRadius) radius = targetRadius;
    if(radius < baseRadius) radius = baseRadius;

    drawDday(radius);   
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

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

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. 现在,将isRunning设置为false ,循环将中断。 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. 为了触发循环,我们使用一种方法来检查循环是否正在运行,如果没有运行,它将重置isPlaying标志并启动循环。 We do this inside both mouseover and mouseout : 我们在mouseovermouseout都这样做:

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: 该方法只是检查isPlaying ,如果未设置,则将其设置为true并启动循环-这样循环仅启动一次:

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. 希望这可以帮助。

The reason your animate function is being called continuously is because you start off by calling requestAnimationFrame(animate); animate函数被连续调用的原因是因为您通过调用requestAnimationFrame(animate); and then each call to animate unconditionally calls requestAnimationFrame(animate); 然后每次调用animate无条件调用requestAnimationFrame(animate); again. 再次。 The cycle is never going to be broken unless you use cancelAnimationFrame at some point (which you don't), or make sure that animate only requests another frame if it's needed. 除非您在某个时候使用cancelAnimationFrame (不需要),或者确保animate仅在需要时请求另一个帧,否则该循环永远不会中断。

Another issue is the fact that radius will currently never reach either targetRadius nor baseRadius , and therefore neither of the following will ever be true: 另一个问题是radius当前将永远不会达到targetRadiusbaseRadius ,因此以下baseRadius都不是正确的:

if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;

This isn't directly responsible for the continual calls to animate , but since targetRadius and baseRadius are being used to indicate the end-points of your animation then we need to form some sort of sensible conditional with them. 这与持续调用animate并不直接相关,但是由于使用了targetRadiusbaseRadius来指示动画的终点,因此我们需要与它们形成某种合理的条件。

So, you could do something like: http://jsfiddle.net/PLDUq/9/ 因此,您可以执行以下操作: http : //jsfiddle.net/PLDUq/9/

var radius = baseRadius = 50,
    targetRadius = 110,
    ease = 50,
    speed = 12,
    currentAnim;

function animate(){
    if(mouseover){
        radius += ((targetRadius-radius)/ease)*speed;
    } else {
        radius -= ((radius-baseRadius)/ease)*speed;
    }

    drawDday(radius);

    if(Math.round(radius) >= targetRadius) {
        // uses Math.round() to ensure condition can be fulfilled

        radius = targetRadius;
        return; // doesn't call next frame
    }
    if(Math.round(radius) <= baseRadius) {
        radius = baseRadius;
        return; // doesn't call next frame
    }

    requestAnimationFrame(animate);
}

canvas.on('mouseenter mouseleave', function (e) {
    if (currentAnim) {requestAnimationFrame(currentAnim);}
    // cancels current animation if one is playing to
    // prevent several concurrent loops calling animate()

    mouseover = (e.type === 'mouseenter');

    requestAnimationFrame(animate);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM