Aclarar u oscurecer programáticamente un color hexadecimal (o rgb, y mezclar colores)

603

Aquí hay una función en la que estaba trabajando para aclarar u oscurecer programáticamente un color hexadecimal en una cantidad específica. Simplemente pase una cadena como "3F6D2A"para el color ( col) y un entero base10 ( amt) para la cantidad a aclarar u oscurecer. Para oscurecer, pase un número negativo (es decir -20).

La razón por la que hice esto fue porque todas las soluciones que encontré, hasta ahora, parecían complicar demasiado el problema. Y tenía la sensación de que se podía hacer con solo un par de líneas de código. Por favor, avíseme si encuentra algún problema o si tiene que hacer algún ajuste que pueda acelerar el proceso.

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


// TEST
console.log( LightenDarkenColor("3F6D2A",40) );

Para el uso de desarrollo, aquí hay una versión más fácil de leer:

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);
}


// TEST
console.log(LightenDarkenColor("3F6D2A", -40));

Y finalmente una versión para manejar colores que pueden (o no) tener el "#" al principio. Además de ajustar los valores de color incorrectos:

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);
}

Bien, ahora no son solo un par de líneas, sino que parece mucho más simple y si no está usando el "#" y no necesita verificar los colores fuera de rango, son solo un par de líneas.

Si no usa el "#", puede agregarlo en un código como:

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

Supongo que mi pregunta principal es, ¿estoy en lo cierto aquí? ¿No abarca esto algunas situaciones (normales)?

14
  • 1
    Si no obtiene los resultados esperados al modificar los colores, sugiero mirar en el espacio de color LAB, que está más cerca de la visión humana. Muchos idiomas tienen bibliotecas para la conversión. En mi experiencia, especialmente los tonos de naranja pueden ser problemáticos al oscurecer o aclarar.
    Henrik
    8/04/2016 a las 11:19
  • Muy buen punto. Sin embargo, el propósito principal de esta pregunta era encontrar, en primer lugar, la fórmula de menor tamaño y tiempo de ejecución más rápido ... y en segundo lugar, su precisión. Por lo tanto, por qué no me ocupé de convertir a HSL o lo que sea. Aquí la velocidad y el tamaño son más importantes. Pero, como puede ver con mi versión 2 de la fórmula. El uso de LERP para sombrear dará como resultado naranjas agradables en toda la gama de tonos. Eche un vistazo a la tabla de colores a continuación y avíseme si ese rango de tonos no se acerca mucho a la precisión real. 8/04/2016 a las 17:58
  • Me confundí un poco con la estructura de aquí, pero tienes razón, los niveles de naranja para shadowColor1 parecen ser muy buenos.
    Henrik
    10/04/2016 a las 12:05
  • Lol, te refieres a shadowColor2. Supongo que la estructura de la que estás hablando es el diseño general de la respuesta en sí. ¿Alguna sugerencia para dejar más claro? 11/04/2016 a las 4:14
  • 7
    Solo hay un problema en la función con # anterior es que no crea los ceros iniciales si el código hexadecimal final comienza con ceros. Por ejemplo, si el código hexadecimal es # 00a6b7, lo generará como # a6b7, que no funcionará si se usa como css. Puede corregir eso reemplazando la línea de retorno por esto: var string = "000000" + (g | (b << 8) | (r << 16)). ToString (16); return (usePound? "#": "") + string.substr (string.length-6); 23 oct 2018 a las 14:15
958

Bueno, esta respuesta se ha convertido en su propia bestia. Muchas versiones nuevas, se estaba volviendo estúpidamente largo. Muchas gracias a todos los grandes colaboradores de esta respuesta. Pero, para que sea sencillo para las masas. Archivé todas las versiones / historial de la evolución de esta respuesta en mi github . Y lo comencé de nuevo en StackOverflow aquí con la versión más reciente. Un agradecimiento especial para Mike 'Pomax' Kamermans por esta versión. Me dio las nuevas matemáticas.


Esta función ( pSBC) tomará un color web HEX o RGB. pSBCpuede sombrearlo más oscuro o más claro, o mezclarlo con un segundo color, y también puede pasarlo directamente pero convertir de Hex a RGB (Hex2RGB) o de RGB a Hex (RGB2Hex). Todo sin que usted sepa siquiera qué formato de color está utilizando.

Esto funciona muy rápido, probablemente el más rápido, especialmente considerando sus muchas características. Fue un largo tiempo de preparación. Vea la historia completa en mi github . Si desea la forma más pequeña y rápida posible de sombrear o difuminar, consulte las Micro Funciones a continuación y use uno de los demonios de velocidad de 2 líneas. Son excelentes para animaciones intensas, pero esta versión aquí es lo suficientemente rápida para la mayoría de las animaciones.

Esta función utiliza Log Blending o Linear Blending. Sin embargo, NO se convierte a HSL para aclarar u oscurecer correctamente un color. Por lo tanto, los resultados de esta función serán diferentes de las funciones mucho más grandes y lentas que usan HSL.

jsFiddle with pSBC

github > pSBC Wiki

Características:

  • Detecta automáticamente y acepta colores hexadecimales estándar en forma de cadenas. Por ejemplo: "#AA6622"o "#bb551144".
  • Detecta automáticamente y acepta colores RGB estándar en forma de cadenas. Por ejemplo: "rgb(123,45,76)"o "rgba(45,15,74,0.45)".
  • Sombrea los colores a blanco o negro por porcentaje.
  • Combina colores por porcentaje.
  • Hace conversión Hex2RGB y RGB2Hex al mismo tiempo, o solo.
  • Acepta códigos de color HEX de 3 dígitos (o 4 dígitos con alfa), en la forma #RGB (o #RGBA). Los expandirá. Por ejemplo: se "#C41"convierte en "#CC4411".
  • Acepta y (lineal) combina canales alfa. Si el c0color (desde) o el color c1(hasta) tiene un canal alfa, el color devuelto tendrá un canal alfa. Si ambos colores tienen un canal alfa, entonces el color devuelto será una combinación lineal de los dos canales alfa usando el porcentaje dado (como si fuera un canal de color normal). Si solo uno de los dos colores tiene un canal alfa, este alfa solo pasará al color devuelto. Esto permite mezclar / sombrear un color transparente mientras se mantiene el nivel de transparencia. O, si los niveles de transparencia también deben combinarse, asegúrese de que ambos colores tengan alfas. Al sombrear, pasará el canal alfa directamente. Si desea un sombreado básico que también sombree el canal alfa, utilice rgb(0,0,0,1)o rgb(255,255,255,1)como suc1(a) color (o sus equivalentes hexadecimales). Para los colores RGB, el canal alfa del color devuelto se redondeará a 3 decimales.
  • Las conversiones RGB2Hex y Hex2RGB están implícitas cuando se usa la combinación. Independientemente del c0(de) color; el color devuelto siempre estará en el formato de color del color c1(a), si existe. Si no hay c1(a) color, pase 'c'como el c1color y sombreará y convertirá el c0color que sea. Si solo desea conversión, pase también 0como porcentaje ( p). Si c1se omite el color o stringse pasa un no , no se convertirá.
  • También se agrega una función secundaria a la global. pSBCrse puede pasar un color hexadecimal o RGB y devuelve un objeto que contiene esta información de color. Tiene el formato: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Donde .r, .gy .btienen un rango de 0 a 255. Y cuando no hay alfa: .aes -1. De lo contrario: .atiene un rango de 0.000 a 1.000.
  • Para la salida RGB, genera rgba()una salida rgb()cuando se pasó un color con un canal alfa a c0(desde) y / o c1(a).
  • Se ha agregado la comprobación de errores menores. No es perfecto Todavía puede fallar o crear jibberish. Pero atrapará algunas cosas. Básicamente, si la estructura es incorrecta de alguna manera o si el porcentaje no es un número o está fuera de alcance, regresará null. Un ejemplo:, pSBC(0.5,"salt") == nulldonde como piensa #saltes un color válido. Elimine las cuatro líneas que terminan con return null;para eliminar esta función y hacerla más rápida y más pequeña.
  • Utiliza Log Blending. Pase truepara l(el cuarto parámetro) para usar Linear Blending.

Código:

// Version 4.0
const pSBC=(p,c0,c1,l)=>{
    let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
    if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
    if(!this.pSBCr)this.pSBCr=(d)=>{
        let n=d.length,x={};
        if(n>9){
            [r,g,b,a]=d=d.split(","),n=d.length;
            if(n<3||n>4)return null;
            x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
        }else{
            if(n==8||n==6||n<4)return null;
            if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
            d=i(d.slice(1),16);
            if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
            else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
        }return x};
    h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
    if(!f||!t)return null;
    if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
    else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
    a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
    if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
    else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}

Uso:

// Setup:

let color1 = "rgb(20,60,200)";
let color2 = "rgba(20,60,200,0.67423)";
let color3 = "#67DAF0";
let color4 = "#5567DAF0";
let color5 = "#F3A";
let color6 = "#F3A9";
let color7 = "rgb(200,60,20)";
let color8 = "rgba(200,60,20,0.98631)";

// Tests:

/*** Log Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1 ); // rgb(20,60,200) + [42% Lighter] => rgb(166,171,225)
pSBC ( -0.4, color5 ); // #F3A + [40% Darker] => #c62884
pSBC ( 0.42, color8 ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(225,171,166,0.98631)

// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c" ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #a6abe1ac

// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c" ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)

// Blending
pSBC ( -0.5, color2, color8 ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(142,60,142,0.83)
pSBC ( 0.7, color2, color7 ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(168,60,111,0.67423)
pSBC ( 0.25, color3, color7 ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(134,191,208)
pSBC ( 0.75, color7, color3 ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #86bfd0

/*** Linear Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1, false, true ); // rgb(20,60,200) + [42% Lighter] => rgb(119,142,223)
pSBC ( -0.4, color5, false, true ); // #F3A + [40% Darker] => #991f66
pSBC ( 0.42, color8, false, true ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(223,142,119,0.98631)

// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c", true ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #778edfac

// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c", true ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)

// Blending
pSBC ( -0.5, color2, color8, true ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(110,60,110,0.83)
pSBC ( 0.7, color2, color7, true ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(146,60,74,0.67423)
pSBC ( 0.25, color3, color7, true ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(127,179,185)
pSBC ( 0.75, color7, color3, true ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #7fb3b9

/*** Other Stuff ***/
// Error Checking
pSBC ( 0.42, "#FFBAA" ); // #FFBAA + [42% Lighter] => null  (Invalid Input Color)
pSBC ( 42, color1, color5 ); // rgb(20,60,200) + #F3A + [4200% Blend] => null  (Invalid Percentage Range)
pSBC ( 0.42, {} ); // [object Object] + [42% Lighter] => null  (Strings Only for Color)
pSBC ( "42", color1 ); // rgb(20,60,200) + ["42"] => null  (Numbers Only for Percentage)
pSBC ( 0.42, "salt" ); // salt + [42% Lighter] => null  (A Little Salt is No Good...)

// Error Check Fails (Some Errors are not Caught)
pSBC ( 0.42, "#salt" ); // #salt + [42% Lighter] => #a5a5a500  (...and a Pound of Salt is Jibberish)

// Ripping
pSBCr ( color4 ); // #5567DAF0 + [Rip] => [object Object] => {'r':85,'g':103,'b':218,'a':0.941}

La siguiente imagen ayudará a mostrar la diferencia entre los dos métodos de mezcla:


Funciones micro

Si realmente desea velocidad y tamaño, tendrá que usar RGB, no HEX. RGB es más sencillo y directo, HEX escribe demasiado lento y viene en demasiados sabores para un simple dos líneas (es decir, podría ser un código HEX de 3, 4, 6 u 8 dígitos). También deberá sacrificar algunas funciones, sin verificación de errores, sin HEX2RGB ni RGB2HEX. Además, deberá elegir una función específica (según el nombre de la función a continuación) para las matemáticas de combinación de colores, y si desea sombrear o mezclar. Estas funciones admiten canales alfa. Y cuando ambos colores de entrada tengan alfa, los combinará linealmente. Si solo uno de los dos colores tiene un alfa, lo pasará directamente al color resultante. A continuación se muestran dos funciones de revestimiento que son increíblemente rápidas y pequeñas:

const RGB_Linear_Blend=(p,c0,c1)=>{
    var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
    return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
}

const RGB_Linear_Shade=(p,c)=>{
    var i=parseInt,r=Math.round,[a,b,c,d]=c.split(","),P=p<0,t=P?0:255*p,P=P?1+p:1-p;
    return"rgb"+(d?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+t)+","+r(i(b)*P+t)+","+r(i(c)*P+t)+(d?","+d:")");
}

const RGB_Log_Blend=(p,c0,c1)=>{
    var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
    return"rgb"+(x?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+p*i(e[3]=="a"?e.slice(5):e.slice(4))**2)**0.5)+","+r((P*i(b)**2+p*i(f)**2)**0.5)+","+r((P*i(c)**2+p*i(g)**2)**0.5)+j;
}

const RGB_Log_Shade=(p,c)=>{
    var i=parseInt,r=Math.round,[a,b,c,d]=c.split(","),P=p<0,t=P?0:p*255**2,P=P?1+p:1-p;
    return"rgb"+(d?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+t)**0.5)+","+r((P*i(b)**2+t)**0.5)+","+r((P*i(c)**2+t)**0.5)+(d?","+d:")");
}

¿Quieres más información? Lea el artículo completo en github .

PT

(Ps, si alguien tiene las matemáticas para otro método de combinación, por favor comparta).

87
  • 9
    Una versión de PHP para quienes la necesitan: gist.github.com/chaoszcat/5325115#file-gistfile1-php 6 de abril de 2013 a las 6:28
  • 45
    Solía TinyColor -tinycolor.darken(color,amount);
    FWrnr
    15 de enero de 2014 a las 10:11
  • 4
    Gran publicación ... :) ... acabo de crear la extensión Swift de la misma: gist.github.com/matejukmar/1da47f7a950d1ba68a95 11/03/15 a las 17:15
  • 2
    Aquí está la versión de PHP para la versión actualizada de shadeColor2: function shadeColor2($color, $percent) { $color = str_replace("#", "", $color); $t=$percent<0?0:255; $p=$percent<0?$percent*-1:$percent; $RGB = str_split($color, 2); $R=hexdec($RGB[0]); $G=hexdec($RGB[1]); $B=hexdec($RGB[2]); return '#'.substr(dechex(0x1000000+(round(($t-$R)*$p)+$R)*0x10000+(round(($t-$G)*$p)+$G)*0x100+(round(($t-$B)*$p)+$B)),1); }
    Kevin M
    17/04/15 a las 3:07
  • 2
    Lo siento, aparentemente me perdí ese punto. Quizás haya dos razones. El primero y obvio es que uso Math.Round y no puedes usar números decimales para colorear con precisión (los colores no tienen decimales en hexadecimal). Por ejemplo, si el canal rojo es 8, agregue 10%usted obtiene a 8.8qué rondas 9. Luego 9.09%quita 9y obtienes 8.1819. Lo que redondea 8así que ese es un mal ejemplo. Pero todavía ilustra que usted está tomando 9.09%de 9no 8.8. Por lo tanto, puede haber algunos números allí que no se redondean exactamente igual que en mi ejemplo aquí. 13/07/2015 a las 16:56
176

Hice una solución que funciona muy bien para mí:

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;
}

Ejemplo aclarar:

shadeColor("#63C6FF",40);

Ejemplo Oscurecer:

shadeColor("#63C6FF",-40);
7
  • 5
    ¡Bien, me gusta el porcentaje! +1 Tho, lo haría R = ((R<255)?R:255).toString(16);entonces R = R.length==1 ? "0"+R : Rpor la velocidad. ¿Y no estoy seguro del punto del toUpperCase? 23/11/12 a las 21:15
  • Es innecesario. Solo agrego eso para una impresión bonita durante la prueba. Lo editaré.
    Pablo
    24/11/12 a las 1:36
  • Muy agradable. Sin embargo, ¿el 100% más claro no debería volverse completamente blanco y el 100% oscuro siempre negro, sin importar el color? parece que -100 hace que cualquier color sea negro, pero 100 (positivo) no lo hace completamente blanco.
    Kevin M
    16/04/15 a las 21:24
  • 5
    no funciona con colores sólidos como # ff0000, # 00ff00, # 0000ff
    Hitori
    5 feb 2018 a las 10:40
  • 2
    Para que funcione con el color negro, acabo de hacer este truco. var R = parseInt(color.substring(1, 3), 16) var G = parseInt(color.substring(3, 5), 16) var B = parseInt(color.substring(5, 7), 16) if (R == 0) R = 32; if (G == 0) G = 32; if (B == 0) B = 32; 25 de mayo de 2019 a las 18:32
61

Aquí hay una línea super simple basada en la respuesta de Eric

function adjust(color, amount) {
    return '#' + color.replace(/^#/, '').replace(/../g, color => ('0'+Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2));
}

Ejemplos:

adjust('#ffffff', -20) => "#ebebeb"
adjust('000000', 20) => "#141414"
5
  • 25
    "super simple".
    Andrew
    8/11/19 a las 20:32
  • 2
    ¿Cual es la cantidad? Podría ayudar a aclarar si es px, porcentaje, etc. 25 nov.20 a las 19:00
  • 1
    Puede acortarlo un poco usando \w\w(que no coincide #) en esa expresión regular en lugar de eliminar y volver a agregar el archivo #. Tampoco entiendo por qué está agregando 0y luego quitando. ¿Es esto algo de JS que no estoy siguiendo (ya lo estás usando toString(), así que no necesitas lanzarlo?) En cualquier caso, terminé con lo siguiente, que parece comportarse de la misma manera:var adj = (c, a) => c.replace(/\w\w/g, m => Math.min(255, Math.max(0, parseInt(m, 16) + a)).toString(16)) 21 dic.20 a las 15:36
  • Se agregó un demostrador para esto ... jsfiddle.net/Abeeee/xeos0p42/20 9 de enero a las 23:25
  • 1
    @MartinTournoij: Los '0'+y substr(-2)están ahí para esos números entre 0 y 15, que serán solo un dígito en hexadecimal y, por lo tanto, darán una cadena de color hexadecimal mal formada. Agrega el '0' al frente y conserva los dos últimos dígitos para que el 9 se convierta en '09', no solo en el '9'. ¡Buen primer punto! 👍
    Jedai
    10 feb a las 17:20
23

Estoy agregando mis 2 centavos aquí, una combinación satisfactoriamente pequeña de diferentes respuestas:

const colorShade = (col, amt) => {
  col = col.replace(/^#/, '')
  if (col.length === 3) col = col[0] + col[0] + col[1] + col[1] + col[2] + col[2]

  let [r, g, b] = col.match(/.{2}/g);
  ([r, g, b] = [parseInt(r, 16) + amt, parseInt(g, 16) + amt, parseInt(b, 16) + amt])

  r = Math.max(Math.min(255, r), 0).toString(16)
  g = Math.max(Math.min(255, g), 0).toString(16)
  b = Math.max(Math.min(255, b), 0).toString(16)

  const rr = (r.length < 2 ? '0' : '') + r
  const gg = (g.length < 2 ? '0' : '') + g
  const bb = (b.length < 2 ? '0' : '') + b

  return `#${rr}${gg}${bb}`
}

acepta un color que comience con #o no, con 6 caracteres o 3 caracteres.

ejemplo de uso: colorShade('#54b946', -40)

Aquí está la salida de 4 colores con 3 tonos más claros y 3 tonos más oscuros para cada uno de ellos (la cantidad es un múltiplo de 40 aquí).

ingrese la descripción de la imagen aquí

6

Esto es lo que usé en función de su función. Prefiero usar pasos sobre el porcentaje porque es más intuitivo para mí.

Por ejemplo, el 20% de un valor de 200 azules es muy diferente del 20% de un valor de 40 azules.

De todos modos, aquí está mi modificación, gracias por su función original.

function adjustBrightness(col, amt) {

    var usePound = false;

    if (col[0] == "#") {
        col = col.slice(1);
        usePound = true;
    }

    var R = parseInt(col.substring(0,2),16);
    var G = parseInt(col.substring(2,4),16);
    var B = parseInt(col.substring(4,6),16);

    // to make the colour less bright than the input
    // change the following three "+" symbols to "-"
    R = R + amt;
    G = G + amt;
    B = B + amt;

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

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

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

    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 (usePound?"#":"") + RR + GG + BB;

}
1
  • Encontré esto mucho más útil que la respuesta principal porque la respuesta principal era hacer que mis colores fueran muy intensos en lugar de solo más oscuros. Saludos Eric
    Worm
    16 de julio de 2018 a las 1:44
5

Probé su función y hubo un pequeño error: si algún valor 'r' final es solo de 1 dígito, el resultado aparece como: 'a0a0a' cuando el valor correcto es '0a0a0a', por ejemplo. Lo arreglé rápidamente agregando esto en lugar de su devolución:

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;

Tal vez no sea tan agradable, pero funciona. Gran función, por cierto. Justo lo que necesitaba. :)

1
  • 1
    ¡Gracias por la depuración y el cumplido! Lástima que no sea una respuesta a si existe o no una forma más rápida, que es mi pregunta principal. Como posiblemente uno que usa todas las conversiones hexadecimales y sin bases. Aunque, supongo que me dijiste si tenía el código correcto (+1). Desafortunadamente, la solución agregó una sobrecarga considerablemente mayor (ahora su llamada toString 6 veces) y un poco menos de KISS. Quizás sería más rápido verificar si el número base10 es 15 o menos, antes de la conversión base16. ¡Pero me gusta! 12/10/12 a las 4:42
4

¿ha pensado en una conversión rgb> hsl? luego mueva la Luminosidad hacia arriba y hacia abajo? esa es la forma en que yo iría.

Una mirada rápida a algunos algoritmos me llevó a los siguientes sitios.

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

EDITAR el enlace anterior ya no es válido. Puede ver git hub para la fuente de la página o la esencia

Alternativamente, otra pregunta de StackOverflow podría ser un buen lugar para buscar.


Aunque esta no es la opción correcta para el OP, la siguiente es una aproximación del código que sugería originalmente. (Suponiendo que tiene funciones de conversión 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;
}

Esto supone:

  1. Tienes funciones hslToRgby rgbToHsl.
  2. El parámetro colorValuees una cadena con el formato #RRGGBB

Aunque si estamos discutiendo css, hay una sintaxis para especificar hsl / hsla para IE9 / Chrome / Firefox.

17
  • Interesante, pero ¿no tendría que convertir de cadena hexadecimal a rgb a hsl? Parece que es más complicado. Quizás me estoy perdiendo algo. Pero, estoy buscando una forma KISS de hacerlo, lo más rápido posible (tiempo de ejecución). Siento que idealmente, si pudiera hacerlo todo en hexadecimal, sería lo más rápido. Pero, la solución que he desarrollado aquí implica ir a rgb para poder agregar una cantidad incremental. 6 de abril de 2011 a las 1:25
  • Sí, supongo que sería más lento, más complicado y si no usa la conversión de rgb a hsl en ningún otro lugar, probablemente no sería la solución más simplista. Sin embargo, sería más preciso que agregar valores rgb, aunque yo no soy una gran persona de color. Todo depende de cuán preciso quieras ser, supongo. 6/04/11 a las 1:36
  • ¿Cuál es la pérdida de precisión que mencionas? Supongo que quiere decir que no todos los colores [web] son ​​accesibles con rgb o algo así. 6/04/11 a las 1:40
  • Como dije, no sé mucho sobre el color: wiki Color Theory 6 de abril de 2011 a las 1:51
  • @Pimp Trizkit: Es menos preciso porque (y esta es solo mi teoría ... no soy un experto en color) estás cambiando cada canal la misma cantidad, sin importar cuánto de ese color haya para empezar. Creo que eso daría como resultado una disminución de la saturación porque está acercando los canales entre sí (en porcentaje). Por supuesto, si se desborda / desborda, eso es inevitable de todos modos. 9/04/11 a las 2:01
4

Quería cambiar un color a un nivel de brillo específico , sin importar el brillo que tenía antes, aquí hay una función JS simple que parece funcionar bien, aunque estoy seguro de que podría ser más corto

function setLightPercentage(col: any, p: number) {
    const R = parseInt(col.substring(1, 3), 16);
    const G = parseInt(col.substring(3, 5), 16);
    const B = parseInt(col.substring(5, 7), 16);
    const curr_total_dark = (255 * 3) - (R + G + B);

    // calculate how much of the current darkness comes from the different channels
    const RR = ((255 - R) / curr_total_dark);
    const GR = ((255 - G) / curr_total_dark);
    const BR = ((255 - B) / curr_total_dark);

    // calculate how much darkness there should be in the new color
    const new_total_dark = ((255 - 255 * (p / 100)) * 3);

    // make the new channels contain the same % of available dark as the old ones did
    const NR = 255 - Math.round(RR * new_total_dark);
    const NG = 255 - Math.round(GR * new_total_dark);
    const NB = 255 - Math.round(BR * new_total_dark);

    const RO = ((NR.toString(16).length === 1) ? "0" + NR.toString(16) : NR.toString(16));
    const GO = ((NG.toString(16).length === 1) ? "0" + NG.toString(16) : NG.toString(16));
    const BO = ((NB.toString(16).length === 1) ? "0" + NB.toString(16) : NB.toString(16));

    return "#" + RO + GO + BO;}
5
  • Coolio! ¿Asumo que ptiene alcance 0-100? Ni siquiera sé cómo definir correctamente el brillo en RGB, eso es algo de HSL. Por ejemplo, ¿es #FF00FFmás brillante que #FF0000? Si es así, eso implicaría que el magenta es dos veces más brillante que el rojo. Por lo tanto, se utiliza la prueba del rojo puro. Pase en rojo puro #FF0000, ajuste al 50% de brillo, y aquí lo tenemos #FF4040, ¿es así? Supuse que para hacer rojo 50% de brillo, nos oscureceríamos, ya que ya es completamente brillante ... como en #800000o 150% de brillo #FF8080. ¿Es el rosa un rojo más brillante? ¿O el rojo ya es completamente brillante? 7 de marzo de 2018 a las 16:46
  • Tienes razón, debería haber mencionado que p debe estar en el rango de 1 a 100. 8 de marzo de 2018 a las 8:27
  • # FF00FF tiene 255 como valor en el canal rojo, 0 en el canal verde y 255 en el canal azul. Cuanto más altos sean los valores combinados en los canales, mayor será el brillo del color. El número p indica que queremos que el nuevo color sea un 50% más brillante que el color original. No estoy 100% de que # FF4040 sea la respuesta correcta a "50% de rojo tan brillante como sea posible". La producción de tonos más oscuros (con, en este caso, un valor más bajo en el canal rojo) requeriría modificaciones 8 de marzo de 2018 a las 8:38
  • Sí, solo estaba señalando la ambigüedad al hablar sobre el brillo en RGB. Si se convierte a HSL, el Lcanal es literalmente brillo. Mi problema [mental personal] aquí es que, para mí, #FF0000es completamente brillante. Y #FF4040es más claro pero no más brillante ... para mí más claro significa más cerca del blanco, como el rosa. Y el brillo es la cantidad que tiene, y su rojo completo, tan rojo, es completamente brillante. Por lo tanto, #FF0000no se puede hacer más brillante ... sino más bien ... más liviano ... ¡¡tal vez solo soy un fenómeno, jajaja !! Realmente no conozco la teoría del color, así que, en realidad solo estoy hablando de mi a ... 8 de marzo de 2018 a las 17:48
  • Pero sé que cuando cambio el brillo de mi monitor, los Rojos no se vuelven rosados ​​... para mí. Así que aquí es probablemente donde comencé mi lógica. 8 de marzo de 2018 a las 17:54
3

Versión C # ... tenga en cuenta que estoy obteniendo cadenas de colores en este formato # FF12AE34, y necesito cortar el #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);
    }
2
  • 5
    Nunca usé C # antes, pero parece que las últimas tres declaraciones de variables son extrañas. An inty dos varspara el mismo tipo de datos. 25 de septiembre de 2014 a las 7:03
  • 4
    La palabra clave var en C # significa permitir que el compilador infiera el tipo en tiempo de compilación. Entonces, en el ejemplo anterior, int y var definen una variable del mismo tipo: int. Esto es útil si tiene un nombre de tipo largo o si desea hacer referencia a un tipo anónimo. Es extraño porque user1618171 ha mezclado dos estilos de declaración de variables, probablemente un error tipográfico. 28/12/15 a las 20:28
3

Su enfoque está bien :) Simplifico un poco su versión más corta (para el control de saturación, mire aquí )

(col,amt)=> (+('0x'+col)+amt*0x010101).toString(16).padStart(6,0)

// Similar to OP shortest version, we not have here # and colors range checking

var LightenDarkenColor = 
     (col,amt) => (+('0x'+col)+amt*0x010101).toString(16).padStart(6,0);    




// ------
// TEST
// ------

function update() {
  let c= col.value.padEnd(6,'0').slice(0,6);
  let color = '#'+LightenDarkenColor(c, +amt.value);
  oldColor.innerHTML = 'Old: #'+c;
  oldColor.style = `background: #${c}`;
  newColor.innerHTML = 'New: '+color
  newColor.style = `background: ${color}`;

  
}

update();
.box{ width: 100px; height: 100px; margin: 10px; display: inline-block}
<input id="col" value="3F6D2A" oninput="update()">
<input id="amt" value="30" oninput="update()"><br>
<div id="oldColor" class="box"></div>
<div id="newColor" class="box"></div>

Y versión con verificación de rangos de color y #

// # and colors range checking

var LightenDarkenColor = 
     (col,amt) => '#'+col.slice(1).match(/../g)
                         .map(x=>(x=+`0x${x}`+amt,x<0?0:(x>255?255:x))
                         .toString(16).padStart(2,0)).join``;




// ------
// TEST
// ------

function update() {
  let c= col.value.padEnd(6,'0').slice(0,7);
  let color = LightenDarkenColor(c, +amt.value);
  oldColor.innerHTML = 'Old: '+c;
  oldColor.style = `background: ${c}`;
  newColor.innerHTML = 'New: '+color
  newColor.style = `background: ${color}`;
}

update();
.box{ width: 100px; height: 100px; margin: 10px; display: inline-block}
<input id="col" value="#3F6D2A" oninput="update()">
<input id="amt" value="40" oninput="update()"><br>
<div id="oldColor" class="box"></div>
<div id="newColor" class="box"></div>
4
  • 1
    Me gustó mucho la idea de esta función corta y poderosa, pero no parece funcionar para todos los colores. : / Por ejemplo: # 54b946 aplica -30, -60, -90, -120, devuelve un morado en lugar de un verde más claro.
    antoni
    29/06/20 a las 13:46
  • 1
    @antoni Actualizo la respuesta y hago más interacción con los fragmentos (pero no cambio el código del procedimiento principal). En el fragmento inferior, verifico su color, y parece que todo funciona bien. Si no es así, ¿puede proporcionar el número hexadecimal con el resultado exacto que espera para # 54b946 y -120? 29/06/20 a las 14:19
  • 1
    Esa fue mi mala @kamil, gracias por tu función. Inteligente y conciso. +1 Tu probador me ayudó a ver que el error estaba en otra parte de mi código.
    antoni
    29/06/20 a las 14:46
  • ¡Gran respuesta, simple y muy útil! 12 de enero a las 22:08
3

Solo usé el número hexadecimal precedido por '#'.

var x = 0xf0f0f0;
x=x+0xf00; //set this value as you wish programatically
document.getElementById("heading").style = 'background-color: #'+x.toString(16);

mayor el número .. más claro el color

2

Lo necesitaba en C #, puede ayudar a los desarrolladores de .net

public static string LightenDarkenColor(string color, int amount)
    {
        int colorHex = int.Parse(color, System.Globalization.NumberStyles.HexNumber);
        string output = (((colorHex & 0x0000FF) + amount) | ((((colorHex >> 0x8) & 0x00FF) + amount) << 0x8) | (((colorHex >> 0xF) + amount) << 0xF)).ToString("x6");
        return output;
    }
4
  • Convirtió la función "no funcional" que no se ocupa de los ceros a la izquierda y puede ir por encima de FF en las sumas, = modificar el componente de color de arriba ...
    B. Go
    22 de mayo de 2020 y 20:40
  • Cuando probé la función, no funcionó debido a valores no hexadecimales (16 = 0xF) y (8 = 0x8) y da un color en 8 posiciones, pero ahora funciona muy bien
    Nassim
    23 de mayo de 2020 a las 23:43
  • ¿Intentaste aumentar desde FF? (por ejemplo, de 0xFFFFFF + 0x000004): su código pasa por encima de ese valor máximo (por ejemplo, a 0x1000003), en lugar de no aumentar, y especialmente estableciendo los 2 componentes de color superiores en 00, que es el cambio más grande que podrían hacer ...
    B. Go
    24 de mayo de 2020 a las 22:01
  • tiene razón, gracias por el comentario, excepto una entrada de límites alrededor de fff y 000, funcionará bien.
    Nassim
    25 de mayo de 2020 a las 23:43
2

Mi versión escrita a máquina:

function changeColorLightness(color: number, lightness: number): number {
    return (Math.max(0, Math.min(((color & 0xFF0000) / 0x10000) + lightness, 0xFF)) * 0x10000) +
        (Math.max(0, Math.min(((color & 0x00FF00) / 0x100) + lightness, 0xFF)) * 0x100) +
        (Math.max(0, Math.min(((color & 0x0000FF)) + lightness, 0xFF)));
}

explicación:

export function changeColorLightness(color: number, lightness: number): number {
    const r = (color & 0xFF0000) / 0x10**4;
    const g = (color & 0x00FF00) / 0x10**2;
    const b = (color & 0x0000FF);

    const changedR = Math.max(0, Math.min(r + lightness, 0xFF));
    const changedG = Math.max(0, Math.min(g + lightness, 0xFF));
    const changedB = Math.max(0, Math.min(b + lightness, 0xFF));

    return (changedR * 0x10**4) + (changedG * 0x10**2) + changedB;
}

uso:

changeColorLightness(0x00FF00, 0x50);
changeColorLightness(parseInt("#00FF00".replace('#',''), 16), 0x50);
changeColorLightness(0x00FF00, 127.5);
1

El siguiente método le permitirá aclarar u oscurecer el valor de exposición de una cadena de color hexadecimal (Hex):

private static string GetHexFromRGB(byte r, byte g, byte b, double exposure)
{
    exposure = Math.Max(Math.Min(exposure, 1.0), -1.0);
    if (exposure >= 0)
    {
        return "#"
            + ((byte)(r + ((byte.MaxValue - r) * exposure))).ToString("X2")
            + ((byte)(g + ((byte.MaxValue - g) * exposure))).ToString("X2")
            + ((byte)(b + ((byte.MaxValue - b) * exposure))).ToString("X2");
    }
    else
    {
        return "#"
            + ((byte)(r + (r * exposure))).ToString("X2")
            + ((byte)(g + (g * exposure))).ToString("X2")
            + ((byte)(b + (b * exposure))).ToString("X2");
    }

}

Para el último valor de parámetro en GetHexFromRGB (), pase un valor doble en algún lugar entre -1 y 1 (-1 es negro, 0 no cambia, 1 es blanco):

// split color (#e04006) into three strings
var r = Convert.ToByte("e0", 16);
var g = Convert.ToByte("40", 16);
var b = Convert.ToByte("06", 16);

GetHexFromRGB(r, g, b, 0.25);  // Lighten by 25%;
1

Hay una falta de compatibilidad con colores a partir de 00, es decir, "# 000623", pero aquí está la solución.

function lightenDarkenColor(colorCode, amount) {
 let usePound = false;

 if (colorCode[0] == "#") {
     colorCode = colorCode.slice(1);
     usePound = true;
 }
 const num = parseInt(colorCode, 16);
 let r = (num >> 16) + amount;

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

 let b = ((num >> 8) & 0x00FF) + amount;

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

 let g = (num & 0x0000FF) + amount;

 if (g > 255) {
     g = 255;
 } else if (g < 0) {
     g = 0;
 }
 let color = (g | (b << 8) | (r << 16)).toString(16);
 while (color.length < 6){
   color = 0 + color;
 }
 return (usePound ? '#' : '') + color;  
}
1
0

¿Cómo simplificar el color de la sombra en PHP?

<?php
function shadeColor ($color='#cccccc', $percent=-25) {

  $color = Str_Replace("#",Null,$color);

  $r = Hexdec(Substr($color,0,2));
  $g = Hexdec(Substr($color,2,2));
  $b = Hexdec(Substr($color,4,2));

  $r = (Int)($r*(100+$percent)/100);
  $g = (Int)($g*(100+$percent)/100);
  $b = (Int)($b*(100+$percent)/100);

  $r = Trim(Dechex(($r<255)?$r:255));  
  $g = Trim(Dechex(($g<255)?$g:255));  
  $b = Trim(Dechex(($b<255)?$b:255));

  $r = ((Strlen($r)==1)?"0{$r}":$r);
  $g = ((Strlen($g)==1)?"0{$g}":$g);
  $b = ((Strlen($b)==1)?"0{$b}":$b);

  return (String)("#{$r}{$g}{$b}");
}

echo shadeColor(); // #999999
1
  • Esta es una versión php de la respuesta de Pablo. Desafortunadamente, es más larga y lenta que la solución final y no aclara los colores con precisión. Aunque los oscurece con precisión. Pruebe con rojo puro (# FF0000), un aclarado del 25% debe ser (# FF4040). Consulte el final de mi respuesta para la versión PHP de Kevin M de la solución final v2. 19 de mayo de 2015 a las 17:15
0

Hice un puerto de la excelente biblioteca xcolor para eliminar su dependencia de jQuery. Hay un montón de funciones que incluyen colores claros y oscuros.

Realmente, convertir hexadecimal a RGB es una función completamente separada de aclarar u oscurecer los colores. Mantenga las cosas SECAS por favor. En cualquier caso, una vez que tenga un color RGB, puede simplemente agregar la diferencia entre el nivel de luz que desea y el nivel de luz que tiene a cada uno de los valores RGB:

var lightness = function(level) {
    if(level === undefined) {
        return Math.max(this.g,this.r,this.b)
    } else {
        var roundedLevel = Math.round(level) // fractions won't work here
        var levelChange = roundedLevel - this.lightness()

        var r = Math.max(0,this.r+levelChange)
        var g = Math.max(0,this.g+levelChange)
        var b = Math.max(0,this.b+levelChange)

        if(r > 0xff) r = 0xff
        if(g > 0xff) g = 0xff
        if(b > 0xff) b = 0xff

        return xolor({r: r, g: g, b: b})
    }
}

var lighter = function(amount) {
    return this.lightness(this.lightness()+amount)
}

Consulte https://github.com/fresheneesz/xolor para obtener más información sobre la fuente.

7
  • Todavía no he analizado el código en lo que respecta a mi OP (velocidad / tamaño / precisión). Pero en la primera lectura, hay algunos comentarios que hacer: 1) Estoy de acuerdo en que convertir hexadecimal a RGB puede verse como una función completamente separada. SI mi problema estaba destinado a resolverse con una función seca, lo cual no era un requisito. La intención aquí era tener una respuesta (ver mi Versión 2) que fuera súper rápida y súper pequeña (¡2 líneas!) Y una que aclarara y oscureciera un color hexadecimal ... específicamente ... como un autónomo autónomo función. De modo que, en su uso final, será una simple llamada a una sola función. <Cont.> 21 de enero de 2017 a las 13:04
  • 2) Y el caso de la Versión 3, por demanda popular, es la intención de tener una función universal autónoma completamente autónoma, tan rápida y tan pequeña como sea posible, que pueda tomar ciegamente un color hexadecimal o RGB y en todos sus aspectos. variaciones. Por lo tanto, se necesita una conversión de hexadecimal a RGB. <cont.> 21/01/2017 a las 13:12
  • 3) Tras un simple análisis del código. Parece que funcionaría mucho más lento y obviamente es mucho más grande que mi Versión 2 (que es la respuesta real a mi OP; la versión 3 era para las masas). Para ser justos, debería comparar este código con mi RGB Versión 2 que no hace una conversión y parece responder a su punto sobre la sequedad. Y, sinceramente, tu puerto no es mucho más simple de entender que mi 2 liner for hexadecimal. Entonces, aunque es secador, en realidad no es mucho, si es que lo hay, más simple. (la sequedad no ayudó mucho a la capacidad de comprensión) <cont.> 21 ene 2017 a las 13:20
  • 4) Mi RGB Versión 2 es una función de 2 líneas sin conversión si lo desea. Mi solución particular para mi OP original quería hexadecimal. Es por eso que hay dos tipos diferentes de Versión 2. Pero mencionas el punto sobre la sequedad y las conversiones hexadecimales, por lo que ahora nos estamos enfocando realmente en la versión 3. La versión 3 llegó mucho más tarde; solo después de que la Versión 2 fuera popular. <cont.> 21 de enero de 2017 a las 13:50
  • 5) Aunque estoy de acuerdo en que la sequedad generalmente ayuda a la universalidad. Y en la mayoría de los casos, por la capacidad de comprensión. Desafortunadamente, tiene un costo en este ejemplo. Estos costos son que es mucho más grande y aparentemente mucho más lento y aparentemente usa más memoria tanto en la pila (con su naturaleza recursiva) como en la global (2 funciones; en comparación con v2). 21 de enero de 2017 a las 13:55
0

Hace tiempo que quería poder producir tintes / matices de colores, aquí está mi solución de JavaScript:

const varyHue = function (hueIn, pcIn) {
    const truncate = function (valIn) {
        if (valIn > 255) {
            valIn = 255;
        } else if (valIn < 0)  {
            valIn = 0;
        }
        return valIn;
    };

    let red   = parseInt(hueIn.substring(0, 2), 16);
    let green = parseInt(hueIn.substring(2, 4), 16);
    let blue  = parseInt(hueIn.substring(4, 6), 16);
    let pc    = parseInt(pcIn, 10);    //shade positive, tint negative
    let max   = 0;
    let dif   = 0;

    max = red;

    if (pc < 0) {    //tint: make lighter
        if (green < max) {
            max = green;
        }

        if (blue < max) {
            max = blue;
        }

        dif = parseInt(((Math.abs(pc) / 100) * (255 - max)), 10);

        return leftPad(((truncate(red + dif)).toString(16)), '0', 2)  + leftPad(((truncate(green + dif)).toString(16)), '0', 2) + leftPad(((truncate(blue + dif)).toString(16)), '0', 2);
    } else {    //shade: make darker
        if (green > max) {
            max = green;
        }

        if (blue > max) {
            max = blue;
        }

        dif = parseInt(((pc / 100) * max), 10);

        return leftPad(((truncate(red - dif)).toString(16)), '0', 2)  + leftPad(((truncate(green - dif)).toString(16)), '0', 2) + leftPad(((truncate(blue - dif)).toString(16)), '0', 2);
    }
};
3
  • Algunos ejemplos de uso ayudarían. Y tal vez alguna explicación de por qué esta versión sobre las demás. Esta versión parece funcionar considerablemente más lenta. Y es mucho más largo. Y no parece sombrear con precisión. Parece que estás usando LERP, o algo similar ... lo cual es bueno. Desafortunadamente, es solo de un canal, entonces ese mismo valor se usa en todos los canales. Esto no es correcto, para obtener una mayor precisión, debe LERP cada canal individualmente. Como lo hace mi respuesta a esta pregunta. Además, es más pequeño y más rápido y comprueba si hay errores y maneja rgb, y hace conversiones, podría continuar. 29 de agosto de 2017 a las 4:41
  • Un ejemplo de uso: changeHue ("6e124c", 77) donde el primer argumento es el color en hexadecimal y el segundo el cambio porcentual. Un cambio de porcentaje positivo matiza (oscurece) mientras que un valor negativo tiñe (aclara) el resultado. Escribí la rutina como mi primer intento solo unas horas antes de encontrar esta página y la publiqué simplemente como una cuestión de interés. No sabía que tenía que mejorar su esfuerzo o que necesitaba su aprobación antes de hacerlo. Es completamente mi propio trabajo sin referencia a nadie más. No he oído hablar de LERP. Lo comprobaré, gracias por la sugerencia. 29 de agosto de 2017 a las 5:50
  • Jeje, bueno, ¡por supuesto que no tienes que hacer nada! ¡Y todos les agradecemos sus esfuerzos! Mis primeras preocupaciones principales fueron las que se enumeraron primero. Tratando de ayudarte con tu respuesta para que obtenga votos. (mostrar usos y explicación de cómo funciona, etc.) El resto de cosas es obviamente un análisis rápido para ayudar a ampliar el conocimiento de todos . Lo siento, si parecía un poco agresivo. Pero otra sugerencia es hacer que acepte los #colores hexadecimales. Lo siento si me pareció ... "aprobación" ... Lo vi como una revisión por pares. Si no desea que alguien analice su código u ofrezca comentarios, le pido disculpas. 29 de agosto de 2017 a las 13:01
0

Según David Sherret y Pablo, la respuesta anterior convirtió la solución a una versión más segura para Typecript.

/**
 * @param color Hex value format: #ffffff or ffffff
 * @param decimal lighten or darken decimal value, example 0.5 to lighten by 50% or 1.5 to darken by 50%.
 */
static shadeColor(color: string, decimal: number): string {
    const base = color.startsWith('#') ? 1 : 0;

    let r = parseInt(color.substring(base, 3), 16);
    let g = parseInt(color.substring(base + 2, 5), 16);
    let b = parseInt(color.substring(base + 4, 7), 16);

    r = Math.round(r / decimal);
    g = Math.round(g / decimal);
    b = Math.round(b / decimal);

    r = (r < 255)? r : 255;
    g = (g < 255)? g : 255;
    b = (b < 255)? b : 255;

    const rr = ((r.toString(16).length === 1)? `0${r.toString(16)}` : r.toString(16));
    const gg = ((g.toString(16).length === 1)? `0${g.toString(16)}` : g.toString(16));
    const bb = ((b.toString(16).length === 1)? `0${b.toString(16)}` : b.toString(16));

    return `#${rr}${gg}${bb}`;
}