Програмно освітлити або затемнити шестигранний колір (або rgb, і змішати кольори)


503

Ось функція, над якою я працював, щоб програмно освітлити або затемнити шестигранний колір на певну кількість. Просто введіть рядок, як "3F6D2A"для color ( col) та base10 ціле число ( 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 для відтінку призведе до отримання приємних апельсинів через позатіновий діапазон. Подивіться на кольорову діаграму нижче і повідомте мені, чи діапазон відтінків не дуже близький до фактично точного.
Сутенер Трізкіт

Я трохи заплутався в структурі тут, але ви маєте рацію, помаранчеві рівні для shadowColor1 здаються дуже хорошими.
Генрік

Lol, ти маєш на увазі абажур Color2. Я здогадуюсь, що структура, про яку ви говорите, - це загальна компонування самої відповіді? Будь-які підказки зробити більш зрозумілими?
Сутенер Трізкіт

3
Існує лише одна проблема у функції з # нагорі - це те, що вона не створює провідні нулі, якщо кінцевий шістнадцятковий код починається з нулів. Наприклад, якщо шістнадцятковий код # 00a6b7, він виведе його як # a6b7, який не працюватиме, якщо використовувати його як css. Ви можете це виправити, замінивши зворотний рядок цим: var string = "000000" + (g | (b << 8) | (r << 16)). ToString (16); return (usePound? "#": "") + string.substr (string.length-6);
Рафаель Леві

Відповіді:


876

Що ж, ця відповідь стала власним звіром. Багато нових версій, це стало дурно довго. Велике спасибі всім дуже великим учасникам цієї відповіді. Але для того, щоб це було просто для мас. Я заархівував усі версії / історію розвитку цієї відповіді на свій github . І почав це з чистого на StackOverflow тут, з новітньою версією. Особлива подяка висловлена ​​Майку "Pomax" Камермансу за цю версію. Він дав мені нову математику.


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

Це працює дуже швидко, мабуть, найшвидше, особливо враховуючи його безліч особливостей. Це було тривалий час у створенні. Подивіться всю історію на моєму github . Якщо ви хочете зробити найменший і швидкий можливий спосіб відтінити або змішати, перегляньте мікрофункції нижче та скористайтеся одним із демонів швидкості на 2 вкладиші. Вони чудово підходять для інтенсивної анімації, але ця версія тут досить швидка для більшості анімацій.

Ця функція використовує змішування журналу або лінійне змішування. Однак він НЕ перетворюється на HSL, щоб правильно освітлити або затемнити колір. Тому результати цієї функції будуть відрізнятися від тих значно більших та повільніших функцій, які використовують HSL.

jsFiddle з pSBC

github> pSBC Wiki

Особливості:

  • Автоматично виявляє та приймає стандартні шістнадцяткові кольори у вигляді рядків. Наприклад: "#AA6622"або "#bb551144".
  • Автоматично визначає та приймає стандартні кольори RGB у вигляді рядків. Наприклад: "rgb(123,45,76)"або "rgba(45,15,74,0.45)".
  • Відтінки кольорів до білого або чорного у відсотках.
  • Змішує кольори разом у відсотках.
  • Перетворює Hex2RGB і RGB2Hex одночасно або окремо.
  • Приймає тризначні (або чотиризначні ш / альфа) коди кольорів 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може передаватися шістнадцятковий або RGB-колір, і він повертає об'єкт, що містить цю інформацію про колір. Його у вигляді: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Де .r, .gі .bмають діапазон від 0 до 255. А коли немає альфа: .aє -1. В іншому випадку: .aмає діапазон від 0,000 до 1 000.
  • Для виведення RGB, він виводить rgba()більш , rgb()коли колір з альфа - каналом був переданий в c0(з) і / або c1(с).
  • Додано незначну перевірку помилок. Це не ідеально. Він все ще може вийти з ладу або створити ритм. Але це зловить деякі речі. В основному, якщо структура якимось чином помиляється або якщо відсоток не є числом чи не виходить за межі, вона повернеться null. Приклад:, pSBC(0.5,"salt") == nullде, як він думає, #saltє дійсним кольором. Видаліть чотири рядки, якими закінчується, return null;щоб видалити цю функцію та зробити її швидшою та меншою.
  • Використання змішування журналу. Перейдіть trueна l(четвертий параметр) для використання лінійного змішування.

Код:

// 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 пише занадто повільно і містить занадто багато ароматів для простого дволанкового (IE. Це може бути 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
Matej Ukmar

2
Ось версія PHP для оновленої версії тениColor2: 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. Тож там можуть бути якісь цифри, які не закручуються точно так само, як мій приклад тут.
Сутенер Трізкіт

121

Я прийняв рішення, яке працює для мене дуже добре:

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

Приклад затемнення:

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

4
Приємно, мені подобається відсоток! +1 Тхо, я б R = ((R<255)?R:255).toString(16);тоді міг би зробити це R = R.length==1 ? "0"+R : Rдля швидкості. І я не впевнений у точці toUpperCase?
Сутенер Тризкіт

Це зайве. Я додаю лише це для симпатичного друку під час тесту. Я це відредагую.
Пабло

Дуже хороший. Однак чи повинен 100% світліший не стати повністю білим, а 100% темним завжди чорним, незалежно від того, якого кольору? здається -100 робить будь-який колір чорним, але 100 (позитивний) не робить його повністю білим.
Кевін М

4
не працює з суцільними кольорами, як # ff0000, # 00ff00, # 0000ff
Хіторі

Щоб він працював з чорним кольором, я щойно зробив цей злом 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"

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;

Можливо, це не так приємно, але це робить роботу. Відмінна функція, BTW. Тільки те, що мені було потрібно. :)


1
Дякуємо за налагодження та комплімент! Шкода, що це не відповідь на те, чи існує швидший шлях, це моє головне питання. Можливо, такий, який використовує всі шістнадцяткові і без базових перетворень. Тьо, я думаю, ти мені сказав, чи маю я правильний код (+1). На жаль, виправлення додало значно більше накладних витрат (тепер ваш дзвінок вString 6 разів) і трохи менше KISS. Можливо, було б швидше перевірити, чи є базове число 10 15 або менше, перш ніж базове перетворення. Але мені подобається!
Сутенер Трізкіт

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 для джерела сторінки або суті

Крім того, ще одне питання StackOverflow може бути хорошим місцем для пошуку.


Незважаючи на те, що це не правильний вибір для ОП, наступне - це наближення коду, який я спочатку пропонував. (Припустимо, що у вас є функції перетворення 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? Здається, що це складніше. Можливо, мені чогось не вистачає. Але я шукаю спосіб KISS зробити це якнайшвидше (час виконання). Я відчуваю себе в ідеалі, якби я міг зробити це все в шістнадцятку, це було б найшвидше. Але розроблене тут рішення передбачає перехід на rgb, щоб мати можливість додати додаткову суму.
Сутенер Трізкіт

Так, я припускаю, що це буде повільніше, складніше, і якщо ви не використовуєте rgb для перетворення hsl в іншому місці, то, ймовірно, це не буде найбільш спрощеним рішенням. Однак це було б точніше, ніж додавання значень rgb, хоча я сам не дуже кольоровий. Все залежить від того, наскільки точним ви хочете бути, я думаю.
Джеймс Хоурі

Про яку втрату точності ви згадуєте? Я припускаю, ви маєте на увазі, що всі кольори [веб] не доступні за допомогою rgb чи чогось іншого?
Сутенер Трізкіт

Як я вже говорив, я не так багато знаю про колір: wiki Теорія кольорів
Джеймс Хоурі

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

5
Ніколи раніше не використовувався C #, але, схоже, три останні декларації змінної є вільними. intІ два varsдля одного типу даних.
Сутенер Тризкіт

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% червону, ми стали б темнішими, побачивши, що її вже повністю яскрава .. як і в #800000150% яскравості #FF8080. Чи рожевий яскравіше червоний? або червоний колір вже повністю яскравий?
Сутенер Тризкіт

Ви маєте рацію - я мав би зазначити, що р повинен бути в межах 1-100!
Торбьорн Йозефссон

# FF00FF має значення 255 в червоному каналі, 0 в зеленому каналі і 255 в синьому каналі. Чим вище комбіновані значення каналів, тим вище яскравість кольору. Цифра p вказує, що ми хочемо, щоб новий колір був на 50% таким же яскравим, як може бути початковий колір. Я не на 100%, що # FF4040 є правильною відповіддю на "50% якомога яскравішого червоного". Вироблення більш темних відтінків (у цьому випадку з нижчим значенням у червоному каналі) потребує модифікації
Torbjörn Josefsson

Так, я просто вказував на неоднозначність, коли говорив про яскравість у RGB. Якщо перетворити на HSL, Lканал має буквальну яскравість. Моє [особисте психічне] питання тут полягає в тому, що для мене #FF0000це абсолютно яскраво. І #FF4040легше, але не яскравіше .... для мене світліше означає ближче до білого, як рожевий. А яскравість - це стільки, скільки вона отримала, і її отримала повністю червона, така червона, повна яскрава. Тому #FF0000не можна зробити яскравішим .. а скоріше .. легшим ... можливо я просто виродком, хаха !! Я насправді не знаю теорії кольорів, так що, я справді просто розмовляю зі своїм ...
Сутенер Тризкіт

Але я знаю, що коли я міняю яскравість на моніторі, Червоні не стають рожевими ... для мене. Тож, мабуть, саме тут я і почав свою логіку.
Сутенер Трізкіт

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

Це 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 .


Я ще не проаналізував код, оскільки він стосується моєї ОП (швидкість / розмір / точність). Але спочатку читайте, що слід зробити кілька коментарів: 1) Я погоджуюся, що перетворення шестигранника в RGB можна розглядати як абсолютно окрему функцію. ЯКЩО моя проблема мала вирішитись із сухою функцією, яка не була вимогою. Наміром тут було отримати відповідь (див. Мою версію 2), яка була дуже швидкою і супер крихітною (2 рядки!) І такою, яка світлішала і затемняла шестигранний колір ... конкретно ... як окремий автономний функція. Так що, в своєму остаточному використанні, це буде простий виклик однієї функції. <Прод.>
Pimp Trizkit

2) І справа з Версією 3, за потребою населення, полягає у намірі мати повністю автономну універсальну функцію, якомога швидшу та якомога меншу, яка може сліпо прийняти шестигранний або RGB-колір і у всіх їхніх варіації. Отже, потрібна конверсія шістнадцяткових в RGB. <продовження>
Сутенер Трізкіт

3) При простому аналізі коду. Здається, вона працюватиме набагато повільніше, і, очевидно, набагато більша, ніж моя версія 2 (що є реальною відповіддю на мою ОП; версія 3 була для мас). Справедливості я повинен порівняти цей код з моєю версією 2 RGB, яка не робить перетворення і, здається, відповідає вашій точці щодо сухості. І правдиво кажучи, ваш порт не набагато простіший для розуміння, ніж мій 2 лайнера для шестигранника. Отже, хоча його сушарка, її насправді не так вже й багато, якщо вона є, простішою. (сухість не дуже допомогла для розуміння) <продовження>
Сутенер Трицкіт

4) Моя версія RGB 2 - це функція рядка без перетворення 2, якщо ви цього хочете. Моє конкретне рішення для мого оригінального OP хотів шістнадцятковий. Ось чому існує два різних типи версії 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);
    }
};

Деякі приклади використання допоможуть. І може бути якесь пояснення, чому ця версія над іншими. Ця версія, здається, працює значно повільніше. І це набагато довше. І це, здається, не затінює точно. Це схоже на те, що ти використовуєш LERP, або щось подібне .. що добре. На жаль, це лише один канал, то те саме значення використовується у всіх каналах. Це неправильно, щоб отримати більш високу точність, вам слід LERP кожен канал окремо. Як і моя відповідь на це питання. Плюс його менші та швидші та перевіряє на помилки та обробляє rgb та робить конверсії, я міг би продовжувати
Pimp Trizkit

Приклад використання: varyHue ("6e124c", 77), де перший аргумент - це колір у шістнадцятковій формі, а другий - процентне змінення. Позитивний відсоток зміни відтінків (темніє), а негативне значення відтіняє (освітлює) результат. Я написав процедуру як свою першу спробу лише за кілька годин до того, як зайшов на цю сторінку і розмістив її просто як цікавий предмет. Я не усвідомлював, що я повинен докладати більше зусиль або вимагати вашого схвалення, перш ніж робити це. Це цілком моя власна робота без посилання на когось іншого. Я не чув про ЛЕРП. Я перевірю це, дякую за пропозицію.
user2655360

Хе-х, ну, звичайно, нічого робити не треба! І всі ми дякуємо за ваші зусилля! Мої перші головні проблеми були першими, переліченими. Намагаюся допомогти вам у відповіді, щоб вона могла отримати голоси. (покажіть звичаї та пояснення, як це працює тощо). Інші речі, очевидно, - це швидкий аналіз, який допоможе поглибити всі знання. Вибачте, якщо це здалося трохи агресивним. Але ще одна пропозиція - змусити його приймати #шестигранні кольори. Вибачте, якщо це здалося .. "схваленням" ... Я бачив це як рецензування. Якщо ви не хочете, щоб хтось аналізував ваш код або пропонував відгуки, прошу вибачення.
Сутенер Трізкіт

0

З вашим підходом нормально :) Я трохи спрощую вашу найкоротшу версію (для контролю насичення дивіться тут )

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

І версія з перевіркою діапазону # та кольорів


0

Я теж зробив простий пакет. Я використовував опуклість ІК ^ 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 позиціях, але зараз вона працює дуже добре
Нассім,

ви намагалися збільшити з FF? (Скажімо , від 0xFFFFFF + 0x000004): ваш код зашкалює , що максимальне значення (скажімо , до 0x1000003), а не збільшується, і особливо установка 2 верхніх компонентів кольору 00 , який є найбільшим зміною вони можуть зробити ...
B. Їдьте

Ви маєте рацію, дякую за зауваження, за винятком введення обмежень навколо fff та 000, це буде добре працювати.
Насім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.