БлогNot. Сдвиг цвета RGB или какой цвет считать обратным?

Сдвиг цвета RGB или какой цвет считать обратным?

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

Вообще говоря, не подойдёт ни просто "инвертированный" цвет, рассчитанный по принципу X = 255 - X, где X - интенсивность красного, зелёного или синего (потому что, например, цвет #808080 даст практически неотличимый #7f7f7f), ни комплиментарный (complementary) цвет, как раз не считаемый нормально для оттенков серого, которых нет на цветовом круге.

Что напрашивается, так это некая "инверсия" отдельных 16-ричных цифр кода RGB, так что #808080 превратится в #080808. Проверим этот подход для разных цветов:

тесты
тесты

В тесте 1 показана классическая "безопасная палитра" (фоновыми цветами), в тесте 2 - "пошаговый" переход от цвета RGB color1 к цвету color2, где фон - исходного цвета, а буква - "обратного", видно, что для исходных цветов плавный переход организовать нетрудно, обычная пропорция (мысленно вытяните все строки теста в одну, чтобы увидеть это).

В тесте 3 цвет и фон из теста 2 поменялись местами, как видим, операция необратима и плавного перехода фона не будет. Так что цвет фона должен быть "нормальным", а "сдвинутый" можно считать для контрастного цвета текста.

Исходник функции и тестового скрипта, который можно выполнить из файла типа .html в кодировке Юникода UTF-8:

<div id="result1">...</div>
<script>
 function rgbs_invert (r0=0,g0=0,b0=0) { //"контрастный" цвет для rgb(r0,g0,b0)
  function pad2h(i) { return (i.length<2 ? '0' : '')+i; }
  function pads(c) {
   let c0='0123456789abcdef', c1 = '89abcdef01234567';
   return c1[c0.indexOf(c)];

  }
  let r = pad2h(r0.toString(16).toLowerCase()),
      g = pad2h(g0.toString(16).toLowerCase()),
      b = pad2h(b0.toString(16).toLowerCase()),
      r1 = parseInt(pads(r[0])+pads(r[1]),16).toString(10),
      g1 = parseInt(pads(g[0])+pads(g[1]),16).toString(10),
      b1 = parseInt(pads(b[0])+pads(b[1]),16).toString(10);
  return 'rgb('+r1+','+g1+','+b1+')';
 }

 let str = '<style>.cell { padding: 4px; margin: 0; font-family: monospace; }</style>test 1<br>', 
     cnt = 0;
 //1. Все цвета "безопасной" палитры
 for (let r = 0; r <= 255; r+= 51)
 for (let g = 0; g <= 255; g+= 51)
 for (let b = 0; b <= 255; b+= 51) {
  let rgb = 'rgb('+r+','+g+','+b+')';
  let rgbi = rgbs_invert(r,g,b);
  str += '<span class="cell" style="background-color: '+rgb+
   '; color: '+rgbi+'" title="'+rgbi+' on '+rgb+'">T</span>';
  if (++cnt == 24) { str += '<br>'; cnt = 0; }
 }

 str += 'test 2<br>';

 //2. "Пошаговый" переход от одного цвета RGB к другому, фон исходным цветом
 let color1 = [0, 128, 255], color2 = [255, 255, 0];

 function getColor (m,n,x,steps) { 
  //цвет между color1 и color2 на шаге x из steps
  let r = Math.round(m[0]+x*(n[0]-m[0])/steps),
      g = Math.round(m[1]+x*(n[1]-m[1])/steps),
      b = Math.round(m[2]+x*(n[2]-m[2])/steps);
  return [r,g,b];
 }

 let steps = 128;
 for (let x = 0, cnt = 0; x < steps; x++) {
  let rgb = getColor (color1, color2, x, steps),
      rgbs = 'rgb('+rgb[0]+','+rgb[1]+','+rgb[2]+')';
  let rgbi = rgbs_invert(rgb[0],rgb[1],rgb[2]);  
  str += '<span class="cell" style="background-color: '+rgbs+
   '; color: '+rgbi+'" title="'+rgbi+' on '+rgbs+'">T</span>';
  if (++cnt == 16) { str += '<br>'; cnt = 0; }
 }

 str += 'test 3<br>';

 //3. "Пошаговый" переход от одного цвета RGB к другому, фон обратным цветом
 for (let x = 0, cnt = 0; x < steps; x++) {
  let rgb = getColor (color1, color2, x, steps),
      rgbs = 'rgb('+rgb[0]+','+rgb[1]+','+rgb[2]+')';
  let rgbi = rgbs_invert(rgb[0],rgb[1],rgb[2]);  
  str += '<span class="cell" style="background-color: '+rgbi+
   '; color: '+rgbs+'" title="'+rgbs+' on '+rgbi+'">T</span>';
  if (++cnt == 16) { str += '<br>'; cnt = 0; }
 }

 document.getElementById('result1').innerHTML = str;
</script>
<noscript>Включите Javascript для работы примера!</noscript>

Можно сделать контраст между фоном и текстом чуть меньше, заменив тело функции rgbs_invert простым "сдвигом" цветовых компонент на значение 0x80 (128):

 function rgbs_invert (r0=0,g0=0,b0=0) { 
  let r1 = (r0 + 0x80) % 0x100, g1 = (g0 + 0x80) % 0x100, b1 = (b0 + 0x80) % 0x100;
  return 'rgb('+r1+','+g1+','+b1+')';
 }

25.08.2020, 22:29 [1493 просмотра]


теги: javascript цвет алгоритм

К этой статье пока нет комментариев, Ваш будет первым