Missing example application in embeddable 3D Viewer

I wanted to mess around with incorporating the 3d viewer into a larger application, but it looks like the example files (should be at src/example.js) have been yoinked for now?

I took a look at @chloesun’s cool hackathon project, but it looks like in that case, she was just taking speckle objects into her own ThreeJS environment, rather than using Speckle’s Viewer. From inspecting the Speckle Viewer code, it looks like the Viewer is a wrapper around a standard ThreeJS script.

I’m just running into issues with embedding the Viewer into a React component (I’m getting complaints about “this” being undefined --so trying to append a component to the container doesn’t work, but since it’s an error thrown in the built Speckle Viewer package, it’s hard to debug).

Not a react user but here’s a tiny Vue example. Hope it helps.

One thing to note is that the HTMLElement where the Viewer sits must be mounted. Not sure what’s the React hook for it, but there must be one.

<script setup lang="ts">
import { Viewer } from "@speckle/viewer"
import { onMounted, ref } from "vue";

const canvas = ref<HTMLElement>();
const objectUrl = ref("https://speckle.xyz/streams/508c1fa349/objects/42dfcc0b875322e937353201c93d7e5e");

onMounted(async () => {
  const viewer = new Viewer(canvas.value!)
  await viewer.loadObject(objectUrl.value)
})
</script>

<template>
  <p>✨ Tiny Speckle Viewer</p>
  <label for="objectUrl">Object URL: </label>
  <input type="text" id="objectUrl" :value="objectUrl">
  <div ref="canvas" class="canvas"></div>
</template>

<style>
  .canvas {
    width: 800px;
    height: 600px;
    background-color: cornflowerblue;
  }
  #objectUrl {
    width: 500px;
  }
</style>

5 Likes

Hey @m-clare!

The viewer should be able to be injected into any frontend framework, in the same way you would use it in VanillaJS.

Only differences would be “framework-specific” setup that may be difficult for us to pin-point on each case.

@vwb thx for the super compact example! its pretty neat!

Not a react expert myself either, but in Vue, as far as I remember, you’d need to pass in the libraries to the transpileDependencies list in the vue.config.js file (the Vue configuration file for building, which is done via Webpack afaik)

If that was not included, the viewer will always fail to load. Maybe there’s a similar option in React to look into?

Here’s an example of what it looks like on the revit dash demo app:

And you can also find a slightly more complex viewer (vue based) in there too

This is quite similar to the viewer implementation we used in our frontend.

However, the Viewer has recently undergone some changes, and these examples are yet to be updated to the latest version, so some things may have changed that I’m unaware of.

Alex (our 3D Viewer team-member) is out on vacation so I’ll ping @dimitrie and @fabians to see if they can fill in the gaps here!

2 Likes

We haven’t rolled out the latest viewer refactor, that’s still under dev - and the integration with the frontend won’t change to be honest.

One thing to keep in mind are potential gotchas around an SPA’s component lifecycles and threejs (re)initialisation; you don’t want to create a new viewer instance every time you navigate back and forth to the page with the viewer.

Here’s how we deal with it in our frontend:

2 Likes

Thanks for the quick responses @dimitrie @AlanRynne @vwb!

@AlanRynne I’m pretty sure the viewer library is getting loaded; it’s more of an issue with what @vwb mentioned about making sure the HTML element is mounted. I’m refactoring as a React class based component to check on this (@dimitrie using a class based component should make it easier to be explicit on the component setup and tear down.)

This looks like a minimal example in React for properly mounting a threejs component

1 Like

In general when using frameworks, it’s a good idea to wrap anything complicated in it’s own “component” (or whatever it’s called in your framework of choice :slight_smile: ).

This way you have total control about how it interacts with your framework, and then you just use it as any other component.

I think the world of Speckle + React is rather unexplored (as you may have discovered, we’re mostly Vue fans here :sweat_smile: ) so do let us know what you make of it :speckle:

Turns out my initial issue had something to do with the Vite react-ts template, so I ended up just doing it in plain ReactJS.

Here’s an example of a React component using hooks:


import React, { useEffect, useRef } from "react";
import { Viewer } from "@speckle/viewer";

const SpeckleSceneWithHooks = () => {
  const mount = useRef(null);
  useEffect(() => {
    async function getModel(viewer, speckleURL) {
      console.log(`Loading ${speckleURL}`)
      await viewer.loadObject(speckleURL)
    }

    const v = new Viewer(mount.current);
    const url = 'https://speckle.xyz/streams/508c1fa349/objects/42dfcc0b875322e937353201c93d7e5e'
    getModel(v, url);

    return () => {
      while (mount.current.hasChildNodes()) {
        mount.current.removeChild(mount.current.firstChild)
      }
    }
  }, []);

  return <div id="vis" style={{ width: 400, height: 400 }} ref={mount} />;
};

export default SpeckleSceneWithHooks;

And here’s the class equivalent:

import React, { Component } from "react";
import { Viewer } from "@speckle/viewer";

class SpeckleSceneWithClass extends Component {
  componentDidMount() {
    async function getModel(viewer, speckleURL) {
      console.log(`Loading ${speckleURL}`)
      await viewer.loadObject(speckleURL)
    }

    const v = new Viewer(this.mount);
    const url = 'https://speckle.xyz/streams/508c1fa349/objects/42dfcc0b875322e937353201c93d7e5e'
    getModel(v, url);
  }

  componentWillUnmount() {
    while (this.mount.hasChildNodes()) {
      this.mount.removeChild(this.mount.firstChild);
    }
  }

  render() {
    return (
      <div
        id="vis"
        style={{ width: 400, height: 400 }}
        ref={(mount) => {
          this.mount = mount;
        }}
      />
    );
  }
}

export default SpeckleSceneWithClass;

I’ll have a look this weekend around playing with applying filters and getting properties!

5 Likes

I started a repo for making a generic React app with a Speckle viewer (in progress, there’s some messiness with setting a threejs canvas size inside a component).

2 Likes