Comment un jeu télévisé peut-il nous aider à trouver la bonne couleur? D’aucuns diraient que c’est une idée saugrenue, et pourtant. Je me suis inspiré du Juste Prix pour accélérer ma recherche de la couleur idéale, dans un contexte pourtant difficile.
Table des Matières
Problématique
Sur un projet de site web, j’avais les contraintes suivantes:
- Dans le code, nous avions un thème définissant des couleurs (danger, warning, success…) ;
- Dans les maquettes, des couleurs étaient utilisées: ce n’était pas tout à fait les couleurs que nous avions au niveau du code ;
- Je n’avais pas le droit d’ajouter de nouvelles couleurs, ma seule solution était de partir des couleurs du thème et d’ajouter de la transparence jusqu’à obtenir une couleur proche de celle de la maquette.
Je pars de la maquette, ici à droite: un sélecteur de feuillet. Du vert Spring Wood (#F5F8F0), assez pale, est affiché lorsqu’un feuillet est sélectionné. Ce vert est sur fond blanc.
Problème: je n’ai que du vert Reef (#B7FE7E), bien plus clair, à disposition dans mon thème.
Je dois partir de ce vert plus clair et ajouter de la transparence jusqu’à obtenir une couleur similaire à la maquette. Je finis par trouver le vert Sugar Cane (#F6FFF0). Il s’agit du vert Reef, en transparence 12%, sur fond blanc.
Ce travail d’ajouter manuellement de la transparence étant fastidieux, j’ai cherché une manière de l’automatiser. C’est ce que raconte cet article.
Concepts
Je suis parti de l’idée suivante : j’ai 101 possibilités de transparence (de 0 à 100), ne voulant pas être pointu et aller jusqu’aux décimales. Une de ces 101 couleurs, la plus ressemblante dans le lot, est celle que je veux.
La première étape est de pouvoir comparer la différence entre deux couleurs. Je désire obtenir un score qui m’indique à quel point deux couleurs se ressemblent. Je prends la couleur avec le meilleur score, et je saurai quelle transparence appliquer à ma couleur de base.
Partant de ce constat, je dois savoir quelles sont mes 101 couleurs: puisqu’elles ont plus ou moins de transparence, elles seront altérées par la couleur de fond. Je dois donc pouvoir obtenir la couleur résultante. Par exemple, un vert foncé transparent à 50% sur fond blanc donnera un vert clair.
Enfin, et en bonus, je veux obtenir la couleur sans avoir à faire les 101 tentatives, en tant que challenge personnel. C’est là que le juste prix entrera en jeu.
Le résultat
La comparaison de couleurs
La comparaison de couleur est la fonction qui me donnera un score me permettant de savoir quelle couleur est la plus proche de ma couleur visée. Cette comparaison peut être implémentée de plusieurs manières, j’ai choisis une méthode simple.
La méthode sRGB
Cette première méthode consiste à considérer les couleurs comme étant un mélange de 3 valeurs, Rouge, Vert et Bleu. On peut la représenter comme un cube, 3D, chaque couleur étant un axe de ce graphe.
Le noir se situe à (0, 0, 0), tandis que le blanc se situe à (255, 255, 255).
Pour comparer deux couleurs, on peut faire de la géométrie simple et utiliser la formule de distance entre 3 points.
Pour calculer la distance entre deux couleurs et dans l’espace RGB, nous utilisons la formule de distance Euclidienne :
Une distance de 0 indique deux couleurs identiques. Plus la distance est élevée, moins les deux couleurs seront ressemblantes.
Avec ce système, la distance entre le blanc et le noir est de 441,67.
Démonstration
Prenons nos couleurs N(0, 0, 0) et B(255, 255, 255).
Calculer une couleur transparente
Partons du principe que nous avons un fond bleu opaque (0, 0, 255). Par dessus ce fond opaque, j’ai un bloc rouge (255, 0, 0) avec une opacité de 50%.
Comment obtenir la couleur résultante, mélangeant la couleur de base (le rouge parfait), et la couleur résultante (un rouge parfait transparent à 50% sur un fond bleu parfait) ?
La formule de composition alpha nous permet de réaliser ce calcul. Ainsi:
Pour calculer la couleur résultante lorsqu’une couleur semi-transparente est placée sur une couleur opaque , nous utilisons la formule de composition alpha :
où représente le niveau de transparence de (avec ).
Pour chaque canal RGB, la formule devient :
Démonstration
Soit la couleur une couleur rouge semi-transparente avec 50% de transparence, et la couleur une couleur bleue entièrement opaque. Nous pouvons appliquer la formule de composition alpha :
Pour chaque canal RGB, nous obtenons :
Ainsi, la couleur résultante est :
Cela donne une couleur violette, un mélange de rouge et de bleu.
Implémentation JavaScript
On en déduit une implémentation simple.
type Color = { r: number, g: number, b: number };
type ColorWithTransparency = Color & { a: number };
function colorDistance(colorA: Color, colorB: Color): number {
return Math.sqrt(
(colorA.r - colorB.r) ** 2 +
(colorA.g - colorB.g) ** 2 +
(colorA.b - colorB.b) ** 2
);
}
function blendColors(foreground: ColorWithTransparency, background: Color): Color {
const { r: fr, g: fg, b: fb, a } = foreground;
const { r: br, g: bg, b: bb } = background;
return {
r: a * fr + (1 - a) * br,
g: a * fg + (1 - a) * bg,
b: a * fb + (1 - a) * bb
};
}
function findClosestColor(foreground: Color, background: Color, target: Color): { alpha: number, resultingColor: Color } {
let bestAlpha = 0;
let minDistance = Infinity;
let bestResultingColor: Color = { r: 0, g: 0, b: 0 };
for (let alpha = 0; alpha <= 1; alpha += 0.01) {
const blendedColor = blendColors({ ...foreground, a: alpha }, background);
const distance = colorDistance(blendedColor, target);
if (distance < minDistance) {
minDistance = distance;
bestAlpha = alpha;
bestResultingColor = blendedColor;
}
}
return { alpha: bestAlpha, resultingColor: bestResultingColor };
}
const foreground: Color = { r: 255, g: 0, b: 0 };
const background: Color = { r: 0, g: 0, b: 255 }
const targetColor: Color = { r: 128, g: 0, b: 128 };
const result = findClosestColor(foreground, background, targetColor);
console.log('Closest Alpha:', result.alpha);
console.log('Resulting Color:', result.resultingColor);