Angular 7 ngZone бросает неопределенную ошибку - PullRequest
0 голосов
/ 21 февраля 2019

Я пытаюсь визуализировать 3d-объект, используя three.js.Однако, когда я пытаюсь инициализировать цикл анимации (в пределах ngAfterViewInit), я получаю следующую ошибку:

TypeError: Cannot read property 'ngZone' of undefined

Чтобы уменьшить стоимость процессора, я использую ngZone для запуска requestAnimationFrame снаружиугловойДаже когда я удаляю код для ngZone, я все равно получаю следующую ошибку:

TypeError: Cannot read property 'animate' of undefined

Все это происходит после загрузки соответствующих ресурсов.Чтобы избежать путаницы, для ngZone нет переменной области класса, просто ее вызов в качестве параметра в конструкторе.

Код:

export class ProductComponent{
//setup variables
// shirt model and texutre are pulled from firebase storage
constructor(private storage : AngularFireStorage, public ngZone: NgZone) {

        this.modelUrl = this.storage.ref('path/to/model.obj').getDownloadURL();
        this.textureUrl = this.storage.ref('path/to/texture.jpg').getDownloadURL();

        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
        this.scene = new THREE.Scene();
        this.controls = new THREE.OrbitControls(this.camera,this.renderer.domElement);
        this.clock = new THREE.Clock();
        this.manager = new THREE.LoadingManager();
        this.loader = new THREE.OBJLoader(this.manager);
    }

ngAfterViewInit(){
    //setup
    this.loadResources(this.modelValue, this.texture, this.scene, this.renderer, this.container, this.animate);
 }
private loadResources(model, texture, scene, renderer, container, callback){

    this.camera.position.set(0, 0, 50);
    this.camera.lookAt(new THREE.Vector3(0, 0, 0));

    // scene

    scene.fog = new THREE.FogExp2(0xffffff, 0.0003);

    const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
    scene.add(ambientLight);

    const pointLight = new THREE.PointLight(0xffffff, 0.8);
    this.camera.add(pointLight);
    scene.add(this.camera);

    this.loader.load(model, function (object) {
        object.traverse(function (child) {

            if (child instanceof THREE.Mesh) {

                child.material.map = texture;

                // repeat image on model
                child.material.map.wrapS = child.material.map.wrapT = THREE.RepeatWrapping;
                child.material.map.repeat.set(4, 4);

                child.material.needsUpdate = true;

            }

        });

        object.scale.set(1.5, 1, 1.5);
        scene.add(object);
        console.log('PARTS:', object.children);

        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setClearColor(scene.fog.color);
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);

        callback();
    }); //onProgress, onError
}

animate() : void {
        this.ngZone.runOutsideAngular(() => {
            requestAnimationFrame(this.animate);
        });
        this.render();
        this.update();

    }

}

1 Ответ

0 голосов
/ 23 февраля 2019

this.animate вызывается внутри метода loadResources.Здесь вы передаете его в качестве последнего аргумента:

this.loadResources(this.modelValue, this.texture, this.scene, this.renderer, this.container, this.animate);

Проблема в том, что this.animate будет вызываться внутри callback из this.loader.load, а эта callback является обычной функцией, поэтомуthis внутри animate не будет иметь ngZone или animate.Возможное решение - использовать функцию стрелки для callback из this.loader.load (потому что внутри нее будет вызываться this.animate):

private loadResources(model, texture, scene, renderer, container, callback) {

  this.camera.position.set(0, 0, 50);
  this.camera.lookAt(new THREE.Vector3(0, 0, 0));

  // scene
  scene.fog = new THREE.FogExp2(0xffffff, 0.0003);

  const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
  scene.add(ambientLight);

  const pointLight = new THREE.PointLight(0xffffff, 0.8);
  this.camera.add(pointLight);
  scene.add(this.camera);

  // use arrow function for callback of this.loader.load
  this.loader.load(model, (object) => {

    object.traverse(function (child) {

      if (child instanceof THREE.Mesh) {

        child.material.map = texture;

        // repeat image on model
        child.material.map.wrapS = child.material.map.wrapT = THREE.RepeatWrapping;
        child.material.map.repeat.set(4, 4);

        child.material.needsUpdate = true;

      }
    });

    object.scale.set(1.5, 1, 1.5);
    scene.add(object);
    console.log('PARTS:', object.children);

    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setClearColor(scene.fog.color);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);

    // this.animate
    callback();
  }); //onProgress, onError
}

Или, если вы хотите использовать обычную функцию в качестве обратного вызовадля this.loader.load вы можете привязать this к callback:

// assign this to const that,
// so it can be used for binding
const that = this;
this.loader.load(model, function(object) {

  ...

  // this.animate
  const callbackWithThisBinding = callback.bind(that);
  callbackWithThisBinding();
}
...