hiukim / mind-ar-js

Web Augmented Reality. Image Tracking, Face Tracking. Tensorflow.js
MIT License
2.24k stars 417 forks source link

TypeError: Cannot read properties of undefined (reading 'stopProcessVideo') #549

Open amcc opened 1 week ago

amcc commented 1 week ago

I'm getting the above error with a fresh install of Next.js and attempting an integration of mind-ar and react. I've taken the exact versions of both mind-ar and threejs from the hiukim / mind-ar-js-react repo. My implementation is exactly this on a fresh NextJS install: https://github.com/hiukim/mind-ar-js-react/blob/master/src/mindar-three-viewer.js

I'm not the only one with this error, there's a discussion on this repo too: https://github.com/hiukim/mind-ar-js-react/issues/13 It hasn't been answered, I can't find anything on this error anywhere else

package.json:

    "mind-ar": "^1.2.1",
    "three": "^0.150.1" 

error: TypeError: Cannot read properties of undefined (reading 'stopProcessVideo')


  33 |     return () => {
  34 |       renderer.setAnimationLoop(null);
> 35 |       mindarThree.stop();
     |                   ^
  36 |     };
  37 |   }, []);
  38 |
amcc commented 1 week ago

I've solved this for myself...

I'll also post this over on the react mind AR repo (https://github.com/hiukim/mind-ar-js-react). This is happening in NextJS because it runs in strict mode by default - which is what all modern React implementations should really do.

The error is happening because React (quite rightly) mounts, unmounts and then remounts to check for issues in strictMode when developing. So mindAR is mounted, then unmounted, then mounted again. I'm not sure exactly why the stop() function on MindARThree isn't available immediately, but it evidently isn't - which might point to another issue.

To fix this i've set a state which checks to see if MindARThree has been started, if it has not been started it will not attempt to stop it.

Key changes:

// use state to check if mind is started
const [mindStarted, setMindStarted] = useState(false);
// set the state MindStarted to true in the useEffect
setMindStarted(true);
return () => {
      renderer.setAnimationLoop(null);
      // only stop mindAR if it has already been started
      mindStarted && mindarThree.stop();
    };

Here's the full solution:

import { MindARThree } from "mind-ar/dist/mindar-image-three.prod.js";
import * as THREE from "three";

export default () => {
  const containerRef = useRef(null);

  // store the mindStarted state
  const [mindStarted, setMindStarted] = useState(false);

  useEffect(() => {
    const mindarThree = new MindARThree({
      container: containerRef.current,
      imageTargetSrc:
        "https://cdn.jsdelivr.net/gh/hiukim/mind-ar-js@1.2.0/examples/image-tracking/assets/card-example/card.mind",
    });
    const { renderer, scene, camera } = mindarThree;
    const anchor = mindarThree.addAnchor(0);
    const geometry = new THREE.PlaneGeometry(1, 0.55);
    const material = new THREE.MeshBasicMaterial({
      color: 0x00ffff,
      transparent: true,
      opacity: 0.5,
    });
    const plane = new THREE.Mesh(geometry, material);
    anchor.group.add(plane);

    mindarThree.start();

    // set the state MindStarted to true
    setMindStarted(true);

    renderer.setAnimationLoop(() => {
      renderer.render(scene, camera);
    });

    return () => {
      renderer.setAnimationLoop(null);
      // only stop mindAR if it has already been started
      mindStarted && mindarThree.stop();
    };
  }, []);

  return (
    <div style={{ width: "100%", height: "100%" }} ref={containerRef}></div>
  );
};
amcc commented 5 days ago

Update on this. Another problem is caused by my workaround. Multiple instances of the canvas and mind-ar UI elements are getting created if the stop() function isn't called.

This needs looking into more deeply