In this post I will demonstrate how I was able to build a scene to interact with ThreeJS from DOM elements.
In the example I will show below, there is a textured cube with a rotating animation. I created two DOM elements to interact with the cube.Β
One element is a slider bar created by using an input element. This is used to adjust the PointLight intensity on the object.
Another element is changing the material color of the cube. When the button is clicked, a random color is chosen and applied.
In order to make this method work, you will need to bundle your own library of ThreeJS using Webpack to use. This way you can access the library outside of the modules that ThreeJS is built upon.
In order to create your own bundle, I have a walkthrough on how to do this in the post linked below.
For example below, I will be using the library I created from the above link.
Interacting With Threejs From DOM Elements
Code
Basic HTML
First you will need to create a HTML document. In order to interact with ThreeJS from DOM elements, we need to add some components. For instance inside the example page, I have a DIV that contains the input range slider for the PointLight, a button for the object material color and a canvas to render the ThreeJS scene. These are nested inside a centering DIV.
<!DOCTYPE html>
<html>
<head>
<title>ThreeJS</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* add CSS styling here */
</style>
</head>
<body>
<div class="center">
<div class="threejs_example">
<input type="range" default="20" min="0" max="100" step="1" onchange="changeLight(this.value)"/>
<input type="button" value="Change Color Tint" onclick="changeColorTint()">
<canvas id="threejs_canvas" width="450px" height="450px" ></canvas>
</div>
</div>
<!-- below is custom bundle library -->
<script src="custom_threejs.js"></script>
<script>
//add scene scripting here
</script>
</body>
</html>CSS Styling
Next, add in custom CSS styling. As I have shown below, this is simply styled centering the elements in the main container.
:root{
--main-bg-color: #9E9E9E;
--decor-color: #000000;
--bg-content-color: #611e1ea1;
}
html {
background-color: var(--main-bg-color);
}
body {
color: whitesmoke;
width: 100%;
height: 100%;
margin: auto;
padding: 0;
}
.center {
background-color: rgb(124 191 255);
width: 100%;
height: 100%;
display: grid;
align-items: center;
}
.threejs_example > * {
display: block;
width: 50%;
margin: auto;
}Import ThreeJS Library
You will need to determine the location you have placed your custom ThreeJS bundle and add it to the page.
<script src="custom_threejs.js"></script>In the event that you do not require DOM interaction with ThreeJS, you can use the imports demonstrated below. You would just need to adjust the script file paths.
<script type="importmap">
{
"imports": {
"three": "./scripts/three.module.min.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { MTLLoader } from './scripts/MTLLoader.js';
import { OBJLoader } from './scripts/OBJLoader.js';
import { OrbitControls } from './scripts/OrbitControls.js';
//rest of scene goes here
</script>Build the Scene in JS
Now we are ready for the scene. The base of this scene is mostly taken from the ThreeJS examples that are packaged with the library. In my custom bundle, I have added the following scripts. OBJLoader, MTLLoader as well as OrbitControls.
Breakdown
Create ThreeJS Scene Variables
First we need to create variables to use in our script. At the end we are pointing to a function called init() that we will cover next.
let camera, scene, renderer, pointLight;
let object, material;
let lightInt = 20;
//call to initialize the scene
init();Initialize The Scene
function init() {
// add the below scene items here
function onProgress( xhr ) {
if ( xhr.lengthComputable ) {
const percentComplete = xhr.loaded / xhr.total * 100;
console.log( 'model ' + percentComplete.toFixed( 2 ) + '% downloaded' );
}
}
function onError() {}
}You will need to create a reference for the canvas parent DIV from the DOM element to help with window sizing.
canvasParent = document.getElementsByClassName("threejs_example")[0];Next we will need to create some lighting, the camera to view the scene and the scene itself.
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 200 );
camera.aspect = canvasParent.clientWidth / canvasParent.clientWidth;
camera.position.z = 6;
scene = new THREE.Scene();
const ambientLight = new THREE.AmbientLight( 0xffffff );
scene.add( ambientLight );
pointLight = new THREE.PointLight( 0xffffff, lightInt );
camera.add( pointLight );
scene.add( camera );Following the scene creation, we will load the OBJ file and the MTL file.
const materialLoader = new MTLLoader()
.setPath('./models/')
.load( 'cube.mtl', function ( materials ) {
materials.preload();
material = materials.getAsArray()[0];
} );
const manager = new THREE.LoadingManager( loadModel );
const loader = new OBJLoader( manager );
loader.load( './models/cube.obj', function ( obj ) {
object = obj;
}, onProgress, onError );
function loadModel() {
object.traverse( function ( child ) {
if ( child.isMesh ) {
console.log(material);
child.material = material;
child.material.shininess = 0;
}
} );
object.rotation.y = .55;
object.rotation.x = .55;
scene.add( object );
animate();
}Finally we attach the scene to the canvas reference.
const useCanvas = document.getElementById("threejs_canvas");
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true, canvas: useCanvas } );
renderer.setSize( canvasParent.clientWidth * .50, canvasParent.clientWidth * .50);
const controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 4;
controls.maxDistance = 15;
controls.addEventListener( 'change', render );
window.addEventListener( 'resize', onWindowResize );
Functions After Init()
Following the above init() function, add the function to change the intensity of the PointLight.
function changeLight(value){
lightInt = value;
if(pointLight !== null)
camera.remove( pointLight );
pointLight = new THREE.PointLight( 0xffffff, lightInt );
camera.add( pointLight );
}Next we will add the functions to change the color tint of the OBJ.
function changeColorTint(){
object.traverse(function (child) {
if (child.isMesh) {
// Access the material of the child mesh here
child.material.color.set(randomColor());
}
});
}
function randomColor(){
return Math.floor(Math.random() * 0xffffff);
}We will need a function to handle the window resizing from the event listener created in the init() function.
function onWindowResize() {
camera.aspect = canvasParent.clientWidth / canvasParent.clientWidth;
camera.updateProjectionMatrix();
renderer.setSize( canvasParent.clientWidth * .50, canvasParent.clientWidth * .50 );
}Finally the render and animation functions.
function render() {
renderer.render( scene, camera );
}
function animate(){
requestAnimationFrame(animate);
object.rotation.y +=.002;
object.rotation.x +=.001;
renderer.setClearColor(0xffffff, 0);
render();
renderer.render( scene, camera );
}Hope this was useful.
