Buceando en JavaScript: Cómo crear un convertidor de color Hex2RGB

Actualización (23/07/2019): He corregido algunos errores gramaticales y cambiado un poco el código app.js eliminando la función checkBG.

En este artículo, crearemos una aplicación web que convierte códigos de color entre la forma hexadecimal y la forma RGB.

Puede encontrar una demostración aquí y el código fuente aquí.

Estructura del proyecto:

La estructura del proyecto es bastante simple.

  1. index.html : Contiene la estructura de la aplicación.
  2. style.css : Aplica estilo a la página.
  3. app.js : Contiene todo el código mágico.

Idea:

Aquí está la lista de cosas que quería que realizara esta aplicación:

  1. Siempre que se escribe algo en un campo de texto para hexadecimal, la aplicación debe verificar si el color es válido. Si es así, conviértalo a RGB, configúrelo como fondo y luego coloque el valor RGB en el campo de texto RGB y viceversa.
  2. Si se escribe un código de color hexadecimal corto en el campo de texto, amplíelo cuando el campo de texto pierda el foco (el usuario hace clic fuera del área de texto).
  3. Anteponer automáticamente el símbolo '#' a la entrada hexadecimal.

¡Vamos a empezar!

index.html

      Hex to RGB Converter HEX <--> RGB 

Creamos dos campos de texto con id de 'hex' y 'rgb' respectivamente. Junto a cada entrada hay un icono SVG de error, que tiene una clase de oculto, de forma predeterminada.

style.css

:root { --color: rgba(255,255,255,0.9); --tweet: white; } * { margin: 0; padding: 0; box-sizing: border-box; } ::placeholder { color: var(--color)!important; } body { padding: 50px; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #28a745; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; } .head { position: absolute; top: 30px; text-align: center; color: var(--tweet); font-size: 3rem; border-bottom: 2px solid var(--tweet); } #content { display: block; } input { color: var(--color)!important; margin: 1rem 0; width: 400px; border: none; border-bottom: 1px solid var(--color); font-size: 2.5rem; background-color: transparent; } input:focus { outline: none; } img { width: 24px; } .hidden { visibility: hidden; opacity: 0.8; } .dark { --color: rgba(0,0,0,0.75); --tweet: rgba(0,0,0,0.95); } @media only screen and (max-width: 560px){ #content input { margin: 0.75rem 0; width: 90%; font-size: 1.875rem; } #content img { width: 16px; } .head { font-size: 2rem; } } 

Aquí hay un diseño básico para que el marcado se vea un poco mejor. Hemos definido dos clases aquí, .hiddeny .dark..hiddense utiliza para ocultar / mostrar el icono de error SVG y .darkes para cambiar el color del texto según el color de fondo. De forma predeterminada, configuré el texto en un color oscuro (para fondos brillantes).

app.js

Aquí está la parte mágica. Desglosaré el código en trozos:

Primero, hemos definido variables que apuntan a las entradas con id 'hex' y 'rgb'. A continuación, tenemos funciones para verificar si la entrada Hex / RGB es válida o no. Usan una configuración básica de expresiones regulares y devuelven un valor booleano. Si te intimidan, te recomiendo que pruebes este RegexTutorial.

Aquí, escribimos una función de análisis llamada modifyHexque verifica si el hexadecimal de entrada tiene 4 caracteres; es decir, contiene '#' y es una abreviatura (por ejemplo, # 333) y reemplaza el '#' con un carácter vacío. Luego verifica si la longitud ahora es 3 y la expande a 6 caracteres (por ejemplo, # 123 = # 112233).

Hemos definido dos funciones que convierten hexadecimal en rgb y viceversa. Aquí hay un desglose paso a paso de hexToRgb(este proceso está escrito en forma expandida para una mejor comprensión):

  1. Defina una matriz vacía para almacenar el resultado.
  2. Reemplace el símbolo '#', si existe, y si la longitud no es igual a 6 (es decir, la versión abreviada), llame a la modifyHexfunción anterior y amplíela.
  3. De una manera muy básica, hexadecimal a rgb funciona convirtiendo el código hexadecimal (en base 16) a código rgb (en base 10). Cada dos caracteres en el código hexadecimal representa un valor en el código de color rgb. Por ejemplo, en #aabbcc, el rojo es (aa a base 10), el verde es (bb a base 10) y el azul es (cc a base 10). Entonces, en la función, estamos dividiendo el valor hexadecimal, convirtiéndolo a base 10 usando parseInty luego almacenándolo en la matriz definida.
  4. Finalmente, estamos devolviendo la cadena de salida uniendo la matriz anterior.

Para la rgbToHexfunción (esto está escrito con una lógica más corta):

  1. Estamos usando directamente una expresión regular para extraer solo los valores numéricos, es decir, rgb (123,21,24) devolverá 123,21,24.
  2. A continuación, estamos usando una función de mapa para devolver una nueva matriz, que convierte el número en base 16 y luego rellena el valor.

La expresión regular que usamos anteriormente devuelve datos de tipo 'cadena'. Para convertirlo a Base 16, tenemos que usar el toString()  método, con un parámetro de '16'.

Ahora, el toString()método es aplicable solo a tipos de datos numéricos, por lo que usamos parseIntpara convertir primero cada elemento de la matriz en un número, luego usamos toString(16)para convertirlo a forma hexadecimal y finalmente agregamos relleno para que tenga exactamente 2 caracteres de longitud. El relleno es necesario, si tiene algo como '14', que desea convertir a hexadecimal, devolverá 'e'. Pero el código de color hexadecimal necesita 2 caracteres para cada parte, por lo que se requiere relleno, lo que lo convierte en '0e'.

Nota: padStartes una función de ES8, que podría no ser compatible con todos los navegadores. Para mantener este tutorial simple, no lo he transpilado a ES5.

3. Finalmente, estamos devolviendo la matriz resultante uniéndola y convirtiéndola a mayúsculas.

errorMark()La función se utiliza para mostrar u ocultar el icono de error SVG. Simplemente pasa el contenido de la entrada ( hex.valuey rgb.value) a través de sus respectivas funciones de verificación y usa el booleano devuelto para agregar / eliminar la .hiddenclase.

Ahora estamos definiendo una función que toma el color de fondo y luego determina si es oscuro o brillante (obtuve este código de StackOverflow). Multiplica los valores de color individuales con algunos números calculados y devuelve 'negro' o 'blanco'. Luego uso otra función para cambiar el color del texto agregando / eliminando la .darkclase.

Agregar oyentes de eventos:

Finalmente, estamos conectando todas las funciones agregando Event Listeners.

Primero, agregamos un keyupevento a la hexentrada. Este evento se activa cada vez que se suelta una tecla. Aquí está el desglose del proceso:

  1. Compruebe si el código de entrada es válido y amplíelo si es una abreviatura.
  2. Establezca el color de fondo del cuerpo en el valor de entrada.
  3. Verifique el contraste de color y cambie el color del texto en consecuencia.
  4. Llame a la función de conversión y coloque el color convertido en el campo de entrada RGB.

El otro detector de eventos que usamos es blur. Se activa cada vez que la entrada pierde el "foco", o en términos simples, cada vez que hace clic / toca fuera del elemento de entrada, blurse activa. ¡Así que es bueno modificar el hexadecimal de entrada!

Entonces, verificamos si el color hexadecimal es válido o no, luego lo expandimos si es corto, y finalmente agregamos un '#' si no existe. Tenga en cuenta que estamos comprobando si el índice 0 y 1 contienen '#'. Esto se hace para que la función no anteponga '#' dos veces.

El mismo keyupdetector de eventos se agrega a la entrada RGB y también sigue la misma serie de pasos que el detector de eventos hexadecimal.

Por último, hemos agregado un detector de eventos keyupa todo el documento, es decir, se activará para cualquiera de los dos elementos de entrada. En él, estamos llamando a la errorMarkfunción, que agrega el icono de error, en caso de error, o lo elimina si todo es válido.

Aquí está el código final para app.js:

const hex = document.getElementById("hex"); const rgb = document.getElementById("rgb"); // Check Functions function checkHex(hex) { const hexRegex = /^[#]*([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i if (hexRegex.test(hex)) { return true; } } function checkRgb(rgb) { const rgbRegex = /([R][G][B][A]?[(]\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s*,\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])(\s*,\s*((0\.[0-9]{1})|(1\.0)|(1)))?[)])/i if (rgbRegex.test(rgb)) { return true } } // Parse Function function modifyHex(hex) { if (hex.length == 4) { hex = hex.replace('#', ''); } if (hex.length == 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } return hex; } // Converting Functions function hexToRgb(hex) { let x = []; hex = hex.replace('#', '') if (hex.length != 6) { hex = modifyHex(hex) } x.push(parseInt(hex.slice(0, 2), 16)) x.push(parseInt(hex.slice(2, 4), 16)) x.push(parseInt(hex.slice(4, 6), 16)) return "rgb(" + x.toString() + ")" } function rgbToHex(rgb) { let y = rgb.match(/\d+/g).map(function(x) { return parseInt(x).toString(16).padStart(2, '0') }); return y.join('').toUpperCase() } // Helper Functions function addPound(x) { return '#' + x; } // Function to add cross mark on error values function errorMark() { if (checkHex(hex.value)) { document.getElementById('hexError').classList.add('hidden'); } else { document.getElementById('hexError').classList.remove('hidden'); } if (checkRgb(rgb.value)) { document.getElementById('rgbError').classList.add('hidden'); } else { document.getElementById('rgbError').classList.remove('hidden'); } } // Finding Contrast Ratio to change text color. Thanks //stackoverflow.com/a/11868398/10796932 function getContrastYIQ(hexcolor) { if (checkHex(hexcolor)) { hexcolor = hexcolor.replace("#", '') } else { hexcolor = rgbToHex(hexcolor) } var r = parseInt(hexcolor.substr(0, 2), 16); var g = parseInt(hexcolor.substr(2, 2), 16); var b = parseInt(hexcolor.substr(4, 2), 16); var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; return (yiq >= 128) ? document.body.classList.add('dark') : document.body.classList.remove('dark') } // Adding Event Listeners hex.addEventListener('keyup', function() { let color = hex.value if (checkHex(color)) { color = modifyHex(color); document.body.style.backgroundColor = addPound(color); getContrastYIQ(color) rgb.value = hexToRgb(color); } }) hex.addEventListener('blur', function() { if (checkHex(hex.value)) { hex.value = modifyHex(hex.value) if (hex.value[1] != '#') { if (hex.value[0] != '#') { hex.value = addPound(hex.value); } } } }) rgb.addEventListener('keyup', function() { let color = rgb.value if (checkRgb(color)) { hex.value = color = addPound(rgbToHex(color)) document.body.style.backgroundColor = color; getContrastYIQ(color) } }) document.addEventListener('keyup', function() { errorMark(); })

Conclusión

There you have it! I know the code is not perfect and can be refactored, but hey, this is just the beginning. If you want to improve this code, you can go ahead and open a PR on my github repo.

Happy Coding!