Display a Three.js scene within a Vuetify.js component

Struggling to integrate a basic Three.js scene with the Vuetify v-app component? You're not alone.

By using vue, vue-router, and three-js without Vuetify, I encountered no issues loading a simple 'Hello World' scene. If you're curious, you can find the code for this successful attempt here: https://github.com/TjalleWired/vue-three-test

I've experimented with a route detector to bypass the v-app component when loading /viewer (employing v-if in the components). However, the outcomes were inconsistent, ranging from overlapping UI elements with the 3D viewer to the 3D viewer disappearing altogether with each reload.

My current project setup involves a Navbar.vue component, App.vue, and a Viewer.vue view. Additionally, I've activated vue-router which is functioning as expected.

If you're keen on seeing my latest efforts to make Vuetify play nice with Three.js, check out this minimal project here: https://github.com/TjalleWired/three-vuetify/

Viewer.vue

<template>
  <div id="container"></div>
</template>

<script>
import * as THREE from "three";

export default {
  name: "ThreeTest",
  data() {
    return {
      cube: null,
      renderer: null,
      scene: null,
      camera: null,
    };
  },
  methods: {
    init: function () {
      this.scene = new THREE.Scene();
      this.camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );

      this.renderer = new THREE.WebGLRenderer();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(this.renderer.domElement);

      const geometry = new THREE.BoxGeometry(1, 1, 1);
      const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
      this.cube = new THREE.Mesh(geometry, material);
      this.scene.add(this.cube);

      this.camera.position.z = 5;

    },
    animate: function () {
      requestAnimationFrame(this.animate);

      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;

      this.renderer.render(this.scene, this.camera);
    },
  },
  mounted() {
    this.init();
    this.animate();
  },
};
</script>

Navbar.vue

<template>
  <div>
    <v-app-bar app clipped-left flat dark>
      <v-toolbar-title>
        <span class="first-word font uppercase">stp</span>
        <span class="second-word font uppercase">upload</span>
      </v-toolbar-title>
      <v-spacer></v-spacer>
    </v-app-bar>

    <v-navigation-drawer app clipped flat dark expand-on-hover>
      <v-list>
        <v-list-item class="px-2">
          <v-list-item-avatar>
            <v-img src="https://randomuser.me/api/portraits/men/11.jpg"></v-img>
          </v-list-item-avatar>

          <v-list-item-title>
            <span class="username">{{ username }}</span>
          </v-list-item-title>
        </v-list-item>
        <v-list-item v-for="item in navbarlist" :key="item.route" :to="item.route">
          <v-list-item-icon>
            <v-icon>{{ item.icon }}</v-icon>
          </v-list-item-icon>
          <v-list-item-content>{{ item.text }}</v-list-item-content>
        </v-list-item>
      </v-list>

      <template v-slot:append>
        <v-list>
          <v-list-item @click="logout()">
            <v-list-item-icon>
              <v-icon color="red">mdi-logout</v-icon>
            </v-list-item-icon>
            <v-list-item-content>Logout</v-list-item-content>
          </v-list-item>
        </v-list>
      </template>
    </v-navigation-drawer>
  </div>
</template>

<script>
export default {
  data: () => ({
    drawer: true,
    navbarlist: [
      { icon: "mdi-view-dashboard", text: "Dashboard", route: "/" },
      { icon: "mdi-upload", text: "Upload", route: "/upload" },
      { icon: "mdi-video-3d", text: "Viewer", route: "/viewer" },
    ],
    username: "",
  }),
  created: function () {
    this.username = this.$store.state.userProfile.name;
  },
  methods: {
    logout() {
      this.$store.dispatch("logout", {});
    },
  },
};
</script>

<style>
.font {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.uppercase {
  text-transform: uppercase;
}
.first-word {
  font-weight: 400;
}
.second-word {
  font-weight: 200;
  color: grey;
}
.item-tile-icon {
  color: black;
}
.username {
  color: whitesmoke;
  font-family: "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif;
  font-weight: lighter;
  letter-spacing: 0.001em;
}
</style>

App.vue

<template>
  <div>
    <v-app>
      <Navbar v-if="showNavBar"/>
      <v-main>
        <router-view></router-view>
      </v-main>
    </v-app>
  </div>
</template>

<script>
import Navbar from "./components/Navbar";

export default {
  name: "App",

  components: {
    Navbar,
  },

  data: () => ({
    showNavBar: true,
    app: true
  }),
  mounted() {
    }
};
</script>

Answer №1

I find it puzzling why individuals are so eager to erase my response when I am genuinely attempting to assist the user. If I am unable to make a comment, then I am unable to do so; it is not my decision that I need a certain amount of reputation to leave a comment and am instead required to provide an answer...

Regarding the Issue:

The key is to connect the DOM element not to document.body, but to the specific div within your Vue component.

Therefore, instead of:

document.body.appendChild(this.renderer.domElement);

It is recommended to use:

document.getElementById('container').appendChild(this.renderer.domElement);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The function dataTable is not recognized as a valid command

I am encountering an issue while attempting to utilize the datatables plugin. Whenever I call the function dataTable(), I receive an error. Here is a snippet of my code: @Scripts.Render("~/Scripts/DataTables-1.9.4/media/js/jquery.js") @Scripts.Render("~/S ...

What steps can be taken to resolve the issue of the error message "It is possible that this issue is not related to npm. There may be more detailed logging information provided

While working on a project using Vue CLI, I encountered the following error message: throw er; // Unhandled 'error' event ^ Error: getaddrinfo ENOTFOUND x86_64-apple-darwin13.4.0 at GetAddrInfoReqWrap.onlookup [as oncomplete] (d ...

Encountering error code 2064 without any clear explanation in sight

Hey, I'm currently facing an issue while uploading values to a MySQL table from Node.js. The error 1064 keeps popping up, indicating that the query is badly formatted. However, I can't seem to pinpoint the exact problem. Here's the query in ...

The JSON array data is coming back as undefined

Currently facing an issue with the data sa var data = '[{"idcoupons_tbl":"1","discount_percent":"10"}]'; Every time I attempt to parse and retrieve a discount_percent, ie var result= jQuery.parseJSON(data); alert(result["discount_percent"]); ...

JavaScript: void(0), Internet Explorer 6, and SWFAddress are all important components

Hello there, We are on the verge of launching a secure website (regrettably, we cannot provide the URL) and have come across a rather obscure bug in IE6 that I am hoping someone may have experienced or could shed some light on. This issue only arises when ...

Is JSON.stringify() the standard object and function in JavaScript for converting objects to JSON?

This is the first time I've encountered this, but it appears to function smoothly even without the use of any JavaScript libraries or frameworks. Is this a built-in feature in JavaScript? If so, where can I locate documentation on this and other less ...

What is the best way to integrate Bootstrap 5 with Next.js?

After adding Bootstrap to my project with npm install bootstrap, I included the style in /pages/_app.js like this: import 'bootstrap/dist/css/bootstrap.css'; export default function App({ Component, pageProps }) { return <Component {...pag ...

What steps should I take to export a function from a React functional component in order to create a reusable library?

Currently, I am in the midst of developing a React component library and one of my components contains a function that I want to export. The purpose of the addParticle function is to enable users of the library to dynamically insert particles into a cont ...

Managing user input in Node.js

Users are required to input a URL in the form (e.g: "") and I need to be able to access and read the content from that URL. I am uncertain about my current method. What should I enter in the URL field below? var options = { url: '....', ...

Exploring through an array tree structure?

My menu displays the "selected" array as options. When an option is selected, its branches are rendered as new options. To keep track of the traversal, I create an array called select. For example, if someone selects the 3rd option, then the 1st option, a ...

Retrieve an image from the database and associate it with corresponding features or news in PHP

I have retrieved a value from the database, displayed it as an image, and made it a link. So, I want that when a user clicks on the different image, they get the result from the query related to the image. I hope everyone understands. <?php // Connect ...

What is the reason behind only the initial click boosting the vote count while subsequent clicks do not have the same

In this snippet of code: //JS part echo "<script> function increasevotes(e,location,user,date,vote) { e.preventDefault(); var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (this.readyState ...

Is it possible to activate the onChange function even when the input value is automated?

I am using a Form.Input element <Form.Input fluid required label="First name" placeholder="Jose" name="firstName" value={ _.isNil(selectedProfile) ? firstName : selectedProfile.first_name } onChange={this.onCustomerFieldUpdate} /> In my code, I am ...

Dynamically shift the table footer to the bottom of a scrolling div by utilizing jQuery

I'm facing a challenge where I need to relocate each th tag from the footer row of a table to the bottom of a scrolling div. You can check out the scenario on this link. Currently, I am able to achieve this by hardcoding it like so: $('.sticky- ...

What is the best practice for making a gRPC call within a Typescript Vue.Js component?

Upon reviewing the grpc documentation, I discovered that proto files can be used to generate Node (Javascript), Typescript with the assistance of grpc_tools_node_protoc_ts, and grpc-web. Given that performance is not a critical factor in my particular situ ...

Numerous Fascinating Challenges with React Native

Looking to share my React Native project and seeking help with some issues I'm facing. Despite multiple attempts, I have been unsuccessful in resolving them. Can anyone provide assistance? I have thoroughly searched Stack Overflow for similar questio ...

Chart showing a Google Timeline: Allow for each bar to be colored differently when there are multiple bars on the same line

It appears that the Google Timeline charts have a feature to color individual blocks on the timeline as indicated in the documentation at However, there seems to be an issue when two bars overlap on the same line, which is evident in this fiddle: http://j ...

Encountering difficulties in fetching data with formData in nextJS

Exploring the realm of NextJS and delving into server side actions. I'm facing a challenge with this specific request. import { revalidatePath } from "next/cache"; export async function submitIPCR(prevState: any, formData: FormData) { / ...

Display the JSON data in a table only if the ID is a match

I am working on a table that consists of 5 rows and 2 columns. Each row is assigned an ID ranging from 1 to 5. My goal is to insert JSON data into the table, but only if the ID in the data matches the ID of the corresponding row. If there is no matching I ...

Passing a JSON object between pages in Next.js using props during programmatic navigation

In my Next.js project, I have two pages. On the first page, the user fills out a form to create a post. The information is stored in a large JSON object, which needs to be passed to the second page for the user to preview the post before finalizing it. Wi ...