Crie uma cor hexadecimal com base em uma string com JavaScript

141

Eu quero criar uma função que aceite qualquer string antiga (geralmente será uma única palavra) e a partir disso, de alguma forma, gere um valor hexadecimal entre#000000 e #FFFFFF, para que eu possa usá-la como uma cor para um elemento HTML.

Talvez até um valor hexadecimal abreviado (por exemplo:), #FFFse isso for menos complicado. De fato, uma cor de uma paleta "segura para a Web" seria ideal.

Darragh Enright
fonte
2
Poderia dar uma amostra de entrada e / ou links para perguntas semelhantes?
Qw3n
2
Não é uma resposta, mas você pode achar útil o seguinte: Para converter um hexadecimal em um número inteiro, use parseInt(hexstr, 10). Para converter um número inteiro em um hexadecimal, use n.toString(16), onde n é um número inteiro.
Cristian Sanchez
@ qw3n - exemplo de entrada: apenas pequenas e simples strings de texto antigas ... como 'Medicina', 'Cirurgia', 'Neurologia', 'Clínica Geral' etc. Variando entre 3 e digamos, 20 caracteres ... não consigo encontrar o outro, mas aqui está a questão do java: stackoverflow.com/questions/2464745/… @Daniel - Obrigado. Eu preciso sentar e ter outra chance séria nisso. poderia ser útil.
Darragh Enright

Respostas:

176

Apenas migrando o código de cores hexadecimal do Java from Compute para uma string arbitrária para Javascript:

function hashCode(str) { // java String#hashCode
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
       hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    return hash;
} 

function intToRGB(i){
    var c = (i & 0x00FFFFFF)
        .toString(16)
        .toUpperCase();

    return "00000".substring(0, 6 - c.length) + c;
}

Para converter você faria:

intToRGB(hashCode(your_string))
Cristian Sanchez
fonte
1
ótimo! obrigado, isso funciona bem. Eu não sei muito sobre operadores e outras coisas bit a bit, então sua ajuda é apreciada.
Darragh Enright
Ele precisa preencher as cadeias hexadecimais, como:("00" + ((this >> 24) & 0xFF).toString(16)).slice(-2) + ("00" + ((this >> 16) & 0xFF).toString(16)).slice(-2) + ("00" + ((this >> 8) & 0xFF).toString(16)).slice(-2) + ("00" + (this & 0xFF).toString(16)).slice(-2);
Timina
2
Estou convertendo um monte de tags de gênero musical em cores de fundo e isso me salvou muito tempo.
Kyle Pennell
Eu gostaria de poder converter isso para php.
Nimitz E.
6
Eu tenho alguns problemas com quase mesmas cores para cordas semelhantes, por exemplo: intToRGB(hashCode('hello1')) -> "3A019F" intToRGB(hashCode('hello2')) -> "3A01A0" E eu melhorar seu código adicionando multiplicação de valor final de hash:return 100 * hash;
SirWojtek
185

Aqui está uma adaptação da resposta de CD Sanchez que retorna consistentemente um código de cores de 6 dígitos:

var stringToColour = function(str) {
  var hash = 0;
  for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  var colour = '#';
  for (var i = 0; i < 3; i++) {
    var value = (hash >> (i * 8)) & 0xFF;
    colour += ('00' + value.toString(16)).substr(-2);
  }
  return colour;
}

Uso:

stringToColour("greenish");
// -> #9bc63b

Exemplo:

http://jsfiddle.net/sUK45/

(Uma solução alternativa / mais simples pode envolver o retorno de um código de cores no estilo 'rgb (...)'.)

Joe Freeman
fonte
3
Esse código funciona muito bem em conjunto com os IDs gerados automaticamente pelo NoSQL; sua cor será sempre a mesma para o mesmo usuário.
deviavir
Eu também precisava do canal alfa para obter transparência nos meus códigos hexadecimais. Isso ajudou (adição de dois dígitos para o canal alfa no final do meu código hex): gist.github.com/lopspower/03fb1cc0ac9f32ef38f4
Husterknupp
@Tjorriemorrie Voto positivo por apontar que é cor e não cor. Sim, sim, não é realmente sobre o assunto, mas é algo que é importante para mim (na verdade, ao digitá-lo originalmente, escrevi 'cor' nas duas vezes!). Obrigado.
Pryftan
Interessante que a cor seja diferente para a mesma string em diferentes navegadores / sistemas operacionais - por exemplo, Chrome + Windows e Chrome + Android - meu e-mail => cor é azul em um e verde no outro. Alguma idéia do porquê?
avenmore
43

Eu queria uma riqueza de cores semelhante para elementos HTML, fiquei surpreso ao descobrir que o CSS agora suporta cores hsl (), então uma solução completa para mim está abaixo:

Veja também Como gerar automaticamente N cores "distintas"? para mais alternativas mais semelhantes a isso.

function colorByHashCode(value) {
    return "<span style='color:" + value.getHashCode().intToHSL() + "'>" + value + "</span>";
}
String.prototype.getHashCode = function() {
    var hash = 0;
    if (this.length == 0) return hash;
    for (var i = 0; i < this.length; i++) {
        hash = this.charCodeAt(i) + ((hash << 5) - hash);
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
};
Number.prototype.intToHSL = function() {
    var shortened = this % 360;
    return "hsl(" + shortened + ",100%,30%)";
};

document.body.innerHTML = [
  "javascript",
  "is",
  "nice",
].map(colorByHashCode).join("<br/>");
span {
  font-size: 50px;
  font-weight: 800;
}

No HSL sua Matiz, Saturação, Luminosidade. Portanto, o tom entre 0 e 359 terá todas as cores, a saturação é o quão rica você deseja a cor, 100% funciona para mim. E a luminosidade determina a profundidade, 50% é normal, 25% é cores escuras, 75% é pastel. Tenho 30% porque se encaixa melhor no meu esquema de cores.

Timina
fonte
3
Uma solução muito versátil.
MastaBaba
2
Thx por compartilhar uma solução, onde você pode decidir como as cores devem ser coloridas!
Florian Bauer
@haykam Obrigado por torná-lo um snippet!
Timina 15/10
Essa abordagem é realmente útil para fornecer a vibração / sutileza desejada que meu aplicativo precisava. Um Hex aleatório varia muito em saturação e brilho para ser útil na maioria das situações. Obrigado por isso!
simey.me 18/03/19
Esta solução retorna poucas cores, não funciona.
catamphetamine 20/03/19
9

Acho que gerar cores aleatórias tende a criar cores que não têm contraste suficiente para o meu gosto. A maneira mais fácil que encontrei de contornar isso é preencher previamente uma lista de cores muito diferentes. Para cada nova sequência, atribua a próxima cor na lista:

// Takes any string and converts it into a #RRGGBB color.
var StringToColor = (function(){
    var instance = null;

    return {
    next: function stringToColor(str) {
        if(instance === null) {
            instance = {};
            instance.stringToColorHash = {};
            instance.nextVeryDifferntColorIdx = 0;
            instance.veryDifferentColors = ["#000000","#00FF00","#0000FF","#FF0000","#01FFFE","#FFA6FE","#FFDB66","#006401","#010067","#95003A","#007DB5","#FF00F6","#FFEEE8","#774D00","#90FB92","#0076FF","#D5FF00","#FF937E","#6A826C","#FF029D","#FE8900","#7A4782","#7E2DD2","#85A900","#FF0056","#A42400","#00AE7E","#683D3B","#BDC6FF","#263400","#BDD393","#00B917","#9E008E","#001544","#C28C9F","#FF74A3","#01D0FF","#004754","#E56FFE","#788231","#0E4CA1","#91D0CB","#BE9970","#968AE8","#BB8800","#43002C","#DEFF74","#00FFC6","#FFE502","#620E00","#008F9C","#98FF52","#7544B1","#B500FF","#00FF78","#FF6E41","#005F39","#6B6882","#5FAD4E","#A75740","#A5FFD2","#FFB167","#009BFF","#E85EBE"];
        }

        if(!instance.stringToColorHash[str])
            instance.stringToColorHash[str] = instance.veryDifferentColors[instance.nextVeryDifferntColorIdx++];

            return instance.stringToColorHash[str];
        }
    }
})();

// Get a new color for each string
StringToColor.next("get first color");
StringToColor.next("get second color");

// Will return the same color as the first time
StringToColor.next("get first color");

Embora isso tenha um limite para apenas 64 cores, acho que a maioria dos humanos não pode realmente dizer a diferença depois disso. Suponho que você sempre pode adicionar mais cores.

Embora esse código use cores codificadas, você tem pelo menos a garantia de saber durante o desenvolvimento exatamente quanto contraste verá entre as cores na produção.

A lista de cores foi removida desta resposta para que SO , existem outras listas com mais cores.

Rick Smith
fonte
Fwiw, existe um algoritmo para determinar o contraste. Eu escrevi algo com isso anos atrás (mas em C). Muito se preocupa com isso, e de qualquer maneira é uma resposta antiga, mas achei que eu apontaria que há uma maneira de determinar o contraste.
Pryftan
7

Abri uma solicitação de recebimento no Please.js que permite gerar uma cor a partir de um hash.

Você pode mapear a sequência para uma cor da seguinte maneira:

const color = Please.make_color({
    from_hash: "any string goes here"
});

Por exemplo, "any string goes here"retornará como "#47291b"
e "another!"retornará como"#1f0c3d"

Josue Alexander Ibarra
fonte
Muito legal obrigado por adicionar isso. Oi querendo gerar círculos com letras na base em um nome como o Google caixa de entrada faz :)
marcus7777
quando vi essa resposta, pensei, perfeita, agora tenho que pensar no esquema de cores para que não gere cores muito aleatórias. Depois, li sobre as opções de make.color do Please.js e ele colocou um lindo sorriso na minha cara.
Panchicore
6

Se suas entradas não forem diferentes o suficiente para que um hash simples use todo o espectro de cores, você poderá usar um gerador de números aleatórios semeados em vez de uma função de hash.

Estou usando o codificador de cores da resposta de Joe Freeman e o gerador de números aleatórios semeados de David Bau .

function stringToColour(str) {
    Math.seedrandom(str);
    var rand = Math.random() * Math.pow(255,3);
    Math.seedrandom(); // don't leave a non-random seed in the generator
    for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((rand >> i++ * 8) & 0xFF).toString(16)).slice(-2));
    return colour;
}
Nathan
fonte
5

Outra solução para cores aleatórias:

function colorize(str) {
    for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
    color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16);
    return '#' + Array(6 - color.length + 1).join('0') + color;
}

É uma mistura de coisas que faz o trabalho para mim. Eu usei a função JFreeman Hash (também uma resposta neste tópico) e a função pseudo-aleatória Asykäri daqui e alguns preenchimentos e matemática de mim mesmo.

Duvido que a função produz cores uniformemente distribuídas, embora pareça agradável e faça o que deve ser feito.

estani
fonte
'0'.repeat(...)javascript não é válido
kikito 21/05
@ kikito justo o suficiente, provavelmente eu tinha o protótipo estendido de alguma forma (JQuery?). Enfim, editei a função para que seja apenas javascript ... obrigado por apontar isso.
estani
@kikito é ES6 válido, embora usá-lo estaria negligenciando a compatibilidade entre navegadores.
Patrick Roberts
5

Usando o hashCodecomo na resposta de Cristian Sanchez com hsljavascript moderno, você pode criar um seletor de cores com bom contraste como este:

function hashCode(str) {
  let hash = 0;
  for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
}

function pickColor(str) {
  return `hsl(${hashCode(str) % 360}, 100%, 80%)`;
}

one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
  padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>

Como é hsl, você pode dimensionar a luminância para obter o contraste que procura.

function hashCode(str) {
  let hash = 0;
  for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
}

function pickColor(str) {
  // Note the last value here is now 50% instead of 80%
  return `hsl(${hashCode(str) % 360}, 100%, 50%)`;
}

one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
  color: white;
  padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>

Kyle Kelley
fonte
4

Aqui está uma solução que eu criei para gerar cores pastel esteticamente agradáveis ​​com base em uma string de entrada. Ele usa os dois primeiros caracteres da sequência como uma semente aleatória e gera R / G / B com base nessa semente.

Ele pode ser facilmente estendido para que a semente seja o XOR de todos os caracteres da string, em vez de apenas os dois primeiros.

Inspirado pela resposta de David Crow aqui: Algoritmo para gerar aleatoriamente uma paleta de cores esteticamente agradável

//magic to convert strings to a nice pastel colour based on first two chars
//
// every string with the same first two chars will generate the same pastel colour
function pastel_colour(input_str) {

    //TODO: adjust base colour values below based on theme
    var baseRed = 128;
    var baseGreen = 128;
    var baseBlue = 128;

    //lazy seeded random hack to get values from 0 - 256
    //for seed just take bitwise XOR of first two chars
    var seed = input_str.charCodeAt(0) ^ input_str.charCodeAt(1);
    var rand_1 = Math.abs((Math.sin(seed++) * 10000)) % 256;
    var rand_2 = Math.abs((Math.sin(seed++) * 10000)) % 256;
    var rand_3 = Math.abs((Math.sin(seed++) * 10000)) % 256;

    //build colour
    var red = Math.round((rand_1 + baseRed) / 2);
    var green = Math.round((rand_2 + baseGreen) / 2);
    var blue = Math.round((rand_3 + baseBlue) / 2);

    return { red: red, green: green, blue: blue };
}

O GIST está aqui: https://gist.github.com/ro-sharp/49fd46a071a267d9e5dd

Robert Sharp
fonte
Devo dizer que esta é uma maneira realmente estranha de fazê-lo. É meio que funciona, mas não há muitas cores disponíveis. O XOR das duas primeiras cores não faz distinção de ordem; portanto, existem apenas combinações de letras. Uma adição simples que fiz para aumentar o número de cores foi var seed = 0; para (var i em input_str) {semente ^ = i; }
Gussoh
Sim, depende realmente de quantas cores você gostaria de gerar. Lembro-me de, neste caso, eu estava criando diferentes painéis em um UI e queria um número limitado de cores em vez de um arco-íris :)
Robert afiada
1

Aqui está outra tentativa:

function stringToColor(str){
  var hash = 0;
  for(var i=0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 3) - hash);
  }
  var color = Math.abs(hash).toString(16).substring(0, 6);

  return "#" + '000000'.substring(0, 6 - color.length) + color;
}
kikito
fonte
1

Tudo o que você realmente precisa é de uma boa função de hash. No nó, eu apenas uso

const crypto = require('crypto');
function strToColor(str) {
    return '#' + crypto.createHash('md5').update(str).digest('hex').substr(0, 6);
}
PulseJet
fonte
0

Eu converto isso para Java.

Tanques para todos.

public static int getColorFromText(String text)
    {
        if(text == null || text.length() < 1)
            return Color.BLACK;

        int hash = 0;

        for (int i = 0; i < text.length(); i++)
        {
            hash = text.charAt(i) + ((hash << 5) - hash);
        }

        int c = (hash & 0x00FFFFFF);
        c = c - 16777216;

        return c;
    }
Ali Bagheri
fonte
-1

Esta função faz o truque. É uma adaptação disso, implementação bastante mais longa neste repositório ..

const color = (str) => {
    let rgb = [];
    // Changing non-hexadecimal characters to 0
    str = [...str].map(c => (/[0-9A-Fa-f]/g.test(c)) ? c : 0).join('');
    // Padding string with zeroes until it adds up to 3
    while (str.length % 3) str += '0';

    // Dividing string into 3 equally large arrays
    for (i = 0; i < str.length; i += str.length / 3)
        rgb.push(str.slice(i, i + str.length / 3));

    // Formatting a hex color from the first two letters of each portion
    return `#${rgb.map(string => string.slice(0, 2)).join('')}`;
}
Theódór Ágúst Magnússon
fonte
Isso gera muitos valores muito escuros.
Langdon