Bonjour à tous,
Je m'arrache les cheveux depuis plusieurs jours concernant la mise à jour d'un objet canvas avec THREEJS :
Il s'agît d'une représentation 3d du modèle Bohr des atomes, après l'appui sur un bouton de la table périodique d'un élément s'ouvre une modale représentant l'atome et ses électrons gravitant autour du noyau en 3d...
Tout fonctionne bien au niveau de la représentation et rendering sauf que la scene ne s'actualise pas sauf en rafraichissant manuellement la page !
J'ai tout essayé : placer l'init de ma classe dans l'eventListener de la modale, ne créer l'objet 'BohrModel' qu'à ce moment là, rien n'y fait...
Soit la scene ne s'actualise pas complétement, soit plusieurs instances de celle-ci se rajoutent au fur et à mesure dans la modale...
J'ai tenté aussi :
this.renderer.forceContextLoss()
this.renderer.dispose();
Je pensais qu'il existait un moyen de réinitialiser l'objet à chaque fois que la modale ou l'action d'un bouton était demandé mais apparemment la scène le canvas et le render persistent en mémoire...
Je vois dans les forum énormément de questions du même ordre qui ne sont même pas répondues ...
Quelle serait la bonne méthode svp ?
Modale :
app.js :
import { Atoms } from "./Atoms.js";
import { BohrModel } from "./BohrModel.js";
const bohrModel = new BohrModel();
console.log(window.screen.width)
const btnClose = document.querySelector(".btn-close");
const modaleAtome = document.querySelector(".modale__atome");
const btnAjouter = document.querySelector(".btn__ajouter");
const cardAtome = document.querySelector(".card__atome");
const atomSymbol = document.querySelector(".atom__symbol");
const nomAtome = document.querySelector(".nom__atome");
const qteAtome = document.querySelector(".qte__atome");
const atoms = new Atoms();
//Afficher la liste de boutons pour chaque atome en asynchrone
await atoms.getAtomsList();
const atomsButtons = document.querySelectorAll(".btn__atome");
//Ecouteur sur le bouton d'atomes
atomsButtons.forEach((btn) => {
btn.addEventListener("click", (e) => {
showAtomModal(e);
});
});
//Affiche la modale de l'atome choisi
const showAtomModal = (e) => {
modaleAtome.style.display = "flex";
nomAtome.innerHTML = "";
nomAtome.innerText = e.currentTarget.dataset.atomName;
atomSymbol.innerText = e.currentTarget.dataset.atomSymbol;
bohrModel.init();
let electronsLayers = "";
electronsLayers = e.currentTarget.dataset.electronConfiguration;
//Extrait la configuration électronique de l'atome actif
let atomConfig = atoms.getElectronConfig(electronsLayers);
console.log("tbl config = ", atomConfig);
//Calcul du nombre d'orbites représentant les couches électroniques
let atomOrbits = 0;
atomOrbits = atomConfig.length;
console.log("orbits =", atomOrbits);
//Crée les orbites pour l'atome actif
bohrModel.createOrbits(atomOrbits);
//Crée le noyau de l'atome actif
//Place les electrons sur les orbites
for (let i = 0; i < atomConfig.length; i++) {
bohrModel.placeElectrons(atomConfig[i], i + 1);
console.log(`orbite ${i + 1} nbrElectrons =${atomConfig[i]}`);
}
};
//Traitement bouton ajouter
btnAjouter.addEventListener("click", () => {
modaleAtome.style.display = "none";
});
//Traitement incrémentation clavier
document.addEventListener("keyup", (e) => {
let keyName = e.key;
console.log("control cliqué");
if (keyName === "+" && this.qteAtome.value >= 1) {
this.qteAtome.value++;
}
if (keyName === "-" && this.qteAtome.value > 1) {
this.qteAtome.value--;
}
});
//Fermeture modale atome
btnClose.addEventListener("click", () => {
modaleAtome.style.display = "none";
});
classe BohrModel.js :
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
export class BohrModel {
constructor() {
//Données et distance de séparation par rapport au nucleus
this.distFromNucleus = 1;
this.orbits = [];
}
init() {
if (this.canvas) {
this.canvas="";
}
if (this.renderer) {
this.renderer.forceContextLoss()
this.renderer.dispose();
}
// if (this.scene) {
// this.scene='';
// }
this.createCanvas();
this.createScene();
this.createRenderer();
this.createCamera();
this.createSpot();
this.createNucleus();
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.light = new THREE.DirectionalLight(0xffffff);
this.scene.add(this.light);
this.createAnimations();
this.animate();
}
createCanvas() {
//Données canvas
this.canvas = document.querySelector("#bohr-model");
this.canvasWidth = 200;
this.canvasHeight = 200;
}
createScene() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color("lightsmoke");
}
createCamera() {
this.camera = new THREE.PerspectiveCamera(
75,
this.canvasWidth / this.canvasHeight,
0.1,
1000
);
this.camera.position.z = 15;
}
createRenderer() {
this.renderer = new THREE.WebGLRenderer({
antialias: true,
});
this.renderer.setSize(this.canvasWidth, this.canvasHeight);
this.renderer.setClearColor(0x000, 1.0);
this.canvas.appendChild(this.renderer.domElement);
}
createSpot() {
const spot1 = new THREE.SpotLight(0xffffff);
spot1.position.set(100, 100, 150);
this.scene.add(spot1);
}
createNucleus() {
this.nucleusRadius = 1;
this.nucleusGeo = new THREE.SphereGeometry(this.nucleusRadius, 32, 32);
this.nucleusMat = new THREE.MeshPhongMaterial({ color: "lightblue" });
this.nucleus = new THREE.Mesh(this.nucleusGeo, this.nucleusMat);
this.scene.add(this.nucleus);
}
//Création de chaque orbite représentant les couches électroniques
createOrbits(nbrOrbits) {
//On retire de la scène tous les éléments (orbits) anciens
if (this.orbits.length > 0) {
for (let i = 0; i < this.orbits.length; i++) {
this.scene.remove(this.orbits[i]);
}
}
this.innerOrbitRadius = this.nucleusRadius + this.distFromNucleus;
this.outerOrbitRadius = this.innerOrbitRadius + 0.02;
const orbitMat = new THREE.MeshBasicMaterial({
color: "lightgray",
side: THREE.DoubleSide,
});
for (let i = 0; i < nbrOrbits; i++) {
const orbitGeo = new THREE.RingGeometry(
this.innerOrbitRadius,
this.outerOrbitRadius,
50
);
this.orbits[i] = new THREE.Mesh(orbitGeo, orbitMat);
this.orbits.push(this.orbits[i]);
this.scene.add(this.orbits[i]);
this.innerOrbitRadius++;
this.outerOrbitRadius++;
}
return this.orbits;
}
//calcul des positions et placement d'électrons sur une orbite:
placeElectrons(nbrElectrons, targetedOrbit) {
const maxRadius = targetedOrbit + 1;
const sphereGeo = new THREE.SphereGeometry(0.2, 16, 16);
let sphereMat = new THREE.MeshPhongMaterial({ color: "white" });
for (let i = 0; i < nbrElectrons + 1; i++) {
if (this.orbits.length - 1 === targetedOrbit) {
sphereMat = new THREE.MeshPhongMaterial({ color: "red" });
}
const sphere = new THREE.Mesh(sphereGeo, sphereMat);
//Calculs répartition des sphères sur orbites par angles
const angle = i * ((2 * Math.PI) / nbrElectrons);
const x = maxRadius * Math.cos(angle);
const y = maxRadius * Math.sin(angle);
sphere.position.x = x;
sphere.position.y = y;
this.orbits[targetedOrbit].add(sphere);
}
}
createAnimations() {
this.animate = () => {
this.nucleus.rotateY(0.004);
for (let i = 0; i < this.orbits.length; i++) {
this.orbits[i].rotation.z += -0.001;
}
this.controls.update();
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate);
};
}
}