ThreeJS animation not running in Typescript Eslint5 Next Application

121 views Asked by At

I have a typesafe typescript application running on next and need to implement an "old" three.js animation I built for a react project. Since typescript is a bit tricky with getting document elements i tried the following but to no surprise i keep failing at my attemts to make it work. Can anyone help? Home and AnimateCube are TSX files but Animate3D is JSX.

Here are the three scripts:

Home.tsx

import { Box, Heading } from '@chakra-ui/react';
import { ThreeSphere} from 'components/elements/Animate3D';

const Home = () => {
  return (
    <>
      <Heading size="md" marginBottom={6} textAlign={'center'}>
        Welcome!
      </Heading>
      <Heading size="sm" marginBottom={3} textAlign={'center'}>
        Merging Centralized and DeCentralized Finance..
      </Heading>
      <Heading size="sm" marginBottom={8} textAlign={'center'}>
        Please connect your wallet to see your Blockchain Interactions
      </Heading>

      <Box display={'flex'} justifyContent={'center'}>
        <ThreeSphere/>
      </Box>
    </>
  );
};

export default Home;

AnimateCube.tsx

import React, { useState } from 'react';
import Animate3D from './Animate3D.jsx';
import { Box } from '@chakra-ui/react';

const AnimateCube = () => {
  const [isLoaded, setIsLoaded] = useState();

  return (
    <Box className="cube" minH="260px" minW="260px" boxSize="100%" objectFit="fill" onLoad={() => setIsLoaded}>
      {isLoaded ? <Animate3D /> : 'Cube should be here!'}
    </Box>
  );
};

export default AnimateCube;

And ThreeSphere.jsx

import React, { Component } from "react";
import * as THREE from "three";
class ThreeSphere extends Component {
  componentDidMount() {
    const width = this.mount.clientWidth;
    const height = this.mount.clientHeight;
    //ADD SCENE
    this.scene = new THREE.Scene();
    //ADD CAMERA
    this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    this.camera.position.z = 4;
    //ADD RENDERER
    this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    this.renderer.setClearColor(0xffffff, 0);
    this.renderer.setSize(width, height);
    this.mount.appendChild(this.renderer.domElement);
    //ADD sphere
    const geometry = new THREE.SphereGeometry(2, 8, 18);
    const material = new THREE.MeshNormalMaterial({
      color: 0x3ef5f5,
      wireframe: true,
    });
    this.sphere = new THREE.Mesh(geometry, material);
    this.scene.add(this.sphere);
    this.start();
  }
  componentWillUnmount() {
    this.stop();
    this.mount.removeChild(this.renderer.domElement);
  }
  start = () => {
    if (!this.frameId) {
      this.frameId = requestAnimationFrame(this.animate);
    }
  };
  stop = () => {
    cancelAnimationFrame(this.frameId);
  };
  animate = () => {
    this.sphere.rotation.x += 0.01;
    this.sphere.rotation.y += 0.01;
    this.renderScene();
    this.frameId = window.requestAnimationFrame(this.animate);
  };
  renderScene = () => {
    this.renderer.render(this.scene, this.camera);
  };
  render() {
    return (
      <div
        style={{ width: "300px", height: "300px" }}
        ref={(mount) => {
          this.mount = mount;
        }}
      />
    );
  }
}
export default ThreeSphere;

I tried making the jsx file typescript typesafe but every time I end up with document not defined even when seting type <HTMLDivElemen. . . The animation should render a spheare only made up of rainbow colored lines.

Tried

// Check if "document" exists before using it
 if (typeof document === 'undefined') {
   return null;
 }

Tried this now but get too many linting errors I don't understand: I now tried making it typescript like this but my skills fall short on all the new Linting rules - HTMLDivElement - this. - width/height - and the request/cancelAnimatioFrame throws error:

tsx file:

    import React, { Component, RefObject } from 'react';
    import * as THREE from 'three';
    
    interface Props {}
    interface State {}
    
    class ThreeSphere extends Component<Props, State> {
      private mount: RefObject<HTMLDivElement> | undefined;
      private scene!: THREE.Scene;
      private camera!: THREE.PerspectiveCamera;
      private renderer!: THREE.WebGLRenderer;
      private sphere!: THREE.Mesh;
      private frameId!: number;
    
      componentDidMount() {
        const width = this.mount.current?.clientWidth;
        const height = this.mount.current?.clientHeight;
        //ADD SCENE
        this.scene = new THREE.Scene();
        //ADD CAMERA
        this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
        this.camera.position.z = 4;
        //ADD RENDERER
        this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        this.renderer.setClearColor(0xffffff, 0);
        this.renderer.setSize(width, height);
        this.mount.current?.appendChild(this.renderer.domElement);
        //ADD SPHERE
        const geometry = new THREE.SphereGeometry(2, 8, 18);
        const material = new THREE.MeshNormalMaterial({
          color: 0x3ef5f5,
          wireframe: true,
        });
        this.sphere = new THREE.Mesh(geometry, material);
        this.scene.add(this.sphere);
        this.start();
      }
      componentWillUnmount() {
        this.stop();
        this.mount.current!.removeChild(this.renderer.domElement);
      }
      start = () => {
        if (!this.frameId) {
          this.frameId = requestAnimationFrame(this.animate);
        }
      };
      stop = () => {
        cancelAnimationFrame(this.frameId);
      };
      animate = () => {
        this.sphere.rotation.x += 0.01;
        this.sphere.rotation.y += 0.01;
        this.renderScene();
        this.frameId = requestAnimationFrame(this.animate);
      };
      renderScene = () => {
        this.renderer.render(this.scene, this.camera);
      };
      render() {
        return (
          <div
            style={{ width: '300px', height: '300px' }}
            ref={(mount) => {
              this.mount = mount;
            }}
          />
        );
      }
    }
    
    export default ThreeSphere;
0

There are 0 answers