如何在动画循环中创建闭包(保护全局变量)?

时间:2014-03-03 22:21:00

标签: javascript animation three.js global-variables closures

我正在编写和动画循环使用three.js和所有示例(mrdoob,stemkoski)我看到在线使用不受保护的全局变量。我试图将它们包含在init()函数中,然后通过动画循环将它们作为参数传递。但是,渲染器未定义。

我不确定下面我缺少什么。我的主要问题是如何理解建立具有良好闭包的动画循环的最佳实践(保护本来是全局的变量)。谢谢!

// THE MAIN ANIMATION LOOP:

// UPDATE the scene
function update(keyboard, controls, stats, clock) {

    // delta = change in time since last call (in seconds)
    var delta = clock.getDelta(); 

    // functionality provided by THREEx.KeyboardState.js    
    if ( keyboard.pressed("z") ) 
    { 
        // do something
    }

    controls.update();
    stats.update();

};

// RENDER the scene
function render(renderer, scene, camera) {

    renderer.render(scene, camera);

};

// ANIMATE the scene
function animate(scene, camera, renderer, controls, stats, keyboard, clock) {

    requestAnimationFrame(animate);
    render(renderer, scene, camera);        
    update(keyboard, controls, stats, clock);
};


// *********************
// INITIALIZES THE SCENE

function init(images) { // `images` is passed by a callback from loadImages

    // standard global variables, held privately
    var container, scene, camera, renderer, controls, stats;
    var keyboard = new THREEx.KeyboardState();
    var clock = new THREE.Clock();

    ///////////
    // SCENE //
    ///////////
    scene = new THREE.Scene();

    ////////////
    // CAMERA //
    ////////////

    // set the view size in pixels (custom or according to window size)
    var SCREEN_WIDTH = 1920, SCREEN_HEIGHT = 1080;
    // var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;     
    // camera attributes
    var VIEW_ANGLE = 20, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
    // set up camera
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    // add the camera to the scene
    scene.add(camera);
    // the camera defaults to position (0,0,0)
    //  so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
    // (x,y,z)
    camera.position.set(0,150,1000);
    camera.lookAt(scene.position);  

    //////////////
    // RENDERER //
    //////////////

    // create and start the renderer; choose antialias setting.
    if (Detector.webgl)
        renderer = new THREE.WebGLRenderer( {antialias:true} );
    else
        renderer = new THREE.CanvasRenderer(); 

    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

    // attach div element to variable to contain the renderer
    container = document.getElementById( 'ThreeJS' );

    // attach renderer to the container div
    container.appendChild( renderer.domElement );

    ///////////
    // STATS //
    ///////////

    // displays current and past frames per second attained by scene
    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.bottom = '0px';
    stats.domElement.style.zIndex = 100;
    container.appendChild( stats.domElement );

    ///////////
    // LIGHT //
    ///////////

    // create a light
    var light = new THREE.PointLight(0xffffff);
    light.position.set(100,250,0);
    scene.add(light);

    ////////////
    // IMAGES //
    ////////////

    var element1 = THREE.ImageUtils.loadTexture(images.dresser10);
    var element2 = THREE.ImageUtils.loadTexture(images.dresser14);
    var element1Material = new THREE.SpriteMaterial( { map: element1, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.topLeft  } );
    var sprite = new THREE.Sprite(element1Material);
    sprite.position.set( 50, 50, 0 );
    sprite.scale.set( 64, 64, 1.0 ); // imageWidth, imageHeight
    scene.add(sprite);



    animate(container, scene, camera, renderer, controls, stats, keyboard, clock);      
};


// ********************************************************
// CHECKS TO SEE IF THE WINDOW HAS LOADED BEFORE PROCEEDING
// Once the window is loaded, calls the init function

window.addEventListener ("load", eventWindowLoaded, false);
function eventWindowLoaded() {
    loadImages(init); // calls to initialize the scene once the images are loaded
}

1 个答案:

答案 0 :(得分:1)

我按照@Bergi上面的建议重新编写了一个Crockford样式模块中的动画循环,该模块返回一个充满特权方法的对象,可以访问现在受保护的变量。这是适合任何寻找类似模式的人:

// ************************
// THE MAIN ANIMATION LOOP:

var animLoop = (function () {

    // standard global variables, held privately in this module
    var container, scene, camera, renderer, controls, stats;
    var keyboard = new THREEx.KeyboardState();
    var clock = new THREE.Clock();

    // SCENE
    scene = new THREE.Scene();

    // CAMERA
    var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;    
    var VIEW_ANGLE = 20, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    scene.add(camera);
    camera.position.set(0,150,1000);
    camera.lookAt(scene.position);  

    // RENDERER
    if (Detector.webgl) {
        renderer = new THREE.WebGLRenderer( {antialias:true} );
    } else {
        renderer = new THREE.CanvasRenderer();
    } 
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    container = document.getElementById( 'ThreeJS' );
    container.appendChild( renderer.domElement );

    // LIGHT
    var light = new THREE.PointLight(0xffffff);
    light.position.set(100,250,0);
    scene.add(light);

    // IMAGES
    var images;
    var element1, element2, element1Material, sprite;   

    // RETURN:
    // *** returns an object full of functions with priviledged access to the private variables listed above ***
    return { 
        setImages: function (images_) { // sets the value of the images (array) above
            images = images_; 

        },
        createSprites: function () {
            var element1 = THREE.ImageUtils.loadTexture(images.dresser10.src);
            var element1Material = new THREE.SpriteMaterial( { map: element1, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.topLeft  } );
            var sprite = new THREE.Sprite(element1Material);
            sprite.position.set( 50, 50, 0 );
            sprite.scale.set( 64, 64, 1.0 );
            scene.add(sprite);  
        },
        update: function () {
            var delta = clock.getDelta();
            // functionality provided by THREEx.KeyboardState.js    
            if ( keyboard.pressed("z") ) 
            { 
                // do something
            }

        },
        render: function () {
            renderer.render(scene, camera);
        }
    };
}());


// ANIMATE the scene
function animate() {
        requestAnimationFrame( animate );
        animLoop.render();  
        animLoop.update();
};

// INITIALIZES THE SCENE

function init(images) { // `images` is passed by a callback not included here
    animLoop.setImages(images); // places the initial array of images as a private variable in the animLoop object    
    animLoop.createSprites();
    animate();      

};

window.addEventListener ("load", eventWindowLoaded, false);
function eventWindowLoaded() {

    loadImages(init); // calls to initialize the scene once the images are loaded
};