The best way to describe this issue is to checkout the JSFiddle. The animation will be very choppy when you try to move around the mouse or do anything. I've narrowed it down to having something to do with classing this.requestAnimationFrame(this.animation) which didn't work at first, but after looking on stackoverflow I discovered a fix to say this.animate = this.animate.bind(this);
This made it work but extremely slow! How can I make this faster while retaining the class?
JSFiddle:
https://jsfiddle.net/gentleman_goat66/o5wn3bpf/100/
Here is my code below:
HTML
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/three@0.151.3/build/three.module.js",
"OrbitControls": "https://unpkg.com/three@0.151.3/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<div id="container">
</div>
JS
import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
class Heatmap {
constructor(){
console.log("Heatmap - constructor()");
//ThreeJS Variables
this.camera = null;
this.scene = null;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.orbital_controls = null;
this.ambientLight = null;
this.heatmap = new THREE.Object3D(); //This will hold all the meshes that make up the heatmap
this.animate = this.animate.bind(this);
//Animation Related Variables
this.clock = new THREE.Clock();
this.delta = 0;
this.fps = 60; //60 fps
this.interval = 1;
this.seconds = 2; //seconds for animation
this.timeSoFar = 0;
this.targetTime = 3;
//Setup
this.scene = new THREE.Scene();
this.setUpCamera();
this.setUpRenderer();
this.setUpLighting();
this.setUpOrbitalControls();
//Add optional GridHelper for Debug
this.scene.add(new THREE.GridHelper());
//TODO: Create the heatmap here
this.createDataRectangle();
//Add the finished heatmap to the scene
this.scene.add(this.heatmap);
this.render();
this.animate();
}
setUpRenderer(){
console.log("Heatmap - setUpRenderer()");
let width = $('#container').width();
let height = $('#container').height();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width,height);
this.renderer.setClearColor(0x404040);
$("#container").html(this.renderer.domElement);
window.addEventListener("resize", event => {
this.camera.aspect = innerWidth / innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(innerWidth, innerHeight);
});
}
setUpCamera(){
console.log("Heatmap - setUpCamera()");
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);
this.camera.position.z = 4;
}
setUpLighting(){
console.log("Heatmap - setUpLighting()");
this.ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(this.ambientLight);
}
setUpOrbitalControls(){
console.log("Heatmap - setUpOrbitalControls()");
this.orbital_controls = new OrbitControls( this.camera, this.renderer.domElement );
this.orbital_controls.target.set(0, 0, 0);
this.orbital_controls.enableDamping = true;
this.orbital_controls.dampingFactor = 0.025;
this.orbital_controls.maxPolarAngle = Math.PI/2.0;
this.orbital_controls.minDistance = 2.0;
this.orbital_controls.maxDistance = 80.0;
this.orbital_controls.update();
this.orbital_controls.addEventListener( 'change', function(){
if (this.target.y < 0){
this.target.y = 0;
/* camera.position.y = -1; */
} else if (this.target.y > 1){
this.target.y = 1;
/* camera.position.y = 1; */
}
});
}
createDataRectangle(){ //TODO: Make this create at a position specified
console.log("Heatmap - createDataRectangle()");
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, 0, 1,//0, floor, bottom left
-1, 0, 1,//1, floor, bottom right
-1, 0, -1,//2, floor, top right
-2, 0, -1,//3 floor, top left
-2, 0, 1,//4 roof, bottom left
-1, 0, 1,//5 roof, bottom right
-1, 0, -1,//6 roof, top right
-2, 0, -1//7 roof, top left
];
const indices = [
0, 1, 2,//floor, first triangle
2, 3, 0,//floor, second triangle
4, 5, 6,//roof, first triangle
6, 7, 4,//roof, second triangle
3, 0, 4,//west wall, first triangle
3, 4, 7,//west wall, second triangle
0, 1, 5,//south wall, first triangle
0, 5, 4,//south wall, first triangle
1, 2, 6,//east wall, first triangle
1, 6, 5,//east wall, second triangle
2, 3, 7,//north wall, first triangle
2, 7, 6,//north wall, second triangle
];
const colors = [
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0, // top left
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0 // top left
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({side:THREE.FrontSide,vertexColors:true,color: 0xFFFFFF });
const sample_rectangle = new THREE.Mesh(geometry, material);
this.heatmap.add(sample_rectangle);
}
animate(){
requestAnimationFrame( this.animate);
this.orbital_controls.update();//Required for Damping on OrbitControls
this.delta += this.clock.getDelta();
if (this.delta > this.interval) {
//this.timeSoFar = this.timeSoFar + (1 * morphDirection);
this.render();
this.delta = this.delta % this.interval;
}
}
render(){
this.renderer.render(this.scene, this.camera);
}
}
$(document).ready(function(){
console.log("DOM ready!");
new Heatmap();
});
CSS
body,html{
margin: 0;
width: 100%;
height: 100%;
}
#container {
width: 100%;
height: 100%;
background-color: black;
margin: 0;
}
I'm not sure what were you expecting the
this.delta += this.clock.getDelta();
if (this.delta > this.interval) {
part would do. But this is your issue, this.interval = 1
here means that the part in the if
block will get executed only once every second.
Maybe you wanted to limit to some FPS, in which case you should have had this.interval = 1 / this.fps;
but I don't see a good reason to do this, since limiting to a fixed FPS is VERY hard to get right (you must choose a multiple of the active monitor's native refresh rate, and with variable rate monitors...)
So the best is probably to get rid of this check entirely and run as fast as the monitor and browser are willing to go.
body,html{
margin: 0;
width: 100%;
height: 100%;
}
#container {
width: 100%;
height: 100%;
background-color: black;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/three@0.151.3/build/three.module.js",
"OrbitControls": "https://unpkg.com/three@0.151.3/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<div id="container">
</div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
class Heatmap {
constructor(){
console.log("Heatmap - constructor()");
//ThreeJS Variables
this.camera = null;
this.scene = null;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.orbital_controls = null;
this.ambientLight = null;
this.heatmap = new THREE.Object3D(); //This will hold all the meshes that make up the heatmap
this.animate = this.animate.bind(this);
this.seconds = 2; //seconds for animation
this.timeSoFar = 0;
this.targetTime = 3;
//Setup
this.scene = new THREE.Scene();
this.setUpCamera();
this.setUpRenderer();
this.setUpLighting();
this.setUpOrbitalControls();
//Add optional GridHelper for Debug
this.scene.add(new THREE.GridHelper());
//TODO: Create the heatmap here
this.createDataRectangle();
//Add the finished heatmap to the scene
this.scene.add(this.heatmap);
this.render();
this.animate();
}
setUpRenderer(){
console.log("Heatmap - setUpRenderer()");
let width = $('#container').width();
let height = $('#container').height();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width,height);
this.renderer.setClearColor(0x404040);
$("#container").html(this.renderer.domElement);
window.addEventListener("resize", event => {
this.camera.aspect = innerWidth / innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(innerWidth, innerHeight);
});
}
setUpCamera(){
console.log("Heatmap - setUpCamera()");
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);
this.camera.position.z = 4;
}
setUpLighting(){
console.log("Heatmap - setUpLighting()");
this.ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(this.ambientLight);
}
setUpOrbitalControls(){
console.log("Heatmap - setUpOrbitalControls()");
this.orbital_controls = new OrbitControls( this.camera, this.renderer.domElement );
this.orbital_controls.target.set(0, 0, 0);
this.orbital_controls.enableDamping = true;
this.orbital_controls.dampingFactor = 0.025;
this.orbital_controls.maxPolarAngle = Math.PI/2.0;
this.orbital_controls.minDistance = 2.0;
this.orbital_controls.maxDistance = 80.0;
this.orbital_controls.update();
this.orbital_controls.addEventListener( 'change', function(){
if (this.target.y < 0){
this.target.y = 0;
/* camera.position.y = -1; */
} else if (this.target.y > 1){
this.target.y = 1;
/* camera.position.y = 1; */
}
});
}
createDataRectangle(){ //TODO: Make this create at a position specified
console.log("Heatmap - createDataRectangle()");
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, 0, 1,//0, floor, bottom left
-1, 0, 1,//1, floor, bottom right
-1, 0, -1,//2, floor, top right
-2, 0, -1,//3 floor, top left
-2, 0, 1,//4 roof, bottom left
-1, 0, 1,//5 roof, bottom right
-1, 0, -1,//6 roof, top right
-2, 0, -1//7 roof, top left
];
const indices = [
0, 1, 2,//floor, first triangle
2, 3, 0,//floor, second triangle
4, 5, 6,//roof, first triangle
6, 7, 4,//roof, second triangle
3, 0, 4,//west wall, first triangle
3, 4, 7,//west wall, second triangle
0, 1, 5,//south wall, first triangle
0, 5, 4,//south wall, first triangle
1, 2, 6,//east wall, first triangle
1, 6, 5,//east wall, second triangle
2, 3, 7,//north wall, first triangle
2, 7, 6,//north wall, second triangle
];
const colors = [
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0, // top left
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0 // top left
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({side:THREE.FrontSide,vertexColors:true,color: 0xFFFFFF });
const sample_rectangle = new THREE.Mesh(geometry, material);
this.heatmap.add(sample_rectangle);
}
animate(){
requestAnimationFrame(this.animate);
this.orbital_controls.update();//Required for Damping on OrbitControls
this.render();
}
render(){
this.renderer.render(this.scene, this.camera);
}
}
$(document).ready(function(){
console.log("DOM ready!");
new Heatmap();
});
</script>