简体   繁体   English

JavaScript:在requestAnimationFrame循环内使用cancelAnimationFrame

[英]JavaScript: using cancelAnimationFrame inside requestAnimationFrame loop

For the sake of learning I am prototyping an animate function for all HTMLElements, inspired by jQuery. 为了学习,我为所有HTMLElements设计了一个动画函数原型,其灵感来自jQuery。 The animation starts up just fine, but I want it to stop after the requestAnimationFrame's time = the duration given in the function. 动画启动正常,但我希望在requestAnimationFrame的time =函数中给定的duration后停止动画。 I am using cancelAnimationFrame inside the animation loop, but it doesn't stop the loop. 我在动画循环内使用cancelAnimationFrame ,但它不会停止循环。

    HTMLElement.prototype.animate = function(properties,duration){

        for(prop in properties){

            var last = 0,
                fps = 60;

            function ani(time){

                requestAnimationFrame(ani);
                if ((time - last) > fps ){                       
                    last = time
                    console.log(time)
                    if(time >= (duration*1000)){
                        window.cancelAnimationFrame(aniLoop)
                    }    
                }
            }
            var aniLoop = requestAnimationFrame(ani)
        }
    }

The function is called like this 该函数这样调用

    c.animate({"property":"value"},1)

At the core of the problem lies that fact that you're only getting the ID of the first animation frame (the var aniLoop = (...) line) and that's what you're trying to cancel - except that each call to requestAnimationFrame has a different ID, thus you'd need to store the return value of the last call and cancel that instead: 问题的核心在于,您仅获得第一个动画帧的ID( var aniLoop = (...)行),而这就是您要取消的事实-除了每次对requestAnimationFrame调用具有不同的ID,因此您需要存储上一次调用的返回值,然后取消该调用:

HTMLElement.prototype.animate = function(properties,duration) {
    "use strict";

    var aniLoop,
        prop,
        last = 0,
        fps = 60;

    for (prop in properties) {

        function ani(time) {

            aniLoop = requestAnimationFrame(ani);
            if ((time - last) > fps) {                       
                last = time;
                console.log(time);
                if (time >= (duration * 1000)) {
                    cancelAnimationFrame(aniLoop);
                }    
            }
        }
        aniLoop = requestAnimationFrame(ani);
    }
};

There are, however, several other problems with your code that need to be tackled as well, otherwise your function will blow up rather thoroughly: 但是,您的代码还有其他一些问题也需要解决,否则您的函数将被彻底炸毁:

:1 Function declaration in a loop :1 循环中的函数声明

I would recommend reading about differences between function declaration and expression to get a better picture, but the problem here is that you're doing function declaration in a loop, which is considered undefined behaviour (some engines will replace the functions, some will not, some will blow up). 我建议阅读一些有关函数声明和表达式之间的差异的信息,以获得更好的画面,但是这里的问题是,您正在循环中进行函数声明,这被认为是未定义的行为(某些引擎将替换函数,有些则不会,一些会炸毁)。 Given that the animations have only single duration given and thus, are probably synchronised, it'd be a better option to iterate over properties to animate inside of a single animation function, like so: 鉴于动画只给出了一个持续时间,因此可能是同步的,所以最好在属性上进行迭代以在单个动画函数中进行动画处理,如下所示:

HTMLElement.prototype.animate = function(properties,duration) {
    "use strict";

    var aniLoop,
        last = 0,
        fps = 60;

    function ani(time) {

        var prop;

        aniLoop = requestAnimationFrame(ani);
        if ((time - last) > fps) {

            last = time;

            for (prop in properties) {
                console.log(prop + ': ' + time);
            }

            if (time >= (duration * 1000)) {
                cancelAnimationFrame(aniLoop);
            }    
        }
    }
    aniLoop = requestAnimationFrame(ani);
}

:2 Animation timestamping :2 动画时间戳记

As it looks currently, your animation function will probably not run more than one frame anyway - if you look at requestAnimationFrame documentation on MDN , you'll notice that the callback given to requestAnimationFrame is given a timestamp, ie value in milliseconds from the beginning of UNIX epoch (1st of January 1970) - therefore the condition of time >= (duration * 1000) will always be true. 从目前的情况看,您的动画功能可能不会运行超过一帧-如果您查看MDN上的requestAnimationFrame文档 ,您会注意到,给requestAnimationFrame的回调具有时间戳记,即从值开始的毫秒数UNIX时代(1970年1月1日)-因此time >= (duration * 1000)将始终为true。 Instead of that, register the starting time when you kick the animation off and compare the timestamp within the callback to it, like so: 取而代之的是,在动画开始时注册开始时间,然后将其与回调之间的时间戳进行比较,如下所示:

HTMLElement.prototype.animate = function(properties,duration) {
    "use strict";

    var aniLoop,
        start,
        last = 0,
        fps = 60;

    function ani(time) {

        var prop,
            progress;

        aniLoop = requestAnimationFrame(ani);
        if ((time - last) > fps) {

            last = time;
            progress = time - start;

            for (prop in properties) {
                console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
            }

            // This is where we get a difference between current and starting time
            if (progress >= (duration * 1000)) {
                cancelAnimationFrame(aniLoop);
            }    
        }
    }
    start = Date.now();
    aniLoop = requestAnimationFrame(ani);
}

:3 Animation throttling :3 动画节流

This one is not as crucial, but still worth a consideration - requestAnimationFrame is intended to be automatically throttled and regulated by the browser, thus you don't need to apply your own conditions on whether animation should run (it won't go over 60FPS anyway, as that's the specification's ceiling). 这不是很关键,但仍然值得考虑requestAnimationFrame旨在由浏览器自动调节和调节,因此您不需要对是否应运行动画应用自己的条件(动画不会超过60FPS)无论如何,因为这是规范的上限)。 Instead, it should simply work on difference of current time from starting time, to make sure your animation still ends up in correct place even if for some reason, there is a lag in animation: 相反,它应该仅处理当前时间与开始时间之间的时差,以确保即使由于某种原因动画存在滞后,动画仍然可以在正确的位置结束:

HTMLElement.prototype.animate = function(properties,duration) {
    "use strict";

    var aniLoop,
        start;

    function ani(time) {

        var prop,
            progress;

        aniLoop = requestAnimationFrame(ani);
        progress = time - start;

        for (prop in properties) {
            console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
        }

        // This is where we get a difference between current and starting time
        if (progress >= (duration * 1000)) {
            cancelAnimationFrame(aniLoop);
        }    
    }
    start = Date.now();
    aniLoop = requestAnimationFrame(ani);
}

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

相关问题 RequestAnimationFrame 中的 forEach 循环 - forEach loop inside RequestAnimationFrame JavaScript-setTimeout和requestAnimationFrame中的“ this” - JavaScript - 'this' inside setTimeout and requestAnimationFrame 在调用cancelAnimationFrame之后,requestAnimationFrame似乎更新了变量 - requestAnimationFrame seems to update variables after cancelAnimationFrame is called 在 requestIdleCallback 中使用 requestAnimationFrame - Using requestAnimationFrame inside requestIdleCallback 为什么cancelAnimationFrame不取消requestAnimationFrame? - Why cancelAnimationFrame doesn't cancel the requestAnimationFrame? 我必须在下一个 requestAnimationFrame 之前取消 AnimationFrame 吗? - Do I have to cancelAnimationFrame before next requestAnimationFrame? 使用requestAnimationFrame()作为setInterval()Javascript - Using requestAnimationFrame() as setInterval() Javascript javascript - 使用 requestAnimationFrame 的简单 30fps 游戏循环? - A simple 30fps game loop in javascript using requestAnimationFrame? 如何在Javascript中调用requestAnimationFrame循环? - How to call in Javascript a requestAnimationFrame loop? Javascript-暂停并继续requestAnimationFrame循环 - Javascript - Pausing and continuing requestAnimationFrame loop
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM