代码之家  ›  专栏  ›  技术社区  ›  TomBombadil

threejs/typesript-viewer对象在渲染一次后被销毁

  •  0
  • TomBombadil  · 技术社区  · 5 年前

    我试图用3ejs和typescript编写一个对象查看器。它嵌入在react服务中。

    当调用初始化查看器对象和场景时,一切正常。但是,在渲染场景一次(一次执行animate函数)之后,viewer对象被破坏,我得到一个错误 this requestAnimationFrame(this.animate) ( cannot read property animate of undefined ). 这是我的代码:

    import * as THREE from 'three'
    
    export default class ObjectViewer{
        scene : THREE.Scene;
        camera : THREE.PerspectiveCamera;
        renderer : THREE.WebGLRenderer;
    
        mesh : THREE.Mesh;
    
        public constructor(public node : any,
            public width : number = 1100,
            public height: number = 600) {
    
                this.init();
            }
    
        protected init() {
            //.. initializing renderer, scene and camera here and adding camera to scene
    
            this.node.appendChild(this.renderer.domElement);       
    
            this.camera.position.z = 5;
    
            this.initTestScene();
        }
    
        private initTestScene() {
            var geometry = new THREE.BoxGeometry( 1, 1, 1 );
            var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
            this.mesh = new THREE.Mesh( geometry, material );
            this.scene.add( this.mesh );
        }
    
        private animate() {
            requestAnimationFrame(this.animate);
            this.mesh.rotation.x += 0.1;
            this.renderer.render( this.scene, this.camera );
        }
    
        public render() {
            this.animate();
        }
    }
    

    if(this) 围绕动画块旋转一次的绿色立方体: rendered cube

    动画中的代码:

    if(this){
        requestAnimationFrame(this.animate);
        this.mesh.rotation.x += 0.1;
        this.renderer.render( this.scene, this.camera );
    }
    

    为了提供更多的上下文:我有一个包装器来管理实际的查看器,并使其对周围的react环境可用:

    type Props = {
        width?: number,
        height?: number,
    };
    
    export default class ObjectViewerWrapper extends React.Component<Props, {}> {
        node : HTMLDivElement | null;
        viewer : ObjectViewer;
    
        constructor(props : Props) {
            super(props);
    
            this.node = null;
        }
    
        componentDidMount() {
            this.viewer = new ObjectViewer(this.node, this.props.width, this.props.height);
            this.forceUpdate();
        }
    
        componentDidUpdate(){
            if(this.viewer) {
                this.viewer.render();
            }
        }
    
        render() {
            return(
                <div style={{"height": "100%", "width": "100%", "position": "relative"}} ref={ inst => { this.node = inst } }/>
            );
        }
    }
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Józef Podlecki    5 年前

    我得到一个未定义的错误

    你必须绑定函数 this.animate = this.animate.bind(this);

    工作实例

    const { useState, useEffect, Component, createRef } = React
    
    class ObjectViewer {
        
      constructor(node, width = 1100, height = 600) {
        this.node = node;
        this.width = width;
        this.height = height;
        this.requestAnimationFrameHandle = null;
        this.animate = this.animate.bind(this);
        this.init();
      }
    
      init() {
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera( 45, this.width / this.height, 1, 1000 )
        this.renderer = new THREE.WebGLRenderer();
        const element = this.node.current;
        element.appendChild(this.renderer.domElement);
        this.renderer.setSize(this.width, this.height);
        this.camera.position.z = 5;
        this.initTestScene();
        this.render();
      }
      
      destroy() {
        if(this.requestAnimationFrameHandle) {
          cancelAnimationFrame(this.requestAnimationFrameHandle)
        }
        
        this.node = null;
        this.camera = null;
        this.scene = null;
        this.mesh = null;
      }
    
      initTestScene() {
        var geometry = new THREE.BoxGeometry( 1, 1, 1 );
        var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
        this.mesh = new THREE.Mesh( geometry, material );
        this.scene.add( this.mesh );
      }
    
      animate() {
        this.requestAnimationFrameHandle = requestAnimationFrame(this.animate);
        this.mesh.rotation.x += 0.1;
        this.renderer.render( this.scene, this.camera );
      }
    
      render() {
        this.animate();
      }
    }
    
    class ObjectViewerWrapper extends Component {
       
      constructor(props) {
        super(props);
    
        this.node = createRef();
      }
    
      componentDidMount() {
          const { width, height } = this.props;
          
          this.viewer = new ObjectViewer(this.node, width, height);
          this.forceUpdate();
      }
      
      componentWillUnmount() {
        this.viewer.destroy();
      }
    
      componentDidUpdate() {
          if(this.viewer) {
              this.viewer.render();
          }
      }
    
      render() {
        const style = {
          "height": "100%",
          "width": "100%",
          "position": "relative"
        }
      
        return(<div style={style} ref={this.node}/>);
      }
    }
    
    const App = () => {
      const [dimension, setDimension] = useState(null);
    
      useEffect(() => {
        setDimension({
          width: window.innerWidth,
          height: window.innerHeight
        })
      }, [])
      
      if(!dimension) {
        return <span>Loading...</span>
      }
      
      const { width, height } = dimension;
    
      return <ObjectViewerWrapper width={width} height={height}/>
    }
    
    ReactDOM.render(
        <App />,
        document.getElementById('root')
      );
    html, body {
      margin: 0;
      height: 100%;
    }
    
    #root {
      height: 100%;
    }
    <script src="https://unpkg.com/react/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.js"></script>
    <div id="root"></div>
        2
  •  0
  •   TomBombadil    5 年前

    发现了问题并找到了解决方法,尽管我不知道为什么之前它没有起作用。我必须在render函数中定义animate函数:

    public render() {
        let viewer = this;
        function animate() {
            requestAnimationFrame(animate);
            // animation stuff
            viewer.renderer.render( viewer.scene, viewer.camera );
        }
        animate();
    }