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/palette.js · 8.3 Ko
/* ==========================================================================
* js/palette.js — LA PALETTE DE COULEURS SELON L'HEURE
* --------------------------------------------------------------------------
* Toute l'ambiance du diorama (ciel, mer, végétation, nuages…) dépend de
* l'heure de la journée. Ce module répond à UNE question :
*
* « Quelles couleurs faut-il à 17 h 15 ? »
*
* Méthode : on définit quelques « CLÉS » (des heures précises avec leurs
* couleurs : nuit, aube, plein jour, crépuscule…). Pour une heure donnée,
* on repère les deux clés qui l'encadrent et on MÉLANGE leurs couleurs
* (interpolation linéaire, ou « lerp »).
*
* Performance : la fonction calculer() ÉCRIT dans un objet `sortie` fourni
* par l'appelant (créé une seule fois avec creerSortie()). Ainsi on ne
* fabrique pas de nouveaux objets à chaque calcul.
*
* Exemple d'utilisation :
* var p = Baie.Palette.creerSortie(); // une seule fois
* Baie.Palette.calculer(17.25, p); // à chaque changement d'heure
* forme.color = p.css.mer1; // une couleur CSS prête à l'emploi
* ========================================================================== */
Baie.Palette = (function () {
'use strict';
// ---- Les couleurs de chaque moment --------------------------------------
// Chaque « ambiance » est une table nom -> couleur hexadécimale.
// `etoiles` (0 à 1) dit si le ciel est assez sombre pour voir les étoiles.
var NUIT = {
cielHaut: '#050b26', cielBas: '#1a2354',
mer1: '#0a1a3c', mer2: '#142c5c', ecume: '#5a6fa8',
herbe: '#1d3b2c', sable: '#6c6b78', palme: '#1b4d38',
nuage: '#3c4670', soleil: '#ffcc66', lune: '#e8eefc',
etoiles: 1,
};
var AUBE = {
cielHaut: '#4b5c9e', cielBas: '#ffb18c',
mer1: '#2b5c88', mer2: '#d98c80', ecume: '#ffe3d0',
herbe: '#5f9460', sable: '#dcb99c', palme: '#3b7d55',
nuage: '#ffc9a8', soleil: '#ffb04d', lune: '#e8eefc',
etoiles: 0.15,
};
var JOUR = {
cielHaut: '#3f9be6', cielBas: '#c4e8ff',
mer1: '#168bb3', mer2: '#34b8cc', ecume: '#ffffff',
herbe: '#6fcf5a', sable: '#f2deaa', palme: '#2f9e50',
nuage: '#ffffff', soleil: '#ffe36a', lune: '#e8eefc',
etoiles: 0,
};
var CREPUSCULE = {
cielHaut: '#5d4092', cielBas: '#ff7f5e',
mer1: '#2c4b7c', mer2: '#dd6f5d', ecume: '#ffd2b8',
herbe: '#51804c', sable: '#dcaa8a', palme: '#306d47',
nuage: '#ff9c7c', soleil: '#ff7a3a', lune: '#e8eefc',
etoiles: 0.1,
};
var CREPUSCULE_TARDIF = {
cielHaut: '#1f2055', cielBas: '#80406c',
mer1: '#15274f', mer2: '#313c70', ecume: '#a98fb8',
herbe: '#2d4b37', sable: '#8c7782', palme: '#204e39',
nuage: '#5b4b7c', soleil: '#ff6a30', lune: '#e8eefc',
etoiles: 0.6,
};
// La liste des clés : { h: heure, c: ambiance }, triée par heure croissante.
// Entre 8 h 30 et 16 h, la palette reste celle du plein jour.
var CLES = [
{ h: 0, c: NUIT },
{ h: 5, c: NUIT },
{ h: 6.5, c: AUBE },
{ h: 8.5, c: JOUR },
{ h: 16, c: JOUR },
{ h: 18, c: CREPUSCULE },
{ h: 19.5, c: CREPUSCULE_TARDIF },
{ h: 21, c: NUIT },
{ h: 24, c: NUIT },
];
// Les noms des couleurs gérées (tous sauf `etoiles`, qui est un nombre).
var NOMS = ['cielHaut', 'cielBas', 'mer1', 'mer2', 'ecume', 'herbe',
'sable', 'palme', 'nuage', 'soleil', 'lune'];
// ---- Conversion hexadécimale -> [rouge, vert, bleu] ----------------------
/**
* Transforme '#ffb18c' en [255, 177, 140].
* @param {string} hex Couleur '#rrggbb'.
* @returns {number[]} Trois nombres de 0 à 255.
*/
function hexVersRgb(hex) {
var n = parseInt(hex.slice(1), 16); // base 16
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
}
// On convertit UNE FOIS pour toutes les clés (au chargement du fichier),
// pour ne pas refaire ce travail 60 fois par seconde.
CLES.forEach(function (cle) {
cle.rgb = {};
NOMS.forEach(function (nom) { cle.rgb[nom] = hexVersRgb(cle.c[nom]); });
});
// ---- Petits outils de couleur --------------------------------------------
/** [r, v, b] -> 'rgb(r,v,b)' (chaîne utilisable par Zdog et le CSS). */
function css(rgb) {
return 'rgb(' + Math.round(rgb[0]) + ',' + Math.round(rgb[1]) + ',' + Math.round(rgb[2]) + ')';
}
/** [r, v, b] + opacité -> 'rgba(r,v,b,a)' (couleur semi-transparente). */
function cssAlpha(rgb, alpha) {
return 'rgba(' + Math.round(rgb[0]) + ',' + Math.round(rgb[1]) + ',' + Math.round(rgb[2]) + ',' + alpha + ')';
}
/**
* Mélange deux couleurs.
* @param {number[]} a Première couleur [r, v, b].
* @param {number[]} b Deuxième couleur.
* @param {number} t 0 = tout `a`, 1 = tout `b`.
* @param {number[]} out Tableau de travail réutilisé (évite d'en créer un).
* @returns {string} La couleur obtenue, en CSS.
*/
function melanger(a, b, t, out) {
out[0] = a[0] + (b[0] - a[0]) * t;
out[1] = a[1] + (b[1] - a[1]) * t;
out[2] = a[2] + (b[2] - a[2]) * t;
return css(out);
}
/**
* Éclaircit (f > 0) ou assombrit (f < 0) une couleur.
* @param {number[]} rgb Couleur de départ.
* @param {number} f De -1 (noir) à +1 (blanc).
* @param {number[]} out Tableau de travail réutilisé.
* @returns {string} La couleur obtenue, en CSS.
*/
function nuancer(rgb, f, out) {
var cible = f >= 0 ? 255 : 0;
var force = Math.abs(f);
out[0] = rgb[0] + (cible - rgb[0]) * force;
out[1] = rgb[1] + (cible - rgb[1]) * force;
out[2] = rgb[2] + (cible - rgb[2]) * force;
return css(out);
}
// ---- Création de l'objet « sortie » --------------------------------------
/**
* Crée l'objet dans lequel calculer() écrira ses résultats.
* À appeler UNE SEULE FOIS.
* @returns {Object} sortie = {
* heure, phase (texte), etoiles (0..1),
* rgb: { nom: [r,v,b] }, // pour faire des calculs
* css: { nom: 'rgb(...)' } // pour colorer directement
* }
*/
function creerSortie() {
var sortie = { heure: 0, phase: '', etoiles: 0, rgb: {}, css: {} };
NOMS.forEach(function (nom) {
sortie.rgb[nom] = [0, 0, 0];
sortie.css[nom] = '#000';
});
return sortie;
}
// ---- Nom de la phase de la journée ---------------------------------------
/**
* @param {number} h Heure décimale (0 à 24).
* @returns {string} 'Aube', 'Jour', 'Crépuscule' ou 'Nuit'.
*/
function nomPhase(h) {
if (h >= 5 && h < 7.5) { return 'Aube'; }
if (h >= 7.5 && h < 16.5) { return 'Jour'; }
if (h >= 16.5 && h < 19.5) { return 'Crépuscule'; }
return 'Nuit';
}
// ---- Le calcul principal -------------------------------------------------
/**
* Calcule la palette complète pour une heure.
*
* @param {number} heure Heure décimale : 14,5 = 14 h 30. Valeur ramenée dans [0 ; 24[.
* @param {Object} sortie Objet créé par creerSortie() ; il est REMPLI par cette fonction.
* @returns {Object} Le même objet `sortie` (pratique pour enchaîner).
*/
function calculer(heure, sortie) {
// Zdog.modulo ramène toujours dans [0 ; 24[, même pour -3 ou 27.
var h = Zdog.modulo(heure, 24);
// On cherche i tel que CLES[i].h <= h < CLES[i+1].h
var i = 0;
while (i < CLES.length - 2 && h >= CLES[i + 1].h) { i++; }
var a = CLES[i];
var b = CLES[i + 1];
// t = avancement entre les deux clés (0 au début, 1 à la fin).
var t = (h - a.h) / (b.h - a.h);
// On « adoucit » t avec easeInOut : les transitions de couleur démarrent
// et finissent en douceur plutôt qu'à vitesse constante.
var te = Zdog.easeInOut(t, 2);
for (var n = 0; n < NOMS.length; n++) {
var nom = NOMS[n];
var ra = a.rgb[nom], rb = b.rgb[nom], sortieRgb = sortie.rgb[nom];
sortieRgb[0] = ra[0] + (rb[0] - ra[0]) * te;
sortieRgb[1] = ra[1] + (rb[1] - ra[1]) * te;
sortieRgb[2] = ra[2] + (rb[2] - ra[2]) * te;
sortie.css[nom] = css(sortieRgb);
}
sortie.heure = h;
sortie.etoiles = Zdog.lerp(a.c.etoiles, b.c.etoiles, te);
sortie.phase = nomPhase(h);
return sortie;
}
// Ce que le module offre aux autres fichiers.
return {
creerSortie: creerSortie,
calculer: calculer,
css: css,
cssAlpha: cssAlpha,
melanger: melanger,
nuancer: nuancer,
hexVersRgb: hexVersRgb,
nomPhase: nomPhase,
};
})();