【问题标题】:How to create closure (protect globals) in an animation loop?如何在动画循环中创建闭包(保护全局变量)?
【发布时间】:2014-04-18 21:47:46
【问题描述】:

我正在使用 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
}

【问题讨论】:

  • 如果你想避免全局变量,只需将整个脚本包装在一个模块 IEFE 中。 requestAnimationFrame(animate) 不会传递任何参数。

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


【解决方案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
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多