480 votes

Éclaircir ou assombrir de manière programmée une couleur hexagonale (ou rgb, et couleurs de mélange)

Voici une fonction sur laquelle j'ai travaillé pour éclaircir ou assombrir de manière programmatique une couleur hexagonale par une quantité spécifique. Il suffit de passer une chaîne comme "3F6D2A" pour la couleur (col) et un nombre entier en base10 (amt) pour la quantité à éclaircir ou à assombrir. Pour assombrir, entrez un nombre négatif (par exemple -20).

Je l'ai fait parce que toutes les solutions que j'ai trouvées jusqu'à présent semblaient compliquer le problème à l'excès. Et j'avais le sentiment que cela pouvait être fait avec juste quelques lignes de code. Faites-moi savoir si vous trouvez des problèmes, ou si vous avez des ajustements à faire pour accélérer le processus.

function LightenDarkenColor(col,amt) {
    col = parseInt(col,16);
    return (((col & 0x0000FF) + amt) | ((((col>> 8) & 0x00FF) + amt) << 8) | (((col >> 16) + amt) << 16)).toString(16);
}

Pour le développement, voici une version plus facile à lire :

function LightenDarkenColor(col,amt) {
    var num = parseInt(col,16);
    var r = (num >> 16) + amt;
    var b = ((num >> 8) & 0x00FF) + amt;
    var g = (num & 0x0000FF) + amt;
    var newColor = g | (b << 8) | (r << 16);
    return newColor.toString(16);
}

Et enfin une version pour gérer les couleurs qui peuvent (ou non) avoir le "#" au début. Plus l'ajustement des valeurs de couleur incorrectes :

function LightenDarkenColor(col,amt) {
    var usePound = false;
    if ( col[0] == "#" ) {
        col = col.slice(1);
        usePound = true;
    }

    var num = parseInt(col,16);

    var r = (num >> 16) + amt;

    if ( r > 255 ) r = 255;
    else if  (r < 0) r = 0;

    var b = ((num >> 8) & 0x00FF) + amt;

    if ( b > 255 ) b = 255;
    else if  (b < 0) b = 0;

    var g = (num & 0x0000FF) + amt;

    if ( g > 255 ) g = 255;
    else if  ( g < 0 ) g = 0;

    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}

Ok, alors maintenant ce n'est pas seulement quelques lignes, mais cela semble beaucoup plus simple et si vous n'utilisez pas le "#" et n'avez pas besoin de vérifier les couleurs hors de la gamme, c'est seulement quelques lignes.

Si vous n'utilisez pas le "#", vous pouvez simplement l'ajouter dans le code comme :

var myColor = "3F6D2A";
myColor = LightenDarkenColor(myColor,10);
thePlaceTheColorIsUsed = ("#" + myColor);

Je suppose que ma principale question est la suivante : ai-je raison ? Est-ce que cela n'englobe pas certaines situations (normales) ? Existe-t-il un moyen plus rapide ? C'est toujours KISS ?

Pimp Trizkit

865voto

Pimp Trizkit Points 1357

Après avoir réfléchi... j'ai décidé de répondre à ma propre question. Un an et demi plus tard. C'était vraiment une aventure avec les idées de plusieurs utilisateurs utiles, et je vous remercie tous ! Celle-ci est pour l'équipe ! Bien que ce ne soit pas nécessairement la réponse que je cherchais. Parce que si ce que James Khoury dit est vrai, alors il n'y a pas de véritables maths hexagonales en javascript, je dois utiliser des décimales, cette double conversion est nécessaire. Si nous faisons cette hypothèse, alors c'est probablement le moyen le plus rapide que j'ai vu (ou auquel je peux penser) pour éclaircir (ajouter du blanc) ou assombrir (ajouter du noir) une couleur RBG arbitraire en pourcentage. Cela tient également compte des problèmes mentionnés par Cool Acid dans sa réponse à cette question (il remplit les 0). Mais cette version appelle toString une seule fois. Cela tient également compte du dépassement des limites (0 et 255 seront appliqués comme limites).

Mais attention, la couleur saisie doit comporter EXACTEMENT 7 caractères, comme par exemple #08a35c . (ou 6 si vous utilisez la version supérieure)

Merci à Pablo pour l'inspiration et l'idée d'utiliser le pourcentage. Pour cela, je garderai le même nom de fonction ! lol ! Cependant, celle-ci est différente, car elle normalise le pourcentage à 255 et ajoute donc la même quantité à chaque couleur (plus de blanc). Si vous passez 100 pour percent cela rendra votre couleur blanche pure. Si vous passez 0 pour percent il ne se passera rien. Si vous passez en 1 pour percent il ajoutera 3 teintes à toutes les couleurs (2,55 teintes par 1%, arrondi). Vous passez donc réellement dans un pourcentage de blanc (ou de noir, utilisez le négatif). Cette version vous permet donc d'éclaircir le rouge pur (FF0000), par exemple.

J'ai également utilisé des éléments de la réponse de Keith Mashinter à cette question : Comment convertir des décimales en hexadécimales en JavaScript ?

J'ai supprimé certaines parenthèses, apparemment inutiles. (comme dans la déclaration ternaire double et dans l'artisanat B) Je ne sais pas si cela va perturber la précédence des opérateurs dans certains environnements. Le test est bon dans FireFox.

function shadeColor1(color, percent) {  
    var num = parseInt(color,16),
    amt = Math.round(2.55 * percent),
    R = (num >> 16) + amt,
    G = (num >> 8 & 0x00FF) + amt,
    B = (num & 0x0000FF) + amt;
    return (0x1000000 + (R<255?R<1?0:R:255)*0x10000 + (G<255?G<1?0:G:255)*0x100 + (B<255?B<1?0:B:255)).toString(16).slice(1);
}

Ou, si vous voulez qu'il gère le "#" :

function shadeColor1(color, percent) {  
    var num = parseInt(color.slice(1),16), amt = Math.round(2.55 * percent), R = (num >> 16) + amt, G = (num >> 8 & 0x00FF) + amt, B = (num & 0x0000FF) + amt;
    return "#" + (0x1000000 + (R<255?R<1?0:R:255)*0x10000 + (G<255?G<1?0:G:255)*0x100 + (B<255?B<1?0:B:255)).toString(16).slice(1);
}

Comment cela se passe-t-il pour deux lignes de code ?

EDIT : Correction de la gaffe de l'échange B<->G. Merci svachalek !

-- UPDATE - Version 2 avec Blending --

Un peu plus d'un an plus tard, à nouveau, et ça continue. Mais cette fois, je pense que c'est fini. Je note les problèmes mentionnés à propos de la non-utilisation de HSL pour éclaircir correctement la couleur. Il existe une technique qui élimine la plupart de ces imprécisions sans avoir à convertir en HSL. Le problème principal est qu'un canal de couleur sera complètement saturé avant le reste de la couleur. Ce qui provoque un décalage de la teinte après ce point. J'ai trouvé ces questions ici , ici et ici ce qui m'a mis sur la voie. Le message de Mark Ransom m'a montré la différence, et Le message de Keith m'a montré le chemin. Le Lerp est le sauveur. C'est la même chose que de mélanger les couleurs, donc j'ai créé un blendColors également la fonction.

Donc, sans plus attendre :

function shadeColor2(color, percent) {   
    var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
    return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
}

function blendColors(c0, c1, p) {
    var f=parseInt(c0.slice(1),16),t=parseInt(c1.slice(1),16),R1=f>>16,G1=f>>8&0x00FF,B1=f&0x0000FF,R2=t>>16,G2=t>>8&0x00FF,B2=t&0x0000FF;
    return "#"+(0x1000000+(Math.round((R2-R1)*p)+R1)*0x10000+(Math.round((G2-G1)*p)+G1)*0x100+(Math.round((B2-B1)*p)+B1)).toString(16).slice(1);
}

Il n'y a pas de contrôle d'erreur, donc les valeurs hors limites transmises provoqueront des résultats inattendus. De plus, la couleur saisie doit comporter EXACTEMENT 7 caractères, par exemple #08a35c . Mais tous les autres avantages sont toujours là, comme le plafonnement de la plage de sortie (sorties 00-FF), le remplissage (0A), les poignées, etc. # et utilisable sur des couleurs unies, comme #FF0000 .

Cette nouvelle version de shadeColor prend un flottant pour son deuxième paramètre. Pour shadeColor2 la plage valable pour le deuxième paramètre (pourcentage) est la suivante -1.0 à 1.0 .

Et pour blendColors la plage valable pour le troisième paramètre (pourcentage) est la suivante 0.0 à 1.0 Les négatifs ne sont pas autorisés ici.

Cette nouvelle version ne prend plus un pourcentage de blanc pur, comme l'ancienne version. Elle prend en compte un pourcentage de la DISTANCE entre la couleur donnée et le blanc pur. Dans l'ancienne version, il était facile de saturer la couleur, et par conséquent, de nombreuses couleurs étaient calculées en blanc pur en utilisant un pourcentage important. Avec la nouvelle version, le calcul du blanc pur n'est effectué que si vous indiquez 1.0 ou du noir pur, utilisez -1.0 .

Appel à blendColors(color, "#FFFFFF", 0.5) est la même chose que shadeColor2(color,0.5) . Ainsi que, blendColors(color,"#000000", 0.5) est la même chose que shadeColor2(color,-0.5) . Juste un peu plus lentement.

shadeColor2 est plus lent que shadeColor1 mais pas de manière significative. (Attends, c'est une déclaration contradictoire !)

La précision obtenue peut être vue ici :



-- Version RGB --

function shadeRGBColor(color, percent) {
    var f=color.split(","),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=parseInt(f[0].slice(4)),G=parseInt(f[1]),B=parseInt(f[2]);
    return "rgb("+(Math.round((t-R)*p)+R)+","+(Math.round((t-G)*p)+G)+","+(Math.round((t-B)*p)+B)+")";
}

function blendRGBColors(c0, c1, p) {
    var f=c0.split(","),t=c1.split(","),R=parseInt(f[0].slice(4)),G=parseInt(f[1]),B=parseInt(f[2]);
    return "rgb("+(Math.round((parseInt(t[0].slice(4))-R)*p)+R)+","+(Math.round((parseInt(t[1])-G)*p)+G)+","+(Math.round((parseInt(t[2])-B)*p)+B)+")";
}

Utilisations :

var color1 = "rbg(63,131,163)";
var lighter-color = shadeRGBColor(color1, 0.5);  //  rgb(159,193,209)
var darker-color = shadeRGBColor(color1, -0.25); //  rgb(47,98,122)

var color2 = "rbg(244,128,0)";
var blend1 = blendRGBColors(color1, color2, 0.75);  //  rgb(199,129,41)
var blend2 = blendRGBColors(color2, color1, 0.62);  //  rgb(132,130,101)

-- Version Uni --

function shade(color, percent){
    if (color.length > 7 ) return shadeRGBColor(color,percent);
    else return shadeColor2(color,percent);
}

function blend(color1, color2, percent){
    if (color1.length > 7) return blendRGBColors(color1,color2,percent);
    else return blendColors(color1,color2,percent);
}

Utilisation :

var color1 = shade("rbg(63,131,163)", 0.5);
var color2 = shade("#3f83a3", 0.5);
var color3 = blend("rbg(63,131,163)", "rbg(244,128,0)", 0.5);
var color4 = blend("#3f83a3", "#f48000" 0.5);

PT

112voto

Pablo Points 211

J'ai trouvé une solution qui fonctionne très bien pour moi :

function shadeColor(color, percent) {

    var R = parseInt(color.substring(1,3),16);
    var G = parseInt(color.substring(3,5),16);
    var B = parseInt(color.substring(5,7),16);

    R = parseInt(R * (100 + percent) / 100);
    G = parseInt(G * (100 + percent) / 100);
    B = parseInt(B * (100 + percent) / 100);

    R = (R<255)?R:255;  
    G = (G<255)?G:255;  
    B = (B<255)?B:255;  

    var RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16));
    var GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16));
    var BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16));

    return "#"+RR+GG+BB;
}

Exemple Éclaircir :

shadeColor("#63C6FF",40);

Exemple Darken :

shadeColor("#63C6FF",-40);

4voto

Cool Acid Points 45

J'ai essayé votre fonction et il y a un petit bug : Si la valeur finale de 'r' n'a qu'un seul chiffre, le résultat ressemble à : 'a0a0a' alors que la bonne valeur est '0a0a0a', par exemple. Je l'ai corrigé rapidement en ajoutant ceci à la place de votre retour :

var rStr = (r.toString(16).length < 2)?'0'+r.toString(16):r.toString(16);
var gStr = (g.toString(16).length < 2)?'0'+g.toString(16):g.toString(16);
var bStr = (b.toString(16).length < 2)?'0'+b.toString(16):b.toString(16);

return (usePound?"#":"") + rStr + gStr + bStr;

Ce n'est peut-être pas très joli, mais ça fait le travail. Super fonction, BTW. Juste ce dont j'avais besoin :)

4voto

James Khoury Points 5433

Vous avez pensé à une conversion rgb > hsl ? Il suffit ensuite de déplacer la luminosité vers le haut ou vers le bas ? c'est la solution que je choisirais.

Une recherche rapide de quelques algorithmes m'a permis de trouver les sites suivants.

PHP : http://serennu.com/colour/rgbtohsl.php

Javascript : http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript

EDIT le lien ci-dessus n'est plus valide. Vous pouvez consulter le hub git pour le source de la page ou le Gist

Ou encore, un autre StackOverflow question pourrait être un bon endroit à regarder.


Même si ce n'est pas le bon choix pour le PO, ce qui suit est une approximation du code que je suggérais à l'origine. (En supposant que vous avez des fonctions de conversion rgb/hsl)

var SHADE_SHIFT_AMOUNT = 0.1; 

function lightenShade(colorValue)
{
    if(colorValue && colorValue.length >= 6)
    {
        var redValue = parseInt(colorValue.slice(-6,-4), 16);
        var greenValue = parseInt(colorValue.slice(-4,-2), 16);
        var blueValue = parseInt(colorValue.slice(-2), 16);

        var hsl = rgbToHsl(redValue, greenValue, blueValue);
        hsl[2]= Math.min(hsl[2] + SHADE_SHIFT_AMOUNT, 1);
        var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
        return "#" + rgb[0].toString(16) + rgb[1].toString(16) + rgb[2].toString(16);
    }
    return null;
}

function darkenShade(colorValue)
{
    if(colorValue && colorValue.length >= 6)
    {
        var redValue = parseInt(colorValue.slice(-6,-4), 16);
        var greenValue = parseInt(colorValue.slice(-4,-2), 16);
        var blueValue = parseInt(colorValue.slice(-2), 16);

        var hsl = rgbToHsl(redValue, greenValue, blueValue);
        hsl[2]= Math.max(hsl[2] - SHADE_SHIFT_AMOUNT, 0);
        var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
        return "#" + rgb[0].toString(16) + rgb[1].toString(16) + rgb[2].toString(16);
    }
    return null;
}

Cela suppose :

  1. Vous avez des fonctions hslToRgb et rgbToHsl .
  2. Le paramètre colorValue est une chaîne de caractères de la forme #RRGGBB

Bien que si nous discutons de css, il existe une syntaxe pour spécifier hsl/hsla pour IE9/Chrome/Firefox.

1voto

user1618171 Points 1

Version C#... Notez que j'obtiens des chaînes de couleurs dans ce format #FF12AE34, et que je dois couper le #FF.

    private string GetSmartShadeColorByBase(string s, float percent)
    {
        if (string.IsNullOrEmpty(s))
            return "";
        var r = s.Substring(3, 2);
        int rInt = int.Parse(r, NumberStyles.HexNumber);
        var g = s.Substring(5, 2);
        int gInt = int.Parse(g, NumberStyles.HexNumber);
        var b = s.Substring(7, 2);
        int bInt = int.Parse(b, NumberStyles.HexNumber);

        var t = percent < 0 ? 0 : 255;
        var p = percent < 0 ? percent*-1 : percent;

        int newR = Convert.ToInt32(Math.Round((t - rInt) * p) + rInt);
        var newG = Convert.ToInt32(Math.Round((t - gInt) * p) + gInt);
        var newB = Convert.ToInt32(Math.Round((t - bInt) * p) + bInt);

        return String.Format("#{0:X2}{1:X2}{2:X2}", newR, newG, newB);
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X