Javascript 中 requestAnimationFrame() 的应用问题

问题简述:本代码较长,建议拷贝到编辑器查看。问题是使用两种js方法完成一个效果,个人认为两种方法原理一样,但是一个效果出来了,一个没有出效果并且报错,想知道报错的原因,望大神指教。

HTML代码:

    <p>用 requestAnimationFrame 替代 seTtimeout 实现了一个动画效果:</p>
    <div>
        <p class="ball red" style="margin-left:0px;"></p>
        <p class="ball green" style="margin-left:0px;"></p>
        <p class="ball blue" style="margin-left:0px;"></p>
    </div>

CSS代码:

        .ball {
            width: 20px;
            height: 20px;
            border-radius: 50%; 
        }
        
        .red {
            background-color: red;
        }
        
        .green {
            background-color: green;
        }
        
        .blue {
            background-color: blue;
        }

JavaScript代码方法一:

        var red = document.querySelector(".red");
        var green = document.querySelector(".green");
        var blue = document.querySelector(".blue");

        ani(red, 100, function() {
            ani(green, 200, function() {
                ani(blue, 150, function() {
                    ani(red, 150);
                    ani(green, 150)
                })
            })
        })

        //代码一
        function ani(node, to, callback) {
            animate();

            function animate() {
                var marginLeft = parseInt(node.style.marginLeft);
                if (marginLeft == to) {
                    callback && callback();
                } else {
                    if (marginLeft < to) {
                        marginLeft++
                    } else if (marginLeft > to) {
                        marginLeft--
                    }
                    node.style.marginLeft = marginLeft + "px";
                    requestAnimationFrame(animate);
                }
            }
        }

效果没问题:图片描述

JavaScript代码方法二:

        var red = document.querySelector(".red");
        var green = document.querySelector(".green");
        var blue = document.querySelector(".blue");

        ani(red, 100, function() {
            ani(green, 200, function() {
                ani(blue, 150, function() {
                    ani(red, 150);
                    ani(green, 150)
                })
            })
        })

        function ani(node, to, callback) {
            var marginLeft = parseInt(node.style.marginLeft); //本行报错,说不识别属性marginLeft
            if (marginLeft == to) {
                callback && callback();
            } else {
                if (marginLeft < to) {
                    marginLeft++
                } else if (marginLeft > to) {
                    marginLeft--
                }
                node.style.marginLeft = marginLeft + "px";
                requestAnimationFrame(ani);
            }
        }

效果报错:
clipboard.png

问题:这两种方案的差别是什么?为什么第二种方案会报错?

阅读 2.5k
3 个回答
requestAnimationFrame(ani);

等价于:

requestAnimationFrame(function(){
    ani()
});

那么问题来了,你 ani 函数里面的各个参数必然是 undefined 啊,所以会报错咯。

另外,你第一种方法,既然使用 requestAnimationFrame 了,本质上是一个递归的逻辑,所以不用手动触发 animate 方法,直接在最后写 requestAnimationFrame(animate) 就可以了。

最后,说点儿题外话,可能你有你自己的考虑,但是如果是做这种平移的特效,建议使用 transform,效果会比 margin 好。

function ani(node, to, callback) {
          
            var marginLeft = parseInt(node.style.marginLeft); //本行报错,说不识别属性marginLeft
            
            if (marginLeft == to) {
                callback && callback();
            } else {
                if (marginLeft < to) {
                    marginLeft++
                } else if (marginLeft > to) {
                    marginLeft--
                }
           
                node.style.marginLeft = marginLeft + "px";
                requestAnimationFrame(()=>ani(node, to, callback));
            }
        }

requestAnimationFrame(ani);表示的是浏览器每刷新一帧的时候就调用ani方法一次,以达到连续调用ani方法的目的(严格来说不“连续”,但是肉眼一般无感知)。
在下面这段代码里你已经调用了ani方法:

        ani(red, 100, function() {
            ani(green, 200, function() {
                ani(blue, 150, function() {
                    ani(red, 150);
                    ani(green, 150)
                })
            })
        })

你要连续调用的并不是ani,而是ani方法体里面的控制移动的部分,这就是为什么你方法一是可以成功运行的。
方法二不可以运行是因为1.你并不需要连续运行ani 2.ani是有参数的 但是你没有传,相当于运行的是ani(),所以会报错。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题