【问题标题】:javascript callback animationsjavascript回调动画
【发布时间】:2012-11-20 07:09:02
【问题描述】:

我使用three.js 编写了一个用于3D 渲染的小动画函数(下面是完整的测试应用程序):

每次我在屏幕上单击时,立方体都会根据函数调用中描述的动画进行旋转。但我期待如果立方体已经在一个动画下制作动画,添加另一个动画会导致它闪烁,因为相同的属性正在通过多个动画调用进行动画处理。但这不是最后一个动画停止并且新动画接管的情况,即使我最后的回调函数显示旧动画函数仍在运行!那么为什么发送多次点击时立方体不闪烁呢?

<!DOCTYPE html>
<html>
<head>
    <title>Test Page</title>
    <script src="three-mini.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <script>
        var animate = function(obj, prop, targetValue, length, callback) {

            var startTime = Date.now(), inverseLength = 1 / length, startValue = obj[prop];

            if( typeof targetValue === "string") {
                if(targetValue.substring(0, 2) === "+=") {
                    targetValue = obj[prop] + Number(targetValue.substring(2));
                } else {
                    targetValue = obj[prop] - Number(targetValue.substring(2));
                }
            }

            var animateProp = function() {

                var elapsed = (Date.now() - startTime) * inverseLength;

                if(elapsed >= 1) {

                    obj[prop] = targetValue;

                    if( callback instanceof Function) {
                        requestAnimationFrame(callback);
                    }
                    return;

                } else {

                    obj[prop] = (startValue - targetValue) * (Math.cos(elapsed * Math.PI) + 1) * 0.5 + targetValue;

                    requestAnimationFrame(animateProp);

                }

            };
            requestAnimationFrame(animateProp);

        };
        var camera, scene, renderer;
        var geometry, material, mesh;

        init();

        function init() {
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.z = 1000;
            scene = new THREE.Scene();
            geometry = new THREE.CubeGeometry(200, 200, 200);
            material = new THREE.MeshBasicMaterial({
                color : 0xff0000,
                wireframe : true
            });
            mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);
            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);

            document.body.appendChild(renderer.domElement);
            renderer.domElement.addEventListener('click', function() {
                animate(mesh.rotation, "x", "-=" + Math.PI * 2 * 10, 5000, function() {
                    alert("CALLED BACK!")
                });
                animate(mesh.rotation, "y", "-=" + Math.PI * 2 * 10, 15000, function() {
                });
            });
            window.addEventListener('load', render);
            window.addEventListener('resize', function() {
                renderer.setSize(window.innerWidth, window.innerHeight);
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
            });
        }

        function render() {

            // note: three.js includes requestAnimationFrame shim
            requestAnimationFrame(render);

            renderer.render(scene, camera);

        }
    </script>
</body>
</html>

更新 1:

为了弄清楚发生了什么,我添加了更多的网格并以不同的方式为它们设置动画,代码如下,它为每个后续单击设置了每个网格的动画,它首先通过旋转为它们设置动画,然后将它们向前移动然后向后移动,然后回到旋转。您可以在不停止旋转动画的情况下为位置设置动画,并且可以在不停止先前动画的情况下同时为另一个网格设置动画,那么当多个动画在同一个网格和相同属性上运行时,为什么动画不闪烁?

<!DOCTYPE html>
<html>
<head>
    <title>Test Page</title>
    <script src="three-mini.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <script>
        var animate = function(obj, prop, targetValue, length, callback) {

            var startTime = Date.now(), inverseLength = 1 / length, startValue = obj[prop];

            if( typeof targetValue === "string") {
                if(targetValue.substring(0, 2) === "+=") {
                    targetValue = obj[prop] + Number(targetValue.substring(2));
                } else {
                    targetValue = obj[prop] - Number(targetValue.substring(2));
                }
            }

            var animateProp = function() {

                var elapsed = (Date.now() - startTime) * inverseLength;

                if(elapsed >= 1) {

                    obj[prop] = targetValue;

                    if( callback instanceof Function) {
                        requestAnimationFrame(callback);
                    }
                    return;

                } else {

                    obj[prop] = (startValue - targetValue) * (Math.cos(elapsed * Math.PI) + 1) * 0.5 + targetValue;

                    requestAnimationFrame(animateProp);

                }

            };
            requestAnimationFrame(animateProp);

        };
        var camera, scene, renderer, geometry, material, mesh1, mesh2, mesh3, mesh4, mesh5, i = 0, j = 0;

        init();

        function init() {
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.z = 1000;
            scene = new THREE.Scene();
            geometry = new THREE.CubeGeometry(200, 200, 200);
            material = new THREE.MeshBasicMaterial({
                color : 0xff0000,
                wireframe : true
            });
            mesh1 = new THREE.Mesh(geometry, material);
            scene.add(mesh1);
            mesh2 = new THREE.Mesh(geometry, material);
            mesh2.position.x = mesh2.position.y = mesh2.position.z = 200;
            scene.add(mesh2);
            mesh3 = new THREE.Mesh(geometry, material);
            mesh3.position.x = mesh3.position.z = 200;
            mesh3.position.y = -200;
            scene.add(mesh3);
            mesh4 = new THREE.Mesh(geometry, material);
            mesh4.position.y = mesh4.position.z = 200;
            mesh4.position.x = -200;
            scene.add(mesh4);
            mesh5 = new THREE.Mesh(geometry, material);
            mesh5.position.y = mesh5.position.x = -200;
            mesh5.position.z = 200;
            scene.add(mesh5);
            renderer = new THREE.CanvasRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);

            document.body.appendChild(renderer.domElement);
            renderer.domElement.addEventListener('click', function() {
                var mesh;
                if(i === 5) {
                    i = 0;
                    j++;
                    if(j === 3) {
                        j = 0;
                    }
                }
                i++;

                var mesh = window['mesh' + i];
                if(j === 1) {
                    animate(mesh.position, "z", "+=" + 500, 2000, function() {
                        //alert("CALLED BACK!")
                    });
                    return;
                }
                if(j === 2) {
                    animate(mesh.position, "z", "-=" + 500, 3000, function() {
                        //alert("CALLED BACK!")
                    }); retunr;
                }
                animate(mesh.rotation, "x", "-=" + Math.PI * 2 * 5, 5000, function() {
                    //alert("CALLED BACK!")
                });
                animate(mesh.rotation, "y", "-=" + Math.PI * 2 * 6, 10000, function() {
                });
            });
            window.addEventListener('load', render);
            window.addEventListener('resize', function() {
                renderer.setSize(window.innerWidth, window.innerHeight);
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
            });
        }

        function render() {

            // note: three.js includes requestAnimationFrame shim
            requestAnimationFrame(render);

            renderer.render(scene, camera);

        }
    </script>
</body>
</html>

对 WestLangley 的评论: 让我试着解释一下,我很抱歉不清楚。假设我单击一次,它会启动从position.x = 0targetValue: '+=200' 的动画。然后在该动画进行到一半时,我再次单击它,这一次在负 x 方向上进行动画处理,它将以position.x = 100 开始(因为它从第一个动画的中途开始)并具有targetValue: '-=200'。现在我希望第一个动画仍在运行,所以它继续从x=100 动画到x=200,第二个动画现在也在从x=100x=-100,所以每个动画函数都被调用了期望看到立方体左右跳跃,直到第一个动画结束,然后第二个动画可以不受阻碍地继续。这是我所期待的,因此我期待它闪烁。但显然多个动画功能同时运行,但只有最新的一个对更新网格属性有任何影响。 :S 至少据我所知。

我对此的主要担心是,我可以从实验中看出“隐藏的”动画调用仍在处理中,因此浪费了宝贵的处理器周期,这也会导致在回调函数中添加动画调用时出现问题。为此,我主要关心的是如何停止这些“隐藏”的动画调用?

【问题讨论】:

  • 想要它闪烁吗? :-) 你的方法对我来说看起来很有效。事实上,我有点喜欢它。
  • 我不希望它闪烁,但我期待它。如果您运行第一个代码 sn-p 并每三秒单击一次,您将看到动画在每次单击时开始,最后您将收到每次单击的警报,这意味着多个动画正在为相同的网格运行相同的属性同一时间为什么不闪烁?
  • 它不会闪烁,因为您正在做一些明智的事情。如果你的更新函数是obj[prop] = 100 * Math.random() + targetValue;,你会看到一些严重的闪烁。

标签: javascript animation callback three.js


【解决方案1】:

我仍然不能完全确定发生了什么,但我所知道的是多个动画同时运行,即使早期的动画没有显示。我对此的主要担忧是它正在耗尽不必要的处理能力。所以我添加了一个后缀符号,现在我将动画函数添加到拥有对象本身的动画属性中,所以如果我正在为 position.x 制作动画,我创建我的动画函数并将其分配给 position.x_animateposition.x_animate_alt 交替。这样我就可以取消上一个仍在运行的动画,这样我就不会浪费处理器周期了。完整的 sn-p 仅用于以下动画功能:

var animate = function(obj, prop, targetValue, length, callback) {

            var suffix = '_animate', altSuffix = '_animate_alt', thisSuffix = suffix, startTime = Date.now(), inverseLength = 1 / length, startValue = obj[prop];

            if( typeof targetValue === "string") {
                if(targetValue.substring(0, 2) === "+=") {
                    targetValue = obj[prop] + Number(targetValue.substring(2));
                } else {
                    targetValue = obj[prop] - Number(targetValue.substring(2));
                }
            }

            if(obj[prop+suffix] instanceof Function){
                obj[prop+suffix].cancelled = true;
                thisSuffix = altSuffix;
            }
            if(obj[prop+altSuffix] instanceof Function){
                obj[prop+altSuffix].cancelled = true;
                thisSuffix = suffix;
            }

            obj[prop+thisSuffix] = function() {

                var elapsed;
                if(obj[prop+thisSuffix].cancelled){
                    delete obj[prop+thisSuffix];
                    return;
                }
                elapsed = (Date.now() - startTime) * inverseLength;

                if(elapsed >= 1) {

                    obj[prop] = targetValue;
                    delete obj[prop+thisSuffix];
                    if( callback instanceof Function) {
                        requestAnimationFrame(callback);
                    }
                    return;

                } else {

                    obj[prop] = (startValue - targetValue) * (Math.cos(elapsed * Math.PI) + 1) * 0.5 + targetValue;

                    requestAnimationFrame(obj[prop+thisSuffix]);

                }

            };

            requestAnimationFrame(obj[prop+thisSuffix]);

        };

【讨论】:

    猜你喜欢
    • 2013-01-17
    • 1970-01-01
    • 2013-03-06
    • 2012-05-20
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    相关资源
    最近更新 更多