Annuaire › Code ouvert
Code publié par son auteur, en lecture seule. « Copier dans mon espace » crée un nouveau site à vous (non publié) que vous pouvez modifier librement.
js/main.js · 7.1 Ko
/* ==========================================================================
* js/main.js — LE CHEF D'ORCHESTRE
* --------------------------------------------------------------------------
* Ce fichier :
* 1. crée l'Illustration Zdog (la « scène » liée au <canvas>) ;
* 2. y ajoute le logo (fabriqué par js/logo.js) ;
* 3. lance la boucle d'animation (requestAnimationFrame) ;
* 4. branche les contrôles de la page (case à cocher, curseur, boutons).
*
* Ordre de chargement des scripts (voir index.html) :
* zdog.min.js -> config.js -> logo.js -> main.js
* ========================================================================== */
(function () {
'use strict';
var R = AppLogo.REGLAGES; // raccourci vers les réglages
// ---- 1. ÉTAT DE L'ANIMATION ---------------------------------------------
// Ces variables décrivent « ce qui se passe » à un instant donné.
var etat = {
auto: true, // le balancement automatique est-il actif ?
vitesse: 1, // multiplicateur de vitesse (curseur de la page)
phase: 0, // angle interne du balancement (sinus)
glisse: false, // l'utilisateur est-il en train de glisser ?
tempsDepuisGlissement: 99, // secondes écoulées depuis la fin du glissement
dernierTemps: 0, // date (ms) de l'image précédente
};
// Respect des préférences d'accessibilité : si l'utilisateur a demandé
// « moins d'animations » dans son système, on démarre sans mouvement auto.
try {
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
etat.auto = false;
}
} catch (e) { /* ancien navigateur : on garde la valeur par défaut */ }
// ---- 2. CRÉATION DE LA SCÈNE --------------------------------------------
// Illustration = la scène de plus haut niveau. Elle gère le <canvas>,
// le dessin, et la rotation au glissement.
var illo = new Zdog.Illustration({
element: '#logo', // sélecteur CSS du <canvas> (voir index.html)
dragRotate: true, // glisser (souris OU doigt) fait tourner la scène
resize: true, // le canevas suit la taille de son conteneur CSS.
// Zdog multiplie alors width/height par
// window.devicePixelRatio : le rendu reste NET sur
// les écrans haute densité (téléphones, Retina).
// Fonction appelée au début d'un glissement : on suspend l'animation auto.
onDragStart: function () {
etat.glisse = true;
},
// Fonction appelée à la fin du glissement : on note l'heure de reprise.
onDragEnd: function () {
etat.glisse = false;
etat.tempsDepuisGlissement = 0;
},
// Appelée à chaque changement de taille du canevas. `this` = l'illustration.
onResize: function (largeur, hauteur) {
this.zoom = calculerZoom(largeur, hauteur);
},
});
/**
* Calcule le zoom pour que le logo tienne toujours dans le canevas.
* Le zoom multiplie TOUT (formes ET épaisseurs de trait).
* @param {number} largeur Largeur CSS du canevas en pixels.
* @param {number} hauteur Hauteur CSS du canevas en pixels.
* @returns {number} Le facteur de zoom.
*/
function calculerZoom(largeur, hauteur) {
// On prend le plus petit côté, pour ne jamais déborder.
return Math.min(largeur, hauteur) / R.hauteurScene;
}
// Zoom initial (on ne compte pas sur onResize pour le tout premier calcul).
illo.zoom = calculerZoom(illo.width, illo.height);
// Inclinaison de départ : un léger regard « du dessus ».
illo.rotate.x = R.inclinaisonRepos;
// ---- 3. AJOUT DU LOGO À LA SCÈNE ----------------------------------------
var logo = AppLogo.creerLogo(illo, AppLogo.THEMES.ocean);
// ---- 4. BOUCLE D'ANIMATION ----------------------------------------------
/**
* Appelée ~60 fois par seconde par le navigateur.
* @param {number} maintenant Horodatage en millisecondes fourni par requestAnimationFrame.
*/
function animer(maintenant) {
// deltaTime : temps écoulé depuis l'image précédente, en SECONDES.
// On le plafonne à 0,1 s : si l'onglet était caché, pas de saut brutal.
var dt = Math.min(Math.max((maintenant - etat.dernierTemps) / 1000, 0), 0.1);
etat.dernierTemps = maintenant;
if (etat.auto && !etat.glisse) {
etat.tempsDepuisGlissement += dt;
// On attend un peu après un glissement avant de reprendre.
if (etat.tempsDepuisGlissement > R.delaiReprise) {
etat.phase += dt * R.vitesseBalancement * etat.vitesse;
// Cible du balancement : un sinus oscille entre -1 et +1 doucement.
var cibleY = Math.sin(etat.phase) * R.amplitudeBalancement;
// On ne « saute » pas à la cible : on s'en approche progressivement
// (interpolation). 1 - exp(-k·dt) donne un lissage indépendant du
// nombre d'images par seconde.
var k = 1 - Math.exp(-R.douceurRetour * dt);
illo.rotate.y = Zdog.lerp(illo.rotate.y, cibleY, k);
illo.rotate.x = Zdog.lerp(illo.rotate.x, R.inclinaisonRepos, k);
}
}
// On redessine toute la scène : mise à jour puis rendu sur le canevas.
illo.updateRenderGraph();
// On demande au navigateur de rappeler animer() à la prochaine image.
requestAnimationFrame(animer);
}
// Premier appel : on lance la boucle.
requestAnimationFrame(animer);
// ---- 5. CONTRÔLES DE LA PAGE --------------------------------------------
// Petite fonction utilitaire : récupère un élément par son id (peut être null).
function el(id) { return document.getElementById(id); }
var caseAuto = el('ctrl-auto');
var curseurVitesse = el('ctrl-vitesse');
var texteVitesse = el('txt-vitesse');
if (caseAuto) {
caseAuto.checked = etat.auto;
caseAuto.addEventListener('change', function () {
etat.auto = caseAuto.checked;
});
}
if (curseurVitesse) {
curseurVitesse.addEventListener('input', function () {
// .value est une CHAÎNE de caractères : on la convertit en nombre.
etat.vitesse = parseFloat(curseurVitesse.value);
if (texteVitesse) { texteVitesse.textContent = '×' + etat.vitesse.toFixed(1); }
});
}
// Boutons de thème : chaque bouton porte data-theme="ocean" / "vanille" / …
var boutons = document.querySelectorAll('[data-theme]');
for (var i = 0; i < boutons.length; i++) {
boutons[i].addEventListener('click', function (evenement) {
var cle = evenement.currentTarget.getAttribute('data-theme');
var theme = AppLogo.THEMES[cle];
if (!theme) { return; }
AppLogo.appliquerCouleurs(logo, theme);
// Mise à jour visuelle des boutons (aria-pressed pour les lecteurs d'écran).
for (var j = 0; j < boutons.length; j++) {
boutons[j].setAttribute('aria-pressed', boutons[j] === evenement.currentTarget ? 'true' : 'false');
}
});
}
// Bouton « Remettre face à vous » : revient à la position de départ.
var boutonRaz = el('btn-raz');
if (boutonRaz) {
boutonRaz.addEventListener('click', function () {
illo.rotate.set({ x: R.inclinaisonRepos, y: 0, z: 0 });
etat.tempsDepuisGlissement = 0;
});
}
})();