【问题标题】:How to use requestAnimationFrame?如何使用 requestAnimationFrame?
【发布时间】:2011-08-02 02:41:47
【问题描述】:

我是动画新手,但我最近使用setTimeout 创建了一个动画。 FPS 太低,所以我找到了使用requestAnimationFrame 的解决方案,在此link 中进行了描述。

到目前为止,我的代码是:

//shim layer with setTimeout fallback
    window.requestAnimFrame = (function(){
        return  
            window.requestAnimationFrame       || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function(/* function */ callback){
                window.setTimeout(callback, 1000 / 60);
            };
    })();
    (function animloop(){
        //Get metrics
        var leftCurveEndX = finalLeft - initialLeft;
        var leftCurveEndY = finalTop + finalHeight - initialTop;
        var rightCurveEndX = finalLeft + finalWidth - initialLeft - initialWidth;
        var rightCurveEndY = leftCurveEndY;

        chopElement(0, 0, 0, 0, leftCurveEndX, leftCurveEndY, rightCurveEndX, rightCurveEndY);//Creates a new frame 
        requestAnimFrame(animloop);
    })();

这会在第一帧停止。我在chopElement函数中有一个回调函数requestAnimFrame(animloop);

另外,是否有更详尽的使用此 API 的指南?

【问题讨论】:

  • 作为一般规则,在 JS 中使用 return 时要非常小心,如果在 return 之后删除换行符,这将起作用。

标签: javascript animation requestanimationframe


【解决方案1】:

警告!这个问题不是关于 shim 的最佳方法 requestAnimFrame。如果您正在寻找它,请转到此页面上的任何其他答案。


您被自动插入分号欺骗了。试试这个:

window.requestAnimFrame = function(){
    return (
        window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     || 
        function(/* function */ callback){
            window.setTimeout(callback, 1000 / 60);
        }
    );
}();

javascript 会自动在您的 return 语句后面添加一个分号。它这样做是因为它后面跟着一个换行符,并且下一行是一个有效的表达式。事实上它被翻译成:

return;
window.requestAnimationFrame       || 
window.webkitRequestAnimationFrame || 
window.mozRequestAnimationFrame    || 
window.oRequestAnimationFrame      || 
window.msRequestAnimationFrame     || 
function(/* function */ callback){
    window.setTimeout(callback, 1000 / 60);
};

此代码返回undefined,并且从不执​​行返回语句后面的代码。所以window.requestAnimFrameundefined。当您在animloop 中调用它时,javascript 会产生错误并停止执行。您可以通过将表达式括在括号中来解决问题。

我可以推荐 Chrome 开发者工具或 firebug 来检查 javascript 的执行。使用这些工具,您会看到错误。您应该按如下方式调试它(我假设是 Chrome):

  1. 执行代码(它会产生意想不到的结果)
  2. 打开开发者工具(右键 -> 检查元素) 你会在右边的状态栏中看到一个红色的x(这意味着执行中有错误)
  3. 打开控制台标签
  4. 你会看到
    Uncaught TypeError: Property 'requestAnimFrame' of object [object DOMWindow] is not a function
  5. 在控制台输入:window.requestAnimFrame 并回车,你会看到它是undefined。现在您知道该问题实际上与requestAnimationFrame 无关,您应该专注于代码的第一部分。
  6. 现在需要将代码范围缩小到返回某些内容的程度。这是最困难的部分,如果此时您仍然找不到,您可能需要向互联网寻求更多帮助。

另外,请关注this video 了解编写 javascript 的一些好的做法,他还提到了邪恶的自动分号插入。

【讨论】:

  • 在不支持requestAnimationFrame的浏览器中将此方法与scrollresize等事件一起使用将一直触发回调,而不是像预期的那样,因为setTimeout将是不断触发,创建新的计时器并一个接一个地运行它们。节流法更适合补偿。另外,你不需要在每个全局之前写window...这是多余的。
  • 在全局变量之前编写“窗口”可以让其他开发人员清楚地阅读代码,这些代码可能是内置的,而不是以某种方式在本地范围内提供的函数。考虑到现代 js 应用程序中使用 bower/webpack/etc 的范围有多复杂。这些天来,这是合理的。不会伤害任何东西。
【解决方案2】:
 /*
  Provides requestAnimationFrame in a cross browser way.
  http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 */

if (!window.requestAnimationFrame) {

    window.requestAnimationFrame = (function() {

        return window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame || // comment out if FF4 is slow (it caps framerate at ~30fps: https://bugzilla.mozilla.org/show_bug.cgi?id=630127)
        window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {

                window.setTimeout(callback, 1000 / 60);

        };

    })();

}

animate();

function animate() {
    requestAnimationFrame(animate);
    draw();
}

function draw() {
    // Put your code here
}

看看下面的 jsfiddle 例子;它清楚地说明了我的意思;

http://jsfiddle.net/XQpzU/4358/light/

希望这会有所帮助!

【讨论】:

  • 您在 animate() 中的调用使用了错误的函数名。 Paul 在他的示例中确实使用了 requestAnimFrame,但是您正在填充 requestAnimationFrame (顺便说一句,我更喜欢它)。易于修复。
  • 感谢您的评论,微!我已经编辑过了。我最喜欢使用像你喜欢的长命名函数。我认为这个习惯来自于使用 obj-c 语言:)
【解决方案3】:

“智能节流,因此事件被触发的次数不会超过屏幕重新绘制更改的次数:

var requestFrame = window.requestAnimationFrame ||
                   window.webkitRequestAnimationFrame ||
                   // polyfill - throttle fall-back for unsupported browsers
                   (function() {
                       var throttle = false,
                           FPS = 1000 / 60; // 60fps (in ms)
       
                       return function(CB) {
                         if( throttle ) return;
                         throttle = true;
                         setTimeout(function(){ throttle = false }, FPS);
                         CB(); // do your thing
                       }
                    })();

/////////////////////////////
// use case:

function doSomething() {
  console.log('fired');
}

window.onscroll = function() {
  requestFrame(doSomething);
};
html, body{ height:300%; }
body::before{ content:'scroll here'; position:fixed; font:2em Arial; }

【讨论】:

  • 我不知道,但 FPS 可能应该是 1000/60 为 60fps
  • Sweet,嘿,vsync 你知道吗?或者关于使用 requestAnimationFrame 来缓和滚动页面?
  • @Ampersanda - 以什么方式放松?当用户使用鼠标滚轮手动滚动?
  • 是否可以不间断地运行requestAnimationFrame(即无限期地以60 fps)?一直运行它不是很昂贵吗,即使它只有 60fps?
  • @oldboy - 非常好,而且绝对很贵。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 2018-07-26
  • 2013-01-06
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多