Программно осветлить или затемнить шестнадцатеричный цвет (или RGB, и смешать цвета)

504

Вот функция, над которой я работал, чтобы программно осветлить или затемнить шестнадцатеричный цвет на определенную величину. Просто передайте строку, например, "3F6D2A"для color ( col) и base10 integer ( amt) для количества, которое будет светлее или темнее. Чтобы затемнить, введите отрицательное число (т.е. -20).

Причина, по которой я сделал это, заключалась в том, что из-за всех решений, которые я нашел, до сих пор они, казалось, слишком усложняли проблему. И у меня было чувство, что это можно сделать всего несколькими строками кода. Пожалуйста, дайте мне знать, если вы обнаружите какие-либо проблемы или внесете какие-либо изменения, которые позволят ускорить его.

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

Для разработки используйте здесь более удобную для чтения версию:

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

И, наконец, версия для обработки цветов, которые могут (или не могут) иметь «#» в начале. Плюс настройка для неправильных значений цвета:

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

Итак, теперь это не просто пара строк, но кажется, что это намного проще, и если вы не используете «#» и вам не нужно проверять цвета вне диапазона, это всего лишь пара строк.

Если вы не используете «#», вы можете просто добавить его в код, например:

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

Я предполагаю, что мой главный вопрос, я прав здесь? Разве это не охватывает некоторые (нормальные) ситуации?

Тачка Тризкит
источник
1
Если вы не получите ожидаемых результатов при изменении цвета, я предлагаю рассмотреть цветовое пространство LAB, которое ближе к человеческому зрению. Многие языки имеют библиотеки для конвертации. По моему опыту особенно оттенки оранжевого могут быть проблематичными при потемнении или осветлении.
Хенрик
Очень хороший момент. Однако основной целью этого вопроса было найти, во-первых, самую быструю формулу времени выполнения и наименьшего размера ... а во-вторых, ее точность. Следовательно, почему я не имел дело с преобразованием в HSL или что-то еще. Здесь скорость и размер важнее. Но, как вы можете видеть с моей версией 2 формулы. Использование LERP для затенения приведет к получению приятных апельсинов во всем диапазоне оттенков. Посмотрите на диаграмму цвета ниже и дайте мне знать, если этот диапазон оттенков не слишком близок к точному.
Pimp Trizkit
Я немного запутался со структурой здесь, но вы правы, уровни оранжевого цвета для shadeColor1 кажутся очень хорошими.
Хенрик
Лол, ты имеешь ввиду shadeColor2. Я предполагаю, что структура, о которой вы говорите, является общей структурой самого ответа? Любые намеки, чтобы сделать более понятным?
Pimp Trizkit
3
В функции с # выше есть только одна проблема - она ​​не создает начальные нули, если окончательный шестнадцатеричный код начинается с нуля. Например, если шестнадцатеричный код # 00a6b7, он выведет его как # a6b7, который не будет работать, если использовать его в качестве css. Вы можете исправить это, заменив строку возврата следующим образом: var string = "000000" + (g | (b << 8) | (r << 16)). ToString (16); return (usePound? "#": "") + string.substr (string.length-6);
Рафаэль Леви

Ответы:

877

Ну, этот ответ стал своим собственным зверем. Много новых версий, это было глупо долго. Большое спасибо всем великим авторам этого ответа. Но, чтобы все было просто для масс. Я заархивировал все версии / историю развития этого ответа на свой github . И началось все заново с StackOverflow с самой новой версией. Особая благодарность Майку 'Pomax' Kamermans за эту версию. Он дал мне новую математику.


Эта функция ( pSBC) принимает цвет HEX или RGB. pSBCможет затенить его темнее или светлее или смешать со вторым цветом, а также может передать его прямо через него, но конвертировать из шестнадцатеричного в RGB (Hex2RGB) или из шестнадцатеричного RGB в шестнадцатеричный (RGB2Hex). Все без вас, даже не зная, какой цветовой формат вы используете.

Это работает очень быстро, вероятно, самый быстрый, особенно учитывая его многочисленные функции. Это было долгое время в процессе создания. Смотрите всю историю на моем github . Если вы хотите абсолютно наименьший и самый быстрый способ затенения или смешивания, см. Микро-функции ниже и используйте один из демонов скорости с 2 вкладышами. Они отлично подходят для интенсивной анимации, но эта версия достаточно быстра для большинства анимаций.

Эта функция использует Log Blending или Linear Blending. Тем не менее, он НЕ конвертируется в HSL, чтобы правильно осветлить или затемнить цвет. Следовательно, результаты этой функции будут отличаться от тех функций, которые намного больше и медленнее, которые используют HSL.

jsFiddle с pSBC

github> pSBC Wiki

Особенности:

  • Автоматически определяет и принимает стандартные шестнадцатеричные цвета в виде строк. Например: "#AA6622"или "#bb551144".
  • Автоматически определяет и принимает стандартные цвета RGB в виде строк. Например: "rgb(123,45,76)"или "rgba(45,15,74,0.45)".
  • Оттенки цветов к белому или черному в процентах.
  • Смешивает цвета вместе в процентах.
  • Делает ли Hex2RGB и RGB2Hex конвертацию одновременно или в одиночку.
  • Принимает 3-значные (или 4-значные w / alpha) цветовые коды HEX в форме #RGB (или #RGBA). Это расширит их. Например: "#C41"становится"#CC4411" .
  • Принимает и (линейный) смешивает альфа-каналы. Если либо c0цвет (от), либо цвет c1(до) имеет альфа-канал, то возвращаемый цвет будет иметь альфа-канал. Если оба цвета имеют альфа-канал, то возвращаемый цвет будет представлять собой линейную смесь двух альфа-каналов с использованием заданного процента (как если бы это был обычный цветовой канал). Если только один из двух цветов имеет альфа-канал, эта альфа будет просто передана возвращаемому цвету. Это позволяет смешивать / затенять прозрачный цвет, сохраняя уровень прозрачности. Или, если уровни прозрачности также должны смешиваться, убедитесь, что оба цвета имеют альфа. Когда затенение, это пройдет альфа-канал прямо через. Если вам нужна базовая заливка, которая также затеняет альфа-канал, используйте rgb(0,0,0,1)или rgb(255,255,255,1)какc1(до) цвет (или их шестнадцатеричные эквиваленты). Для цветов RGB альфа-канал возвращаемого цвета будет округлен до 3 десятичных знаков.
  • Преобразования RGB2Hex и Hex2RGB неявны при использовании смешивания. Независимо от c0(от) цвета; возвращаемый цвет всегда будет в цветовом формате c1(до) цвета, если таковой существует. Если c1цвета нет, передайте его 'c'как c1цвет, и он закрасит и преобразует c0цвет. Если требуется только преобразование, укажите 0также и процент ( p). Если c1цвет пропущен или не stringпередан, он не будет преобразован.
  • Вторичная функция также добавлена ​​к глобальной. pSBCrможет быть передан Hex или RGB цвет, и он возвращает объект, содержащий эту информацию о цвете. Это в форме: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Где .r, .gи .bимеют диапазон от 0 до 255. А когда нет альфа - : .a-1. В противном случае: .aимеет диапазон от 0,000 до 1000.
  • Для вывода RGB выводится rgba()больше, rgb()когда цвет с альфа-каналом был передан в c0(из) и / или c1(в).
  • Незначительная проверка ошибок была добавлена. Это не идеально. Это все еще может привести к сбою или созданию тряски. Но это поймает некоторые вещи. По сути, если структура в некотором смысле неверна или процент не является числом или выходит за рамки, он вернется null. Пример: pSBC(0.5,"salt") == nullгде, как он считает, #saltявляется допустимым цветом. Удалите четыре строки, которые заканчиваются, return null;чтобы удалить эту функцию и сделать ее быстрее и меньше.
  • Использует смешивание журналов. Передайте truefor l(4-й параметр), чтобы использовать Linear Blending.

Код:

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

Применение:

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

Картинка ниже поможет показать разницу в двух методах наложения:


Микро Функции

Если вы действительно хотите скорость и размер, вам придется использовать RGB, а не HEX. RGB более прост и прост, HEX пишет слишком медленно и имеет слишком много разновидностей для простого двухстрочного (т. Е. Это может быть 3, 4, 6 или 8-значный код HEX). Вам также придется пожертвовать некоторыми функциями, без проверки ошибок, без HEX2RGB и RGB2HEX. Кроме того, вам нужно будет выбрать конкретную функцию (на основе ее имени ниже) для математики смешивания цветов, а также, если вы хотите, чтобы затемнение или смешивание. Эти функции поддерживают альфа-каналы. И когда оба входных цвета имеют альфы, они будут линейно смешаны. Если только у одного из двух цветов есть альфа, он передаст его прямо к полученному цвету. Ниже приведены две невероятно быстрые и небольшие функции лайнера:

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

Хотите узнать больше? Прочитайте полную рецензию на github .

PT

(Ps. Если у кого-то есть математика для другого метода смешивания, пожалуйста, поделитесь.)

Тачка Тризкит
источник
8
Версия PHP для тех, кому это нужно: gist.github.com/chaoszcat/5325115#file-gistfile1-php
Лайонел Чан,
28
Я использовал TinyColor -tinycolor.darken(color,amount);
FWrnr
4
Отличный пост ... :) ... только что создал его расширение Swift: gist.github.com/matejukmar/1da47f7a950d1ba68a95
Матей Укмар
2
Вот версия PHP для обновленной версии 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); }
Кевин М
2
Извините, я, видимо, упустил этот момент. Возможно, есть две причины. Первое и очевидное, что я использую Math.Round, и вы не можете использовать десятичные числа для точного окрашивания (цвета не имеют десятичных дробей в шестнадцатеричном формате). Например, если есть красный канал 8, добавьте 10%, 8.8какие раунды вы получите 9. Тогда 9.09%забирай 9и получаешь 8.1819. Который округляет, 8так что это плохой пример. Но она по- прежнему показывает , что вы принимаете 9.09%в 9и 8.8. Так что там могут быть некоторые числа, которые не округляются так же, как мой пример здесь.
Сутенер Тризкит
122

Я сделал решение, которое работает очень хорошо для меня:

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

Пример облегчения:

shadeColor("#63C6FF",40);

Пример Darken:

shadeColor("#63C6FF",-40);
Pablo
источник
4
Здорово, мне нравится процент! +1 То я бы мог R = ((R<255)?R:255).toString(16);тогда сделать R = R.length==1 ? "0"+R : Rдля скорости. И я не уверен, что смысл toUpperCase?
Pimp Trizkit
Это не нужно. Я только добавляю это для красивой печати во время теста. Я отредактирую это.
Пабло
Очень хорошо. Однако не должно ли светлее на 100% становиться полностью белым, а на 100% темнее всегда черным, независимо от цвета? кажется, что -100 делает любой цвет черным, но 100 (положительный) не делает его полностью белым.
Кевин М,
4
не работает со сплошными цветами, такими как # ff0000, # 00ff00, # 0000ff
Hitori
Чтобы заставить его работать с черным цветом, я просто сделал этот хак 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;
Ирфан Раза
21

Вот супер простой вкладыш, основанный на ответе Эрика

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

Примеры:

adjust('#ffffff', -20) => "#ebebeb"
adjust('000000', 20) => "#141414"
supersan
источник
7
"супер просто".
Андрей
5

Это то, что я использовал, основываясь на вашей функции. Я предпочитаю использовать шаги за процент, потому что это более интуитивно для меня.

Например, 20% от значения 200 синего цвета сильно отличается от 20% от значения 40 синего цвета.

В любом случае, вот моя модификация, спасибо за вашу оригинальную функцию.

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;

}
Эрик Слоан
источник
Нашел это намного более полезным, чем верхний ответ, потому что верхний ответ делал мои цвета очень интенсивными, а не просто темнее. Приветствия Эрик
Червь
4

Я попробовал вашу функцию, и возникла небольшая ошибка: если какое-то окончательное значение 'r' составляет только 1 цифру, результат выглядит как: 'a0a0a', когда правильное значение, например, '0a0a0a'. Я просто быстро исправил это, добавив это вместо вашего возврата:

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;

Может быть, это не так хорошо, но это делает работу. Отличная функция, кстати. Как раз то, что мне было нужно. :)

Прохладная кислота
источник
1
Спасибо за отладку и комплимент! Жаль, что это не ответ на вопрос, существует ли более быстрый путь, и это мой главный вопрос. Как, возможно, тот, который использует все шестнадцатеричные и без базовых преобразований. То есть, я думаю, ты сказал мне, если у меня был правильный код (+1). К сожалению, исправление добавило значительно больше накладных расходов (теперь ваш вызов toString 6 раз) и немного меньше KISS. Возможно, было бы быстрее проверить, является ли число base10 15 или меньше, перед преобразованием base16. Но мне нравится!
Pimp Trizkit
4

ты думал о преобразовании rgb> hsl? тогда просто двигайте Светимость вверх и вниз? это путь, которым я бы пошел.

Беглый взгляд на некоторые алгоритмы достал мне следующие сайты.

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

РЕДАКТИРОВАТЬ вышеуказанная ссылка больше не действительна. Вы можете просмотреть git hub для источника страницы или суть

Альтернативно, другой вопрос StackOverflow может быть хорошим местом для поиска.


Несмотря на то, что это неправильный выбор для OP, ниже приводится пример кода, который я изначально предлагал. (Предполагая, что у вас есть функции преобразования 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;
}

Это предполагает:

  1. У вас есть функции hslToRgbи rgbToHsl.
  2. Параметр colorValueпредставляет собой строку в форме #RRGGBB

Хотя, если мы обсуждаем css, существует синтаксис для указания hsl / hsla для IE9 / Chrome / Firefox.

Джеймс Хури
источник
Интересно, но разве мне не пришлось бы преобразовывать шестнадцатеричную строку в rgb в hsl? Похоже, это сложнее. Может быть, я что-то упустил. Но я ищу ПОЦЕЛУЙ способ сделать это так же быстро, как это возможно (время выполнения). Я чувствую себя идеально, если бы я мог сделать все это в гексе, это было бы самым быстрым. Но решение, которое я разработал здесь, включает в себя переход к rgb, чтобы иметь возможность добавлять дополнительные суммы.
Pimp Trizkit
Да, я предполагаю, что это будет медленнее, сложнее, и если вы не используете преобразование rgb в hsl где-либо еще, то это, вероятно, не будет самым упрощенным решением. Это, однако, будет более точным, чем добавление к значениям RGB, хотя я сам не очень цветной человек. Все зависит от того, насколько вы хотите быть точным, я думаю.
Джеймс Хури
Какую потерю точности вы упоминаете? Я предполагаю, что вы имеете в виду, что все [веб] цвета не достижимы с помощью RGB или чего-то еще?
Pimp Trizkit
Как я уже сказал, я не очень разбираюсь в цвете: вики-теория цвета
Джеймс Хоури,
@Pimp Trizkit: Это менее точно, потому что (и это только моя теория ... я не эксперт по цвету) вы смещаете каждый канал в одинаковом количестве, независимо от того, сколько этого цвета было с самого начала. Я думаю, что это приведет к уменьшению насыщенности, потому что вы приближаете каналы друг к другу (в процентах). Конечно, если вы переполнены / переполнены, это все равно неизбежно.
Мэтью Крамли
2

Версия C # ... обратите внимание, что я получаю цветные строки в этом формате # FF12AE34, и мне нужно вырезать #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);
    }
user1618171
источник
5
Никогда ранее не использовал C #, но похоже, что последние три объявления переменных являются странными. An intи два varsдля одного и того же типа данных.
Pimp Trizkit,
4
Ключевое слово var в C # означает, что компилятор может определить тип во время компиляции. Таким образом, в приведенном выше примере int и var определяют переменную того же типа - int. Это полезно, если у вас длинное имя типа или если вы хотите сослаться на анонимный тип. Это странно, потому что user1618171 смешал два стиля объявления переменных - возможно, опечатку.
Даниэль Джеймс Брайерс
2

Я хотел изменить цвет на определенный уровень яркости - независимо от того, какой яркостью был цвет раньше - вот простая функция JS, которая, кажется, работает хорошо, хотя я уверен, что она может быть короче

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;}
Торбьерн Йозефссон
источник
Coolio! Я предполагаю, что pесть диапазон 0-100? Я даже не знаю, как правильно определить яркость в RGB, это вещь HSL. Например, #FF00FFярче, чем #FF0000? Если это так, это будет означать, что пурпурный цвет в два раза ярче красного. Поэтому используется чистый красный тест. Перейдите в чистый красный #FF0000, установите яркость 50%, и вот мы получаем #FF4040, верно? Я бы предположил сделать 50% яркости красного цвета, мы бы стали темнее, увидев, что он уже полностью яркий, как при #800000яркости 150% #FF8080. Розовый более яркий красный? или красный уже полностью яркий?
Pimp Trizkit
Вы правы - я должен был упомянуть, что р должно быть в диапазоне 1-100!
Торбьерн Йозефссон
# FF00FF имеет значение 255 в красном канале, 0 в зеленом канале и 255 в синем канале. Чем выше объединенные значения в каналах, тем выше яркость цвета. Число p указывает, что мы хотим, чтобы новый цвет был на 50% ярче, чем может быть исходный цвет. Я не на 100% уверен, что # FF4040 - это правильный ответ на «50% как можно ярче красного». Создание более темных оттенков (с, в этом случае, более низким значением в красном канале) потребовало бы модификации
Torbjörn Josefsson
Да, я просто указал на двусмысленность в разговоре о яркости в RGB. Если преобразовать в HSL, Lканал буквально яркость. Моя [личная ментальная] проблема здесь в том, что для меня #FF0000это совершенно ясно. И #FF4040светлее, но не ярче .... для меня свет означает ближе к белому, как розовый. И насколько яркость у него есть, и у него полный красный, такой красный, полный яркий. Следовательно, #FF0000не может быть сделана ярче ... но скорее .. легче ... может быть, я просто урод, лол !! Я действительно не знаю теорию цвета, ооочень, я просто говорю о своем ...
Pimp Trizkit
Но я знаю, что когда я меняю яркость на мониторе, красные не становятся розовыми ... для меня. Так что это, вероятно, где я начал свою логику.
Pimp Trizkit
1

Следующий метод позволит вам осветлить или затемнить значение экспозиции шестнадцатеричной (шестнадцатеричной) цветовой цепочки:

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

}

Для последнего значения параметра в GetHexFromRGB () передайте двойное значение где-то между -1 и 1 (-1 - черный, 0 - без изменений, 1 - белый):

// 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%;
Джейсон Уильямс
источник
0

Как просто оттенить цвет в 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
jsebestyan
источник
Это php-версия ответа Пабло. К сожалению, оно длиннее и медленнее, чем окончательное решение, и оно не позволяет точно осветлить цвета. Это действительно затемняет их точно. Тест с чистым красным (# FF0000), должно быть светлее 25% (# FF4040). Ознакомьтесь с окончанием моего ответа для PHP-версии окончательного решения v2 Кевина М.
Тачка Тризкит
0

Я сделал порт превосходной библиотеки xcolor, чтобы удалить ее зависимость от jQuery. Там есть множество функций, включая светлые и темные цвета.

Действительно, преобразование гекса в RGB - это совершенно отдельная функция от осветления или затемнения цветов. Держите вещи сухими, пожалуйста. В любом случае, если у вас есть цвет RGB, вы можете просто добавить разницу между желаемым уровнем освещения и уровнем освещения для каждого из значений 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)
}

См. Https://github.com/fresheneesz/xolor для получения дополнительной информации.

BT
источник
Я еще не проанализировал код, так как он относится к моему OP (скорость / размер / точность). Но при первом прочтении необходимо сделать несколько комментариев: 1) Я согласен с тем, что преобразование шестнадцатеричного значения в RGB можно рассматривать как совершенно отдельную функцию. ЕСЛИ мою задачу предполагалось решить с помощью сухой функции, которая не требовалась. Намерение здесь состояло в том, чтобы получить ответ (см. Мою версию 2), который был бы очень быстрым и супер крошечным (2 строки!) И который осветил и затемнил шестнадцатеричный цвет ... в частности ... как автономный автономный функция. Так что в конечном итоге это будет простой вызов одной функции. <
Продолжение
2) И в случае с версией 3, по многочисленным просьбам, это намерение иметь полностью автономную автономную универсальную функцию, максимально быструю и минимальную, которая может вслепую принимать цвета в шестнадцатеричном или RGB-формате и во всех их проявлениях. вариации. Следовательно, необходимо преобразовать гекс в RGB. <продолжение>
Сутенер Тризкит
3) После простого анализа кода. Похоже, что он будет работать намного медленнее и, очевидно, будет намного больше, чем моя Версия 2 (что является реальным ответом на мой ОП; версия 3 была для масс). Чтобы быть справедливым, я должен сравнить этот код с моей версией 2 RGB, которая не выполняет преобразование и, кажется, отвечает вашей точке зрения на сухость. И, честно говоря, ваш порт не намного проще для понимания, чем мой 2 лайнер для hex. Так что, хотя его сушилка, на самом деле не намного, если вообще, проще. (сухость не очень помогла для понимания) <продолжение.
Pimp Trizkit
4) Моя версия RGB 2 - это 2-строчная функция без преобразования, если вы этого хотите. Мое конкретное решение для моего оригинального OP хотел hex. Вот почему существует два разных типа Версии 2. Но вы упомянули пункт о сухости и шестнадцатеричном преобразовании, поэтому сейчас мы действительно сосредоточимся на версии 3. Версия 3 появилась намного позже; только после того, как версия 2 стала популярной. <продолжение>
Сутенер Тризкит
5) Хотя я согласен, что сухость вообще помогает универсальности. И в большинстве случаев для понимания. К сожалению, его стоимость в этом примере. Эти затраты заключаются в том, что он намного больше и, по-видимому, намного медленнее и, по-видимому, использует больше памяти как в стеке (с его рекурсивной природой), так и в глобальном (2 функции; по сравнению с v2).
Тачка Тризкит
0

Я давно хотел иметь возможность создавать оттенки / оттенки цветов, вот мое решение 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);
    }
};
user2655360
источник
Некоторые примеры использования могут помочь. И, возможно, какое-то объяснение, почему эта версия выше других. Эта версия, кажется, работает значительно медленнее. И это намного дольше. И, кажется, не затеняет точно. Похоже, вы используете LERP или что-то подобное ... что хорошо. К сожалению, это только из одного канала, то это же значение используется во всех каналах. Это неправильно, для того, чтобы получить более высокую точность, вы должны LERP каждый канал индивидуально. Как мой ответ на этот вопрос. Плюс его меньше и быстрее, он проверяет ошибки и обрабатывает rgb, а также выполняет преобразования, я мог бы продолжить
Pimp Trizkit
Пример использования: varHue ("6e124c", 77) где первый аргумент - это цвет в шестнадцатеричном формате, а второй - процентное изменение. Положительное процентное изменение затеняет (темнеет), а отрицательное значение окрашивает (осветляет) результат. Я написал эту рутину как свою первую попытку всего за несколько часов до того, как наткнулся на эту страницу и опубликовал ее просто как вопрос интереса. Я не знал, что мне нужно улучшить ваши усилия или что мне нужно ваше согласие, прежде чем сделать это. Это полностью моя собственная работа без ссылки на кого-либо еще. Я не слышал о LERP, я проверю это, спасибо за предложение.
user2655360
Хе-хе, ну, конечно, вам не нужно ничего делать! И мы все благодарим вас за ваши усилия! Моими первыми главными проблемами были те, которые перечислены первыми. Попытка помочь вам с вашим ответом, чтобы он мог получить голоса. (показать использование и объяснение того, как это работает, и т. д.) Очевидно, что другой материал - это быстрый анализ, который поможет расширить знания каждого . Извините, если это показалось немного агрессивным. Но другое предложение состоит в том, чтобы заставить его принять #цвета шестиугольника. Извините, если это показалось как "одобрение" ... Я видел это как рецензирование. Если вы не хотите, чтобы кто-то анализировал ваш код или оставил отзыв, прошу прощения.
Pimp Trizkit
0

Ваш подход в порядке :) Я немного упрощаю вашу самую короткую версию (для контроля насыщенности смотрите здесь )

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

И версия с проверкой # и цветовых диапазонов

Камил Келчевски
источник
0

Я тоже сделал простую упаковку. Я использовал выпуклость IR ^ 3, конечно, значения RGB находятся в IN ^ 3, что не является выпуклым, так что это не совсем идеально

Чтобы затемнить я использовал следующее

for (let i = 0; i < rgb.length; i++) {
   rgb[i] = Math.floor(rgb[i] - ratio * rgb[i]);
}

И осветлить

for (let i = 0; i < rgb.length; i++) {
   rgb[i] = Math.ceil(rgb[i] + ratio * (255 - rgb[i]));
}

Вот пакет https://github.com/MarchWorks/colortone

Демо https://colortone.now.sh/

с тем, как я делаю вещи, если вы передадите соотношение -1, вы получите черный, белый, если отношение равно 1. Передача 0, поскольку отношение не изменит цвет

Евгений Фотия
источник
0

Мне нужно было это в C #, это может помочь разработчикам .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;
    }
Нассим
источник
Вы преобразовали «нерабочую» функцию, которая не работает с ведущими нулями и может превышать FF в суммах, = изменить компонент цвета выше ...
B. Перейти
Когда я попробовал эту функцию, она не работала из-за не шестнадцатеричных значений (16 = 0xF) и (8 = 0x8) и дает цвет в 8 позициях, но теперь она работает очень хорошо
Nassim
Вы пытались увеличить от FF? (скажем, от 0xFFFFFF + 0x000004): ваш код переворачивает это максимальное значение (скажем, до 0x1000003), вместо того, чтобы не увеличиваться, и особенно устанавливая 2 верхних цветовых компонента в 00, что является самым большим изменением, которое они могли сделать ...
B. Иди
Вы правы, спасибо за замечание, кроме ввода пределов arround fff и 000 он будет работать нормально.
Насим