What is the process for creating a loading screen using three.js?

I have a plethora of textures and models that need to be loaded into my project. In order to display a progress bar while everything is loading, I believe the LoadingManager will work perfectly as it monitors the progress of all loaded assets.

Currently, I am using the JSONLoader and TextureLoader for this task.

If anyone can provide guidance on how to implement this in the example code, it would be greatly appreciated.

https://i.sstatic.net/5LnVX.gif

var camera, scene, renderer;

 init();
 animate();

 function init() {

   camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
   camera.position.z = 400;

   scene = new THREE.Scene();


   // Load Textures 
   var modeltexture1 = new THREE.TextureLoader().load('modeltexture1.jpg');
   var modeltexture2 = new THREE.TextureLoader().load('modeltexture2.jpg');
   var modeltexture3 = new THREE.TextureLoader().load('modeltexture3.jpg');
   var modeltexture4 = new THREE.TextureLoader().load('modeltexture4.jpg');

   // Materials
   var material = {
     "texture1": new THREE.MeshPhongMaterial({ map: modeltexture1 }),
     "texture2": new THREE.MeshPhongMaterial({ map: modeltexture2 }),
     "texture3": new THREE.MeshPhongMaterial({ map: modeltexture3 }),
     "texture4": new THREE.MeshPhongMaterial({ map: modeltexture4 })
   }

   // Lights
   scene.add(new THREE.AmbientLight(0xcccccc));
   pointLight = new THREE.PointLight(0xff4400, 5, 30);
   pointLight.position.set(5, 0, 0);
   scene.add(pointLight);

   var loader = new THREE.JSONLoader();


   // Model 1
   var model1 = new THREE.Object3D;
   scene.add(model1);

   loader.load("model1.js", function(geometry) {
     var mainModel1 = new THREE.Mesh(geometry, material["texture1"]);
     model1.add(mainModel1);
   }); 

   // Model 2
   var model2 = new THREE.Object3D;
   scene.add(model2);

   loader.load("model2.js", function(geometry) {
     var mainModel2 = new THREE.Mesh(geometry, material["texture2"]);
     model2.add(mainModel2);
   });

   // Model 3
   var model3 = new THREE.Object3D;
   scene.add(model3);

   loader.load("model3.js", function(geometry) {
     var mainModel3 = new THREE.Mesh(geometry, material["texture3"]);
     model3.add(mainModel3);
   });   

   // Model 4
   var model4 = new THREE.Object3D;
   scene.add(model4);

   loader.load("model4.js", function(geometry) {
     var mainModel4 = new THREE.Mesh(geometry, material["texture4"]);
     model4.add(mainModel4);
   });  


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

   //

   window.addEventListener('resize', onWindowResize, false);

 }

 function onWindowResize() {

   camera.aspect = window.innerWidth / window.innerHeight;
   camera.updateProjectionMatrix();

   renderer.setSize(window.innerWidth, window.innerHeight);

 }

 function animate() {

   requestAnimationFrame(animate);
   renderer.render(scene, camera);

 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>

Answer №1

##Update on LoadingManager

@2pha has highlighted that the code below is redundant as THREE already includes a loading manager. However, for reference purposes, take a look at this:

Simple as pie:

var progress = document.createElement('div');
var progressBar = document.createElement('div');

progress.appendChild(progressBar);

document.body.appendChild(progress);

var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
  progressBar.style.width = (loaded / total * 100) + '%';
};

function addRandomPlaceHoldItImage(){
  var r = Math.round(Math.random() * 4000);
  new THREE.ImageLoader(manager).load('//picsum.photos/' + r + '/' + r);
}

for(var i = 0; i < 10; i++) addRandomPlaceHoldItImage();
div {
  width: 200px;
  height: 20px;
  background: #000;
  border: 2px solid #000;
}
div > div {
  width: 200px;
  height: 20px;
  background: red;
  border: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>

Original Post

The snippet below should serve your purpose well. Please note that I have only done a quick test and there may be room for improvement (including utilizing the progress callback in THREE js). I haven't fine-tuned it completely yet, but it gives you a basic idea of how to go about it. Error handling is also not implemented here, as it requires more time and effort to set up. Nevertheless, you can grasp the concept from this implementation.

You can introduce new types of loaders (compliant with the type + Loader format in THREE) along with their sources and initiate loading simply by calling load.

Hope this helps.

(I've included a random function to fetch images from placehold.it since repeated results lead to server caching issues.)

function LoaderProgress(callback){
  this.length     = -1;
  this.complete   = 0;
  this.domElement = document.createElement('div');
  this.progress   = document.createElement('div');
  
  this.domElement.appendChild(this.progress);
  this.domElement.className = 'loader-progress-wrapper';
  this.progress.className   = 'loader-progress-progress';
  this.progress.style.width = '0%';
  
  this.callback = callback || function(){};
}
LoaderProgress.prototype.add = function(type, src){
  var result = this[++this.length] = {
    loader: new THREE[type + 'Loader'],
    complete: false,
    source: src
  };
  return result;
}
LoaderProgress.prototype.load = function(){
  this.complete = 0;
  for(var i = 0; i < this.length; i++){
    var current = this[i];
    if(!current.complete){
      current.loader.load(this[i].source, function(result){
        current.complete = result;
        this.complete++;
        this.update();
      }.bind(this));
    }
  }
  return this;
}
LoaderProgress.prototype.update = function(a){
  var progress = this.complete / this.length;
  this.progress.style.width = (progress * 100) + '%';
  if(progress === 1) this.callback(this);
  console.log(progress);
  return progress;
}

var loader = new LoaderProgress(function(){
  // Implement actions requiring the loaded elements here
  alert('All resources loaded successfully');
});

document.body.appendChild(loader.domElement);

function addRandomPlaceHoldItImage(){
  var r = Math.round(Math.random() * 4000);
  loader.add('Image', '//picsum.photos/' + r + '/' + r);
}

for(var i = 0; i < 10; i++)
  addRandomPlaceHoldItImage();

loader.load();
.loader-progress-wrapper {
  width: 200px;
  height: 20px;
  background: #000;
  border: 2px solid #000;
}
.loader-progress-progress {
  width: 200px;
  height: 20px;
  background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>

Answer №2

Check out the documentation here:

For an example, take a look at this link:

Here's some code snippet for you:

  var sphereMaterial = new THREE.MeshBasicMaterial();

  var onProgress = function ( xhr ) {
    if ( xhr.lengthComputable ) {
      var percentComplete = xhr.loaded / xhr.total * 100;
      console.log( Math.round(percentComplete, 2) + '% downloaded' );
    }
  };

  var loader = new THREE.TextureLoader();
  var texture = loader.load("___4MB_IMAGE.JPG___", undefined, onProgress);
  sphereMaterial.map = texture;

This solution helped me with a similar issue I was facing - loading a large 4MB texture file that took some time to fetch...

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 Jquery AjaxStop event fails to trigger, but AjaxStart works perfectly

While I was working with APIs, I encountered a roadblock. I have a Jquery function that retrieves data from an API. To prevent a "reposition" effect, I need the function to wait until all calls are made before triggering another function to place all the ...

Utilizing a universal JavaScript array within the jQuery document(ready) function

After using jsRender to render the following HTML template, I encountered an issue with passing data values through jQuery selectors when submitting a form via AJAX. <div class="noteActions top" style="z-index: 3;"> <span onclick="noteAction(&a ...

How to use the transform function in JavaScript

Hey there, I'm looking to create a new function. I am just starting out with javascript. I want to wrap the code below in a function like: ConfirmDelete() { }; This function should perform the same action as this code snippet: $('.btn-danger ...

Retrieving form data from within the resource error callback scope

For my client-side validation on submit, I am calling a resource if the form is valid. On success, everything works fine. However, when encountering an error handler, I also perform server-side validation on my data transfer object bean which contains Hibe ...

What methods are available to adjust the header color on various pages?

I have a solution that works for one location, but I need to add red color to multiple locations. How can I achieve this? import { useRouter } from "next/router"; function Header() { const router = useRouter(); return ( <> & ...

What is the process of converting code to JavaScript and React?

There are two methods defined as shown below. const handleClick = React.useMemo(() => !isRunning ? (items: string | string[]) => { if(Array.isArray(items)){ startRun({ items: items }); } else ...

The argument represented by 'T' does not align with the parameter represented by 'number' and therefore cannot be assigned

I am confused as to why, in my situation, <T> is considered a number but cannot be assigned to a parameter of type number. Changing the type of n to either number or any resolves the issue. Error: https://i.sstatic.net/h1GE9.png Code: const dropF ...

Capture sound from web browser and display live audio visualization

I'm searching for a package that can capture audio input from the browser's microphone and display it in real time. Are there any JavaScript packages available for this purpose? I've looked at various audio visualizer options, but they all r ...

Problems arising from jQuery Mobile, NPM, and WebPack

After spending a considerable amount of time researching and experimenting, I am confident that I could piece something together to make it work. However, I would rather gain an understanding of the root cause of my issue. It is widely acknowledged that j ...

Struggling with a component that won't load in JSX?

Having some difficulty with React not rendering data associated with a component's props: import React from 'react'; import {ItemListing} from './ItemListing.js'; export var SearchResults = React.createClass({ render: functi ...

Organizing content into individual Div containers with the help of AngularJS

As someone who is new to the world of AngularJS, I am currently in the process of learning the basics. My goal is to organize a JSON file into 4 or 5 separate parent divs based on a value within the JSON data, and then populate these divs with the correspo ...

Is there a way to verify the custom form when the braintree PayPal checkout button is clicked?

I am seeking a solution to validate a custom PHP form when the Braintree PayPal checkout button is clicked. Currently, the form redirects to the PayPal screen if it is not properly filled out. My goal is to prevent the PayPal popup window from opening if ...

please transmit the id (or retrieve the id from the router path)

I have a basic blog built with React/Redux for the frontend, featuring user registration and articles. I ran into an issue when trying to pass the article ID to the editor component. The form is the same, but the paths differ for adding new articles and ed ...

Is it possible for a JavaScript environment to restore function normally after modifying the [[Prototype]] of an object?

After thoroughly reviewing the MDN disclaimers and warnings, as well as an enlightening discussion in a popular online forum, I still find myself seeking answers. This question arose from a previous exchange, which can be found here. Imagine if I were to ...

Variable missing in the ExpressJs view

Hey there! I'm new to Nodejs and currently experimenting with it. I've been trying to convert some of my basic Python codes to JavaScript. In one of my projects, I am sending a get request to the YouTube API and receiving 50 results in JSON forma ...

How can I automatically close the menu when I click on a link in Vue?

Tap the menu icon Select a link The URL changes in the background. However, the menu remains open. How do I close the menu when a link is selected? The menu is wrapped in a details HTML element. Is there a way to remove the "open" attribute from the detai ...

Utilizing TypeScript to Define Object Properties with String Keys and Values within Parentheses

I am in the process of developing a telegram bot I have the need to save all my messages as constants My message schema is structured as follows: type MessagesSchema = { [K in keyof typeof MessagesEnum]: string } Here is an example implementatio ...

transferring information to d3.js

I have a json object structured in the following way { "tweet":[ {"text": "hello world"}, {"text": "hello world"} ] } When I check the console output of "data" in my code below, it shows me an Object tweet: Array[131]. However, when I l ...

Looking to verify characters, numbers, and special characters with jQuery's regular expression feature?

Currently, I am facing a challenge in validating the password field to allow characters, numbers, and special characters. It is essential that the password consists of exactly 8 characters. Despite searching online for a solution, I have been unsuccessful ...

Transform date format using VueJS in JavaScript

I need to convert a date format from 19 Oct 2017 to 20171019. Is there a way to do this quickly? I am using FlatPickr in VueJs. Here is the code snippet for reference: import flatPickr from 'vue-flatpickr-component'; import 'flatpickr/dist/ ...