2

私は3Dレンダリング用にthree.jsを使用して小さなアニメーション関数を作成しました(以下の完全なテストアプリケーション):

画面をクリックするたびに、関数呼び出しで記述されたアニメーションに基づいて立方体が回転します。しかし、あるアニメーションの下でキューブがすでにアニメーション化されている場合、別のアニメーションを追加すると、同じプロパティが複数のアニメーション呼び出しによってアニメーション化されるため、キューブがちらつくと予想していました。しかし、これは、最後のコールバック関数が古いアニメーション関数がまだ実行中であることを示していても、最後のアニメーションが停止して新しいアニメーションが引き継ぐということではありません。では、複数のクリックが送信されたときにキューブがちらつかないのはなぜですか?

<!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:

何が起こっているのかを知るために、メッシュを追加してすべて異なる方法でアニメーション化します。コードは次のとおりです。後続のクリックごとに各メッシュをアニメーション化し、最初に回転によってアニメーション化し、次に前方に移動し、次に後方に移動し、次に回転。回転時にアニメーションを停止せずに位置をアニメーション化でき、前のアニメーションを停止せずに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 = 0までのアニメーションが開始されtargetValue: '+=200'ます。次に、そのアニメーションの途中でもう一度クリックします。これは、position.x = 100(最初​​のアニメーションの途中で開始されているため)負のx方向にアニメーション化されますtargetValue: '-=200'。これで、最初のアニメーションがまだ実行されているので、からへのアニメーションが継続されx=100x=2002番目のアニメーションもからx=100へのアニメーションが実行されていると思います。x=-100、したがって、各アニメーション関数が呼び出されると、最初のアニメーションが終了するまでキューブが左右にジャンプし、2番目のアニメーションが妨げられることなく続行できることが期待されます。これは私が起こることを期待していたことであり、したがって私はそれがちらつくことを期待していました。しかし、明らかに複数のアニメーション関数が同時に実行されていますが、メッシュのプロパティを更新する効果があるのは最新のアニメーション関数だけです。:S少なくとも私が知る限り。

これについての私の主な懸念は、「隠された」アニメーション呼び出しがまだ処理されていることを実験から知ることができるため、貴重なプロセッササイクルを破棄し、コールバック関数にアニメーション呼び出しを追加する際の問題にもつながることです。この目的のために、私の主な関心事は、これらの「隠された」アニメーション呼び出しをどのように止めることができるかということです。

4

1 に答える 1

0

何が起こっているのかはまだ完全にはわかりませんが、以前のアニメーションが表示されていなくても、複数のアニメーションが同時に実行されていることはわかっています。これについての私の主な懸念は、それが必要のない処理能力を使い果たしているということです。そこで、接尾辞表記を追加し、オブジェクト自体を所有するアニメーション化されたプロパティにアニメーション化関数を追加しました。アニメーション化する場合は、アニメーション関数を作成して、position.xまたはに割り当てます。これは、以前の実行中のアニメーションをキャンセルできるため、プロセッササイクルを無駄にしないためです。以下のアニメーション機能のための完全なスニペット:position.x_animateposition.x_animate_alt

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]);

        };
于 2012-11-19T22:50:23.933 に答える