Blog

vue.js 2.0 and aframe.js for WebVR & 3D WebGL applications

Introduction

user

Thomas Rutzer

...is a frontend software developer with strong focus on JavaScript based applications, large-scaling CSS and modern User Interfaces


webGL aframe.io vue.js frontend JavaScript webvr

vue.js 2.0 and aframe.js for WebVR & 3D WebGL applications

Posted by Thomas Rutzer on .
Featured

webGL aframe.io vue.js frontend JavaScript webvr

vue.js 2.0 and aframe.js for WebVR & 3D WebGL applications

Posted by Thomas Rutzer on .

INTRODUCTION

What you'll learn

This post provides a conceptual overview of how to combine a modern JavaScript Framework, with a WebGL Framework, in order to make your 3D scenes data-driven!

Interested? Let's begin!

Prerequisites

You'll need a decent knowledge of modern frontend development - including restful-driven, data-binded, component-based applications (buzzword-dropping intended); as well as a basic WebGL knowledge and understanding of 3D Objects.

The JavaScript Framework we use for this example is vue.js 2.0 and the WebGL Framework is Mozillas's aframe.io.

The Notes

This post is not a tutorial covering a specific task. Instead it is a technical concept on how to use your data to create and update your 3D Scenes. There are a lot of useful scenarios for this concept. I'll get to some of them at the end - so your patience will pay off.

I should also mention - the example code shown in the following is not production-ready, but I'm pretty sure you can make this happen!

Vue.js 2.0

Vue.js is yet another Javascript Framework, one of those which seems to evolve every moment. Maybe vue.js is even THE Javascript Framework. Since version 2 vue.js seems to have matured. If you need to spice up your user-interface with interactive components, binded data and more, this might be your new best friend.

A-Frame

Aframe.js on the other hand, is Mozillas answer to the growing demand of web-based virtual reality experiences. Most of these experiences utilize WebGL. Aframe.js wraps ThreeJS - one of the most important Javascript Toolkits for this task, into a HTML-Component System. And this HTML Components can easily be combined with vue.js - this is where the magic happens

HANDS ON

Start by building a basic scene

This article assumes that you already have a basic starter file to work with, which includes a reference to vue.js and aframe.js and of course a HTML-file to start from.

So let's skip ahead and start with building an actual 3D Scene. This is as easy as ABC. Just add a <a-scene> to your HTML. By adding a single tag, we initialize a ThreeJS - scene automatically.
For the sake of this tutorial and to have something visible, let's add some random geometries to our scene. With aframe.js this also easy. Just add some <a-entity> and give those tags some attributes. If you don't know how to work with the aframe-component-entity-system yet, here you'll find help! Let's assume you know everything about it.

After adding those random geometries with some random positioning, even more random texturing and most random scaling, your basic scene DOM could look like this:

<a-scene>
    <a-entity id="box1"
              geometry="primitive: box"
              position="1 1 1"
              scale="3 3 1"
              material="color: red">
    </a-entity>
    <a-entity id="box2"
              geometry="primitive: box"
              position="2 2 1"
              scale="2 2 1"
              material="color: green">
    </a-entity>
    <a-entity id="box3"
              geometry="primitive: box"
              scale="3 3 1"
              position="3 3 1"
              material="color: blue">
    </a-entity>
</a-scene>   

Remember this article isn't about building a magical, web-based 3D Scene, but about feeding your magical web-based 3D Scene with data. You can basically get this data from everywhere (like your database or your clients paper note in his pocket (which you should maybe digitalize at some point)).

Since you know everything about the aframe-component-entity-system, you know that you can fill your entities with help of common attributes, like we did in the example above.

Now finally, the fun part starts - don't hardcode those attributes, but bind them with your data and with help of vue.js! So we...

Add our first vue.js component

Let's assume you know everything about vue-components as well (if not, help is on the way). So we might have a component like this one:

Vue.component('simple-scene', {
    data: function () {
        return {
            data: null
        }
    }
    template:
    `
    <a-scene>
        <a-entity id="box"
                  geometry="primitive: box"
                  position="1 1 1"
                  scale="1 1 1"
                  material="color: red">
        </a-entity>
        <a-entity id="box"
                  geometry="primitive: box"
                  position="2 2 1"
                  scale="2 2 2"
                  material="color: green">
        </a-entity>
        <a-entity id="box"
                  geometry="primitive: box"
                  position="3 3 1"
                  scale="3 3 3"
                  material="color: blue">
        </a-entity>
    </a-scene>
    `
})

new Vue({
  el: '#example'
})

As you can see, we just added our previously created HTML Markup as template to this vue-component. Now add this component to our DOM ... maybe like this:

<div id="example">
  <simple-scene></simple-scene>
</div>  

it should render our magical 3D scene when you open it in your browser (which of course should be able to render WebGL). Still not that exciting - but we're getting there, by...

Adding a Service to get some data

Maybe somewhere you have a simple or even complex backend application running, with a well designed restful API. Connect this API and finally get some - yes right - data!

This could be a provider like the example below:

const API_ROOT = '/your-api-root';

class ExampleProvider {

    constructor() {
        // ----------------------
        // create a http client instance 
        // a good one is https://github.com/mzabriskie/axios
        this.http = Http.create();
    }

    getAll() {
        // ----------------------
        // API Call
        this.getAllDataPromise = this.http.get(API_ROOT);

        // ----------------------
        // return promise
        return this.getAllDataPromise;
    }
}

// ----------------------
// Expose public api
export default ExampleProvider;  

So this could be a basic class to connect your API. We save an instance of an HTTP client in its constructor.

Afterwards we add a getAll() method which returns a promise ... (hopefully) with some data.

Use this service in our vue component

Let's jump back to our previously created vue component and use it there.

First, we need to introduce our service to our component. In this example we use ES6 import system, I trust you know how to import / require your service in your project:

import ExampleProvider from './example.provider';  

In our components created() lifecycle hook:

this.exampleProvider = ExampleProvider;

this.exampleProvider.getAll().then((result) => {
    this.data = result;
});

Wohoo... data arrived! Again, only you know how your data is supposed to look like in your project. But we specify that our example data is a JSON which has been transformed to look like this:

[
    {
        id: 1,
        position: '1 1 1',
        material: 'color: red',
        scale: '1 1 1',
        geometry: 'primitive: box'
    },
    {
        id: 2,
        position: '2 2 1',
        material: 'color: green',
        scale: '2 2 2',
        geometry: 'primitive: box'
    },
        {
        id: 3,
        position: '3 3 1',
        material: 'color: blue',
        scale: '3 3 3',
        geometry: 'primitive: box'
    }
]

So you don't need to be Albert Einstein to see: This data looks a lot like the attributes we use in our aframe entities to define their appearance!
Getting closer to the final part. Now we need to replace the value of those attributes with our fresh data.

Use data in our vue component

We use this data in our vue component to render our random geometries. To do so - we need to bind the data to our template. In vue.js it works like this:

template:
`
<template v-for="item in data"
    <a-entity v-bind:id="item.id"
              v-bind:geometry="item.geometry"
              v-bind:position="item.position"
              v-bind:scale="item.scale"
              v-bind:material="item.material">
    </a-entity>
</template>
`

Two things are happening:

  1. First we use vue v-for to loop through our data array
  2. Then we bind properties of each object in this array to the attributes of our aframe-entity with v-bind (you could also use vues shorthand syntax and just do :attribute to bind data)

In a code-perfect world, if you close your eyes, reopen them again, refresh your browser and clap your hands, your magical 3D scene should look - exactly the same!

Now data manages our 3D Scene. We use the best of both, vue.js and aframe.js.

Having vue.js manage our data layer gives us everything it can do. Like automatically updating, hide, show, loop... our geometries - based on your data.

This results in aframe.js rendering those WebGL scenes dynamically for you.

More magic? Need to bind a click event? Say no more! Let's assume we want to give our geometries a random scale when a user clicks it.
To do so, first add a DOM event listener to each geometry like this:

`
<template v-for="item in data"
    <a-entity v-on:click="clickHandler(item.id)"
              v-bind:id="item.id"
              v-bind:geometry="item.geometry"
              v-bind:position="item.position"
              v-bind:scale="item.scale"
              v-bind:material="item.material">
    </a-entity>
</template>
`

(Again, use shorthand syntax for event listeners, like @click to save yourself some chars)

We need to implement this new method clickHandler to the surrounding component in it's methods property. All methods implemented in there, will be added to the components scope. Therefore, they're available both in the components template and it's this. So let's implement our handler:

methods: {
    clickHandler: function (geometryId) {
        this.data.forEach((item) => {
            if (item.id === geometryId) {
                item.scale = '' + Math.floor(Math.random() * (5 - 1 + 1) + 1)  + '' + Math.floor(Math.random() * (5 - 1 + 1) + 1) + '' Math.floor(Math.random() * (5 - 1 + 1) + 1);
            }
        });
   }
}

First we filter our geometries in the handler method by it's unique ID. Then we just concat() a String with random Int values from range 1 - 5 for altering the items scaling property. Aframe.js demands for converting those values to a string.

There's still more magic waiting for you. But we won't go into detail now, since I hope you already got the idea and you can't wait to build your own data-driven 3D world!

HANDS OFF - FINAL WORDS

We finally made it - a short introduction to a technical concept! Hopefully some of you dear readers, got an idea of how useful it could be to combine a modern Javascript application framework with aframe.js to create WebGL scenes. The few examples given in this post are very basic, but it's a starting point. And since modern browsers are getting better at rendering complex 3D stuff and 3D stuff itself gets so important nowaydays - it should not be too hard to think about useful scenarios for a development concept like this. These are some scenarios I came up with:

  • presenting your clients 3D Models products in their webshops, enabeling them to position and create hotspots with notes and links via the underlying Content-Management system

  • interactive 3D Charts visualizing the values of your HTML forms dynamically

  • a Web-VR application for your Mom's knitting group, enabeling them to see 3D instructions for difficult patterns through their VR glasses. You (as her favorite child) add content to this application simply by adding complex algorithms to a CSV File

Now it's your turn! Thanks for reading. Good luck and have fun with your own projects!

Your diePartments frontend-dev team

Hey, we built this with that approach 📷

    webGL aframe.io vue.js frontend JavaScript webvr
user

Thomas Rutzer

...is a frontend software developer with strong focus on JavaScript based applications, large-scaling CSS and modern User Interfaces