JavaScript – Ordenando Elementos por Cores

Olá Web Developer. Hoje vou mostrar como ordenar elementos por cores usando JavaScript. Isso pode ser útil caso você tenha algo como uma galeria de fotos. Além disso também vamos acabar aprendendo como fazer conversão de cores entre os modelos RGB e Hexadecimal.

Eu usei quando fui criar uma imagem para o Instagram mostrando os cursos da TreinaWeb. Criar um código para ordenar os elementos do site e tirar print da tela foi bem mais rápido do que fazer manualmente em um editor de imagem, e teve um resultado melhor do que quando tentei ordenar os elementos do HTML manualmente também.

Como definir a ordem das cores?

O modo mais comum de se ordenar cores é usando o Círculo Cromático, mostrado na imagem abaixo:

Eu já falei sobre ele e sobre como combinar cores aqui no blog. Não se esqueça de dar uma conferida!

É muito comum em programação utilizarmos os modelos RGB ou Hexadecimal para declarar cores, porém, o que vem depois de rgb(32, 15, 59)?.

Desenvolvedor Front-end Sênior
Formação: Desenvolvedor Front-end Sênior
HTML, CSS e JavaScript são a base de toda a web. Tudo o que você está vendo aqui agora depende deste tripé.Nesta formação aprenderemos Sass, Google Analytics, empacotar nossas aplicações com Webpack, criação de aplicações Desktop com Electron, UX/UI e uma introdução aos frameworks mais utilizados no mercado: Angular, React, Vue e Ember.
CONHEÇA A FORMAÇÃO

Também temos o modelo HSL (Hue – Matiz/Tonalidade, Saturation – Saturação, Lightness – Brilho). As cores (vermelho, verde, azul, etc) são definidas pela matiz, a qual é indicada por um ângulo no círculo. Começamos no topo, 0º, que indica o vermelho, e podemos ir mudando de cores conforme vamos indo aos 360º. Isso é bem melhor para usarmos na ordenação, depois de 121 vem 122, simples assim.

Então a primeira coisa que vamos fazer é converter nossas cores para o modelo HSL.

Convertendo Cores para HSL

É mais simples fazer a conversão para HSL a partir do modelo RGB, que possui números de 0 a 255. Se você estiver com cores em Hexadecimal, que utiliza valores entre 00 e FF, basta converter para RGB primeiro.

Hexadecimal para RGB

Os valores entre 00 e FF são basicamente 0 a 255 em hexadecimal. Isso é fácil de converter. Vamos criar uma função que vai pegar cada par de cores do código hexadecimal e retornar como números na base decimal. Então um FF deve retornar 255.

// exemplo de cor: #15FA3D
function hexToRGB(color){
    color = color.replace('#', ''); // vamos remover o #
    // e pegar os valores de Red, Green e Blue
    let red = color.substr(0, 2),
        green = color.substr(2, 2),
        blue = color.substr(4, 2);
    // agora vamos converter a string em número com o parseInt
    // por estar em hexadecimal, indicamos a base 16
    red = parseInt(red, 16);
    green = parseInt(green, 16);
    blue = parseInt(blue, 16);
    // e vamos retornar como um array
    return [red, green, blue];
}
// saída esperada: [21, 250, 61]

Formatando cores RGB

Já temos nossa cor hexadecimal em RGB em um formato de Array, o que nos facilita na hora de pegar cada uma das cores. Mas e se inicialmente já tivermos uma cor em RGB? Vamos criar uma função para formatar cores RGB para o formato que estamos usando.

É comum tanto o uso do RGB quanto o RGBA, então vamos primeiro deixar apenas os números, separar pelas vírgulas e pegar os valores de Red, Green e Blue.

// exemplo de cor: rgba(25, 15, 200, 0.8)
function formatRGB(color){
    // removemos as letras e parênteses
    color = color.replace(/[rgba()]/gi, '');
    // separamos por vírgulas
    color = color.split(',');
    // agora teríamos algo como ["25", " 15", " 200", " 0.8"]
    // vamos pegar cada um dos valores das strings e converter para números
    const red = parseInt(color[0]),
        green = parseInt(color[1]),
        blue = parseInt(color[2]);
    return [red, green, blue];
}
// saída esperada: [25, 15, 200]

Convertendo RGB para HSL

Para chegarmos na parte de ordenação precisamos das cores em HSL. Essa parte é um pouco mais complexa, então eu vou montar a função aos poucos para ir explicando melhor cada pedaço, e no final eu mostro a função inteira. Para não ficar repetindo, as partes já explicadas serão substituídas por ....

Vamos começar simplesmente declarando a função, que vai receber separadamente os valores de Red, Green e Blue.

function RGBToHSL(r, g, b){

}

Os valores de R, G e B sempre serão um número entre 0 e 255. Primeiro temos que transformar esses números em um valor entre 0 e 1. Para isso, basta dividí-los por 255.

function RGBToHSL(r, g, b){
    r /= 255;
    g /= 255;
    b /= 255;
    // lembrando que
    // r/= 255 é igual a
    // r = r / 255;
}

Agora vamos pegar qual o maior e o menor valor entre esses três números. Para isso utilizamos Math.min e Math.max.
Também vamos declarar uma variável chamada delta, que vai indicar a diferença entre o maior e o menor valor, e por último já vamos declarar as variáveis h, s e l, que terão os valores para termos as cores no modelo HSL.

function RGBToHSL(r, g, b){
    ...
    let min = Math.min(r, g, b),
        max = Math.max(r, g, b),
        delta = max - min,
        h = 0,
        s = 0,
        l = 0;
}

Cálculo da Matiz

Agora podemos começar a calcular cada uma das propriedades. Primeiro vamos calcular Hue (matiz). Temos algumas regrinhas para isso:

  • Se todos os valores (r, g e b) são iguais, h deve ser 0.
  • Se a maior cor for Red, a fórmula deve ser ((g – b) / delta) % 6
  • Se a maior cor for Green, a fórmula deve ser (b – r) / delta + 2
  • Se a maior cor for Blue, a fórmula deve ser (r – g) / delta + 4

Podemos utilizar o delta para saber se todas as cores são iguais. Afinal, se o maior valor e o menor são iguais, isso significa que todos os valores são iguais, fazendo o delta ser 0. Então agora, seguindo essas regras, teremos:

function RGBToHSL(r, g, b){
    ...
    if(delta === 0){
        h = 0;
    }else if(max === r){
        h = ((g - b) / delta) % 6;
    }else if(max === g){
        h = (b - r) / delta + 2;
    }else{
        h = (r - g) / delta + 4;
    }
}

Depois de definir o valor de h, multiplicamos por 60 e arrendondamos. Como precisamos de um valor entre 0 e 360, precisamos arrumar caso apareça um valor negativo. Para isso, basta somar 360. Isso significa que -90 resultaria em 270.

function RGBToHSL(r, g, b){
    ...
    h = Math.round(h * 60);
    if(h < 0){
        h += 360;
    }
}

Cálculo do Brilho

Agora vamos calcular Lightness (brilho), já que a saturação depende dele. O cálculo do brilho é a soma do maior valor e do menor valor dividido por 2.

function RGBToHSL(r, g, b){
    ...
    l = (max + min) / 2;
}

Cálculo da Saturação

Agora vamos usar o delta para calcular Saturation (saturação). Se o delta for 0, a saturação também deve ser 0.
Se o valor for diferente de 0 devemos pegar 1 e subtrair o valor absoluto do dobro do brilho menos 1. Valor absoluto é basicamente deixar um número negativo na forma positiva. Então -45 ficaria 45.

function RGBToHSL(r, g, b){
    ...
    s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
}

Finalizando a conversão para HSL

Agora que temos os valores de S e L, mas eles estão entre 0 e 1. Para converter para valores entre 0% e 100% basta multiplicar por 100. Vamos usar .toFixed(1) para limitar um número depois da vírgula. Assim um valor 0.3752 vai se tornar 37.5.

function RGBToHSL(r, g, b){
    ...
    s = +(s * 100).toFixed(1);
    l = +(l * 100).toFixed(1);
}

Nossa função completa ficará assim:

function RGBToHSL(r, g, b){
    r /= 255;
    g /= 255;
    b /= 255;

    let min = Math.min(r, g, b),
        max = Math.max(r, g, b),
        delta = max - min,
        h = 0,
        s = 0,
        l = 0;

    if(delta === 0){
        h = 0;
    }else if(max === r){
        h = ((g - b) / delta) % 6;
    }else if(max === g){
        h = (b - r) / delta + 2;
    }else{
        h = (r - g) / delta + 4;
    }

    h = Math.round(h * 60);
    if(h < 0){
        h += 360;
    }

    l = (max + min) / 2;
    s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));

    s = +(s * 100).toFixed(1);
    l = +(l * 100).toFixed(1);

    return [h, s, l];
}

Como temos nossas cores em um Array, como [25, 15, 200], podemos usar o operador spread para passar esses valores para a função, como:

const color = [25, 15, 200];
RGBToHSL(...color); // retorno: [243, 86, 42.2]
Desenvolvedor React Sênior
Formação: Desenvolvedor React Sênior
Nesta formação conheceremos a Arquitetura Flux, muito utilizada nas aplicações modernas e que nos trás muitos benefícios em relação às arquiteturas clássicas como o MVC. Aprenderemos a sua principal implementação, o Redux. Também veremos a trabalhar com código assíncrono com Redux Thunk e conceito de programação funcional com Redux-Saga.
CONHEÇA A FORMAÇÃO

Ordenando Cores

Agora que temos cores, tanto Hexadecimal quanto RGB, no formato HSL, podemos ordená-las. Como vimos, a matiz é definida pelo primeiro valor no modelo HSL. Então podemos usá-lo para ordenar nossas cores. Para isso basta utilizar o método .sort() dos Arrays e passar uma função para fazer a comparação entre as cores.

let colors = [
    "#fbb735", "#e98931", "#eb403b", "#b32E37", "#6c2a6a",
    "#5c4399", "#274389", "#1f5ea8", "#227FB0", "#2ab0c5",
    "#39c0b3"
];
// convertendo de hexadecimal para hsl
colors = colors.map(color => {
    // de hexadecimal para rgb
    color = hexToRGB(color);
    // de rgb para hsl
    return RGBToHSL(...color);
})

// ordenação pela matiz
colors.sort((a, b) => a[0] - b[0] > 0 ? 1 : -1 );

Resultado

Veja abaixo a implementação. Use os botões para gerar cores e depois para ordenar.

Você vai notar que a ordenação não é 100% perfeita. Isso porque utilizamos apenas a matiz para a ordenação, e o brilho e saturação também são muito importantes para a definição de uma cor. Mas para coisas mais simples esse código funciona muito bem, além de ter servido para aprendermos muitas coisas.

Para mais precisão, prefira utilizar bibliotecas como o color-sorter, onde podemos passar um Array de cores em qualquer formato, até mesmo misturando formatos. Ele também leva em conta coisas como saturação, brilho e transparência.

Veja como o resultado com essa biblioteca é mais preciso:

Há muitas maneiras de ordenar cores, e dependendo do que você quer pode ser necessário dar mais importância para uma propriedade ao invés de outra.

Deixe seu comentário

Graduado em Análise e Desenvolvimento de Sistemas, Pós-graduado em Projetos e Desenvolvimento de Aplicações Web e MBA em Machine Learning. Especializado em Front End e curte desenvolvimento de jogos.

© 2004 - 2019 TreinaWeb Tecnologia LTDA - CNPJ: 06.156.637/0001-58 Av. Paulista, 1765, Conj 71 e 72 - Bela Vista - São Paulo - SP - 01311-200