繁体   English   中英

JavaScript:在requestAnimationFrame循环内使用cancelAnimationFrame

[英]JavaScript: using cancelAnimationFrame inside requestAnimationFrame loop

为了学习,我为所有HTMLElements设计了一个动画函数原型,其灵感来自jQuery。 动画启动正常,但我希望在requestAnimationFrame的time =函数中给定的duration后停止动画。 我在动画循环内使用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)
        }
    }

该函数这样调用

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

问题的核心在于,您仅获得第一个动画帧的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);
    }
};

但是,您的代码还有其他一些问题也需要解决,否则您的函数将被彻底炸毁:

:1 循环中的函数声明

我建议阅读一些有关函数声明和表达式之间的差异的信息,以获得更好的画面,但是这里的问题是,您正在循环中进行函数声明,这被认为是未定义的行为(某些引擎将替换函数,有些则不会,一些会炸毁)。 鉴于动画只给出了一个持续时间,因此可能是同步的,所以最好在属性上进行迭代以在单个动画函数中进行动画处理,如下所示:

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 动画时间戳记

从目前的情况看,您的动画功能可能不会运行超过一帧-如果您查看MDN上的requestAnimationFrame文档 ,您会注意到,给requestAnimationFrame的回调具有时间戳记,即从值开始的毫秒数UNIX时代(1970年1月1日)-因此time >= (duration * 1000)将始终为true。 取而代之的是,在动画开始时注册开始时间,然后将其与回调之间的时间戳进行比较,如下所示:

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 动画节流

这不是很关键,但仍然值得考虑requestAnimationFrame旨在由浏览器自动调节和调节,因此您不需要对是否应运行动画应用自己的条件(动画不会超过60FPS)无论如何,因为这是规范的上限)。 相反,它应该仅处理当前时间与开始时间之间的时差,以确保即使由于某种原因动画存在滞后,动画仍然可以在正确的位置结束:

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.

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