1

I am trying to use Google's model viewer to load 3D models. It has a button. But I want that button to be visible after the model is completely loaded. So, I used to use this Vanilla JavaScript code

<script>
   const modelViewer = document.querySelector("model-viewer");
   const button = document.getElementById("my_ar_button");

   modelViewer.addEventListener("load", function() {
   button.style.display = "block";
   });
</script>

Now I am planning to use it in ReactJS and this is what it looks like

const modelViewer = document.querySelector("model-viewer");
const button = document.getElementById("my_ar_button");
    
modelViewer.addEventListener("load", function() {
button.style.display = "block";
});

<model-viewer 
src="https://modelviewer.dev/shared-assets/models/reflective-sphere.gltf"
alt="A 3D model of an astronaut"
ar ar-modes="webxr scene-viewer quick-look"
ar-scale="auto"
quick-look-browsers="safari chrome"
ios-src="https://modelviewer.dev/shared-assets/models/Astronaut.usdz"
loading="eager"
poster="https://modelviewer.dev/assets/poster-astronaut.png"
autoplay
camera-controls
>
  <button id="my_ar_button" class="my_ar_button" slot="ar-button">Show AR</button>
</model-viewer>
                

My CSS

.my_ar_button {
display: none;
}

I have added model-viewer using script tag

<Helmet>
 <script
 type="module"
 src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"
 async
 >
 </script>
</Helmet>

But it is giving me this error

TypeError: Cannot read property 'addEventListener' of null
Richard Matsen
  • 17,100
  • 3
  • 26
  • 59
  • Make use of ```refs``` to access the DOM element. check useRef. – Medi Apr 15 '21 at 11:32
  • Welcome to SO. There are a few details missing from your question that make it difficult to answer. Please can you show us how you import `model-viewer` into your project. Is it by script tag or via npm? You have provided some code, but no context. Where does that code run? In a component? If so, please show the code for it. – spender Apr 15 '21 at 13:08
  • @spender I have updated my question. Thanks for the suggestion. – Lily Nicole Talmers Apr 15 '21 at 13:47

1 Answers1

1

You need to control <model-viewer> element within a React wrapper. See react-model-viewer, this is is their wrapper

const useModelLoader = (type, src): ModelData => {
  const loader = useMemo(() => getLoader(type), [type]);

  const [model, setModel] = useState(undefined);
  const [modelCenter, setModelCenter] = useState<THREE.Vector3>(undefined);
  const [error, setError] = useState(undefined);
  const [progress, setProgress] = useState(0);

  useEffect(() => {
    loader.load(
      // resource URL
      src,
      // called when the resource is loaded
      model => setModel(model),
      // called while loading is progressing
      ({ loaded, total }) => setProgress((loaded / total) * 100),
      // called when loading has errors
      error => setError(error)
    );
  }, [loader, src]);

  // get the center of the model
  useEffect(() => {
    if (!model) {
      return;
    }

    const box = new THREE.Box3();

    box.setFromObject(model.scene);

    const center = new THREE.Vector3();

    box.getCenter(center);

    setModelCenter(center);
  }, [model]);

  return { model, modelCenter, progress, error };
};

The 2nd useEffect() depends on model, so you can do the same with the button. This would be the React equivalent of modelViewer.addEventListener("load", function() {

const [showButton, setShowButton] = useState(false);

useEffect(() => {
  if (model) {                // when model has a value, the loader has completed
    setShowButton(true);      // now show the button
  }
}, [model]);

const Button = showButton ? <button...> : null;  // use this to wrap <button>
Richard Matsen
  • 17,100
  • 3
  • 26
  • 59
  • Thanks for the help. But it looks like `react-model-viewer` is not being maintained anymore. And `model-viewer` already has a event [Load](https://modelviewer.dev/docs/#entrydocs-loading-events-load) – Lily Nicole Talmers Apr 16 '21 at 06:52
  • It doesn't really matter that it's not maintained, the code I posted shows you how to integrate the non-react `model-viewer` component into the react way of working, including the load event (where is says `// called when the resource is loaded`). Do you understand React components, if so the above should be enough to get you going. – Richard Matsen Apr 16 '21 at 07:42