Using Three.js with Vue3 + TS

Using Three.js with Vue3 + TS

Three.js with Vite + Vue + Typescript Tutorial

Prerequisites

You already know a little bit of Vue3.

About the Errors

After reading this you will be able to create beautiful interactive three.js scenes inside your Vue3 web applications.

I am new to write ups, if I miss something please do tell me so I can improve and people do not leave mid way because something did not work out. Finally, thanks to twitch.com/ladyofcode for inspiring their viewers to write documentations.

Starting the Project

npm create vite@latest

For the purpose of this tutorial let’s name the project as VueThreeProject.

PS F:\Temp\purge> npm create vite@latest
? Project name: » VueThreeProject

Press enter to set the default package name.

PS F:\Temp\purge> npm create vite@latest
√ Project name: ... VueThreeProject
? Package name: » vuethreeproject

Use arrows to select Vue.

? Select a framework: » - Use arrow-keys. Return to submit.
    Vanilla
>   Vue
    React
    Preact
    Lit
    Svelte
    Others

Select Typescript from variant or customize your Vue install you can add routers and other useful packages.

PS F:\Temp\purge> npm create vite@latest
√ Project name: ... VueThreeProject
√ Package name: ... vuethreeproject
√ Select a framework: » Vue
? Select a variant: » - Use arrow-keys. Return to submit.
    JavaScript
>   TypeScript
    Customize with create-vue ↗
    Nuxt ↗

Final Output

PS F:\Temp\purge> npm create vite@latest
√ Project name: ... VueThreeProject
√ Package name: ... vuethreeproject
√ Select a framework: » Vue
√ Select a variant: » TypeScript

Scaffolding project in F:\Temp\purge\VueThreeProject...

Done. Now run:

  cd VueThreeProject
  npm install
  npm run dev

Now the terminal will be serving a website on http://127.0.0.1:5173/. Start another terminal and run the following command. This tutorial would not require using sass but it is important for component wise styling. You can skip this step if you want.

Installing Sass

npm i sass

Once the project is created, cd into the project and run npm install.

Setting Up Three.js

npm i three

Creating a Three.js Component

Create a sub directory named ThreeScene inside src/components in the project root.

Create the following files in ThreeScene directory.

  • ThreeScene.vue

  • ThreeScene.ts

  • SceneBuilder.ts

  • ThreeScene.scss

Use these command to achieve the actions listed above

mkdir -p src\components\ThreeScene
type nul > src\components\ThreeScene\ThreeScene.vue
type nul > src\components\ThreeScene\ThreeScene.ts
type nul > src\components\ThreeScene\SceneBuilder.ts
type nul > src\components\ThreeScene\ThreeScene.scss

Now open the corresponding files and add the following code to it.

ThreeScene.vue

This is a Vue3 single-file component that contains a single div with an id of three-sketch which is used as a reference for the ThreeScene.ts script. The script is written in TypeScript and contains the logic for creating a Three.js scene inside the three-sketch div.

The component also includes a Sass file that imports styles from the ThreeScene.scss file, which can be used to customize the appearance of the Three.js scene.

In short, this component is a simple wrapper that allows for the integration of a Three.js scene within a Vue3 application.

<template>
    <div class="sketch-container">
        <div id="three-sketch" ref="sketch" style="overflow:hidden;"></div> 
    </div>

</template>

<style lang="scss">
@import "./ThreeScene.scss"
</style>
<script lang="ts" src="./ThreeScene.ts"></script>

ThreeScene.ts

import {defineComponent, onMounted } from "vue";
import { toRaw } from "vue";

import SceneBuilder from './SceneBuilder';

export default defineComponent({
    name:"ThreeScene",
    props:{
    },
    components:{
    },
    setup(){       
        return {            
        }
    },
    data(){
        return{
           sketch:null,
           description:null,
           width: 540,
           rendered:false,
        }
    },
    methods:{
        redirectToDetailed(){
        },
        updateWindowWidth() {
                this.scene.camera.aspect = window.innerWidth / window.innerHeight;
            this.scene.camera.updateProjectionMatrix();
            this.scene.renderer.setSize( window.innerWidth, window.innerHeight );
            this.updateWindowWidth()
          }
    },
    mounted(){
        this.scene = new SceneBuilder(this.$refs.sketch)
        this.scene.render()
        window.addEventListener('resize', this.updateWindowWidth);
        this.rendered = true;
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.updateWindowWidth);
      },
    computed:{

    }
  }
);

SceneBuilder.ts

import * as THREE from 'three';

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { toRaw } from 'vue';

export default class SceneBuilder{
    root: HTMLElement
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    controls: OrbitControls
    material: THREE.MeshNormalMaterial
    directionalLight

    constructor(root: HTMLElement){
        this.root = root;
        this.scene = new THREE.Scene(),

        this.renderer = new THREE.WebGLRenderer({ antialias: true });

        this.renderer.setSize( 540, 640 );
        this.renderer.setPixelRatio(window.devicePixelRatio)
        this.scene.background = new THREE.Color( 0x5555555);
        this.cameraSetup();

        this.controls = new OrbitControls( this.camera, this.renderer.domElement );
        this.draw()
        this.root.appendChild( this.renderer.domElement );
    }

    cameraSetup (){

        this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

        this.camera.position.set( -20, 10, 20 );
        this.camera.lookAt(0,0,0)
    }

    draw () {
        // LINE

        this.material = new THREE.LineBasicMaterial( { color: 0xffffff } );


        // GRID
        const gridHelper = new THREE.GridHelper( 500, 50 );
        this.scene.add( gridHelper );

        //AXES
        const axesHelper = new THREE.AxesHelper( 5 );
        this.scene.add( axesHelper );

        //LIGHTS
        var light = new THREE.Light(  0xFFFFFF, 10, 100 );
        light.position.set( 10, 10, 10 );
        this.scene.add( light );

        this.light = new THREE.AmbientLight( 0xaaaaaa)
        this.scene.add( this.light );


        //threejs create box
        let box = new THREE.BoxGeometry( 1, 1, 1 );
        let material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
        let cube = new THREE.Mesh( box, material );
        cube.position.set(0, -6, -6)
        this.scene.add( cube );
}

    render = () => {
        requestAnimationFrame( this.render );
        this.renderer.render(toRaw(this.scene), this.camera)
        this.controls.update();



    }

}

Now to see this component directly we can import it in main.js and see the view.

main.js

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

import ThreeScene from './components/ThreeScene/ThreeScene.vue'

createApp(ThreeScene).mount('#app')

Open browser at http://127.0.0.1:5173/ and see the result

A Three.js scene in Vue3 rendered using  Vite+Vue+TypeScript Project

A Three.js scene in Vue3 rendered using Vite+Vue+TypeScript Project

Usage in .vue

To use it inside other components import the ThreeScene.vue inside the script part and use it as the following <ThreeScene/>.

Git Repository

You can also use the above starter template from github

git clone https://github.com/QuantumNovice/vue3-threejs-typescript-starter-template
cd vue3-threejs-typescript-starter-template
npm i
npm run dev

Concluding Remarks

In conclusion, I hope this tutorial about using Three.js with Vue3 and TypeScript has been informative and helpful. By combining these powerful libraries, you can create stunning 3D graphics and interactive experiences on the web.

As you continue to explore the possibilities of Three.js, Vue 3, and TypeScript, don't be afraid to experiment and push the boundaries of what's possible. The web development landscape is constantly evolving, and there is no limit on your imagination.

Remember to always keep learning, stay curious, and have fun along the way. Thank you for reading, and happy coding!