Crear gradientes en PHP y Javascript

Mientras trabajábamos en un proyecto para un cliente de OXUS se nos ocurrió implementar una solución parecida a una nube de tags para orientar al usuario en la elección sobre distintas opciones. Luego pensamos en agregar colores a los distintos tamaños generados en la nube, pero para esto debíamos tener una forma simple de generar un gradiente de un color a otro, comencé buscando códigos en php y javascript, finalmente encontré uno muy bueno en php, pero me di cuenta que no servía de mucho, ya que dinámicamente dependiendo de la selección del usuario la nube se iba modificando, por lo que me puse a portar este código de php a javascript.

Código en PHP:

< ?php

/*----------------------------------------------------------------------
Colour Tools
============
Colour Tools is a class that contains various functions for
manipulating colours in PHP. It includes basic conversion functions,
a gradient function, an invert function and a set of functions for
comparing two colours for brightness and colour contrast, according
to the W3C recommendations.

Usage:
echo ColourTools::compare('ffffff', '000000');

Version 0.5
Copyright Jack Sleight - www.reallyshiny.com
This script is licensed under the:
Creative Commons Attribution-ShareAlike 2.5 License
----------------------------------------------------------------------*/

class ColourTools
{
/*--------------------------------------------------
Convert Hex code string to RGB array
--------------------------------------------------*/

function hexToRgb($hex)
{
$hex = preg_replace('/^#/', '', $hex);
return array(
'r' => hexdec(substr($hex, 0, 2)),
'g' => hexdec(substr($hex, 2, 2)),
'b' => hexdec(substr($hex, 4, 2))
);
}

/*--------------------------------------------------
Convert RGB array to Hex code string
--------------------------------------------------*/

function rgbToHex($rgb)
{
return sprintf('%02x', $rgb['r']).sprintf('%02x', $rgb['g']).sprintf('%02x', $rgb['b']);
}

/*--------------------------------------------------
Create a gradient from one colour to another
--------------------------------------------------*/

function gradient($col1, $col2, $steps)
{
$col1 = ColourTools::hexToRgb($col1);
$col2 = ColourTools::hexToRgb($col2);

$step = array(
'r' => ($col1['r'] - $col2['r']) / ($steps - 1),
'g' => ($col1['g'] - $col2['g']) / ($steps - 1),
'b' => ($col1['b'] - $col2['b']) / ($steps - 1)
);
$gradient = array();

for($i = 0; $i < = $steps; $i++) {
$colour = array(
'r' => round($col1['r'] - ($step['r'] * $i)),
'g' => round($col1['g'] - ($step['g'] * $i)),
'b' => round($col1['b'] - ($step['b'] * $i))
);
$gradient[] = ColorTools::rgbToHex($colour);
}

return $gradient;
}

/*--------------------------------------------------
Invert a colour
--------------------------------------------------*/

function invert($col1)
{
$col1 = ColourTools::hexToRgb($col1);
$col2 = array(
'r' => 256 - $col1['r'],
'g' => 256 - $col1['g'],
'b' => 256 - $col1['b']
);
return ColourTools::rgbToHex($col2);
}

/*--------------------------------------------------
Checks if two colours adhere to the W3C
recommendations for brightness and colour
contrast
--------------------------------------------------*/

function compare($col1, $col2)
{
$brightnessDifference = ColourTools::brightnessDifference($col1, $col2);
$colourDifference = ColourTools::colourDifference($col1, $col2);
return (($brightnessDifference > 125) &amp;&amp; ($colourDifference > 500)) ? true : false;
}

/*--------------------------------------------------
Get the brightness value of a colour
--------------------------------------------------*/

function brightness($col)
{
$col = ColourTools::hexToRgb($col);
return (($col['r'] * 299) + ($col['g'] * 587) + ($col['b'] * 114)) / 1000;
}

/*--------------------------------------------------
Get the brightness difference of two colours
--------------------------------------------------*/

function brightnessDifference($col1, $col2)
{
return abs(ColourTools::brightness($col1) - ColourTools::brightness($col2));
}

/*--------------------------------------------------
Get the colour difference of two colours
--------------------------------------------------*/

function colourDifference($col1, $col2)
{
$col1 = ColourTools::hexToRgb($col1);
$col2 = ColourTools::hexToRgb($col2);
return
(max($col1['r'], $col2['r']) - min($col1['r'], $col2['r'])) +
(max($col1['g'], $col2['g']) - min($col1['g'], $col2['g'])) +
(max($col1['b'], $col2['b']) - min($col1['b'], $col2['b']));
}

}

?>

Revisé que las funciones utilizadas en PHP existieran o tuvieran una similar en Javascript, pero me encontré con que no hay una implementación de sprintf en Javascript, por lo que dentro de varias opciones que encontré, me quedé con la implementación de sprintf para javascript por WebToolkit, el único detalle es que el código en ese sitio y el de descarga muestra algunos códigos mal encodeados por lo que no funciona a la primera, aquí dejo el código arreglado.

/**
*
*  Javascript sprintf
*  http://www.webtoolkit.info/
*
*
**/

sprintfWrapper = {

init : function () {

if (typeof arguments == "undefined") { return null; }
if (arguments.length < 1) { return null; }
if (typeof arguments[0] != "string") { return null; }
if (typeof RegExp == "undefined") { return null; }

var string = arguments[0];
var exp = new RegExp(/(%([%]|(\-)?(\+|\x20)?(0)?(\d+)?(\.(\d)?)?([bcdfosxX])))/g);
var matches = new Array();
var strings = new Array();
var convCount = 0;
var stringPosStart = 0;
var stringPosEnd = 0;
var matchPosEnd = 0;
var newString = '';
var match = null;

while (match = exp.exec(string)) {
if (match[9]) { convCount += 1; }

stringPosStart = matchPosEnd;
stringPosEnd = exp.lastIndex - match[0].length;
strings[strings.length] = string.substring(stringPosStart, stringPosEnd);

matchPosEnd = exp.lastIndex;
matches[matches.length] = {
match: match[0],
left: match[3] ? true : false,
sign: match[4] || '',
pad: match[5] || ' ',
min: match[6] || 0,
precision: match[8],
code: match[9] || '%',
negative: parseInt(arguments[convCount]) < 0 ? true : false,
argument: String(arguments[convCount])
};
}
strings[strings.length] = string.substring(matchPosEnd);

if (matches.length == 0) { return string; }
if ((arguments.length - 1) < convCount) { return null; }

var code = null;
var match = null;
var i = null;

for (i=0; i<matches.length; i++) {

if (matches[i].code == '%') { substitution = '%' }
else if (matches[i].code == 'b') {
matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(2));
substitution = sprintfWrapper.convert(matches[i], true);
}
else if (matches[i].code == 'c') {
matches[i].argument = String(String.fromCharCode(parseInt(Math.abs(parseInt(matches[i].argument)))));
substitution = sprintfWrapper.convert(matches[i], true);
}
else if (matches[i].code == 'd') {
matches[i].argument = String(Math.abs(parseInt(matches[i].argument)));
substitution = sprintfWrapper.convert(matches[i]);
}
else if (matches[i].code == 'f') {
matches[i].argument = String(Math.abs(parseFloat(matches[i].argument)).toFixed(matches[i].precision ? matches[i].precision : 6));
substitution = sprintfWrapper.convert(matches[i]);
}
else if (matches[i].code == 'o') {
matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(8));
substitution = sprintfWrapper.convert(matches[i]);
}
else if (matches[i].code == 's') {
matches[i].argument = matches[i].argument.substring(0, matches[i].precision ? matches[i].precision : matches[i].argument.length)
substitution = sprintfWrapper.convert(matches[i], true);
}
else if (matches[i].code == 'x') {
matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16));
substitution = sprintfWrapper.convert(matches[i]);
}
else if (matches[i].code == 'X') {
matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16));
substitution = sprintfWrapper.convert(matches[i]).toUpperCase();
}
else {
substitution = matches[i].match;
}

newString += strings[i];
newString += substitution;

}
newString += strings[i];

return newString;

},

convert : function(match, nosign){
if (nosign) {
match.sign = '';
} else {
match.sign = match.negative ? '-' : match.sign;
}
var l = match.min - match.argument.length + 1 - match.sign.length;
var pad = new Array(l < 0 ? 0 : l).join(match.pad);
if (!match.left) {
if (match.pad == "0" || nosign) {
return match.sign + pad + match.argument;
} else {
return pad + match.sign + match.argument;
}
} else {
if (match.pad == "0" || nosign) {
return match.sign + match.argument + pad.replace(/0/g, ' ');
} else {
return match.sign + match.argument + pad;
}
}
}
}

sprintf = sprintfWrapper.init;

Aquí el resultado en Javascript de la clase en PHP con algunos extra:

/**
* Color Tools Javascript
*
* Contiene varias clases para manipular colores en Javascript.
* Basado en la versión 0.5 de Color Tools PHP por
* Jack Sleight - www.reallyshiny.com
*
* @author Pedro Fuentes <pedro.fuentes@oxus.cl>
* @version 0.1
* @license Creative Commons Attribution-ShareAlike 2.5 License
*/

colorTools = {

/**
* Convert Hex code string to RGB array
* @param string hex hexadecimal color code
* @return array
*/
hexToRgb : function(hex){

regex	= hex.replace('#', '');
rgb			= new Array();

rgb['r']	= colorTools.hexdec(regex.substr(0,2));
rgb['g']	= colorTools.hexdec(regex.substr(2,2));
rgb['b']	= colorTools.hexdec(regex.substr(4,2));

return rgb;

},

/**
* Convert RGB array to Hex code string
* @param array rgb
* @return string
*/
rgbToHex : function(rgb){
return sprintf('%02x', rgb['r']) + sprintf('%02x', rgb['g']) + sprintf('%02x', rgb['b']);
},

/**
* Create gradient from one color to another
* @param col1 string
* @param col2 string
* @param steps integer
* @return array
*/
gradient : function(col1, col2, steps){
gradient	= new Array();
step		= new Array();

col1 = colorTools.hexToRgb(col1);
col2 = colorTools.hexToRgb(col2);

step['r'] = (col1['r'] - col2['r']) / (steps - 1);
step['g'] = (col1['g'] - col2['g']) / (steps - 1);
step['b'] = (col1['b'] - col2['b']) / (steps - 1);

for(i = 0; i < = steps; i++) {

color		= new Array();

color['r']	= Math.round(col1['r'] - (step['r'] * i));
color['g']	= Math.round(col1['g'] - (step['g'] * i));
color['b']	= Math.round(col1['b'] - (step['b'] * i));

gradient[i]	= colorTools.rgbToHex(color);

}

return gradient;
},

/**
* Create a text gradient from one color to another
* @param col1 string
* @param col2 string
* @param str string
* @return array
*/
textGradient : function(col1, col2, str){
gradient	= new Array();
step		= new Array();

col1 = colorTools.hexToRgb(col1);
col2 = colorTools.hexToRgb(col2);

step['r']	= (col1['r'] - col2['r']) / (str.length - 1);
step['g']	= (col1['g'] - col2['g']) / (str.length - 1);
step['b']	= (col1['b'] - col2['b']) / (str.length - 1);

var res			= '';

for(i = 0; i <= str.length; i++) {

color		= new Array();

color['r']	= Math.round(col1['r'] - (step['r'] * i));
color['g']	= Math.round(col1['g'] - (step['g'] * i));
color['b']	= Math.round(col1['b'] - (step['b'] * i));

res	= res + '<span style="color: #' + colorTools.rgbToHex(color) + '">' + str.charAt(i) + '';

}

return res;
},

/**
* Invert a color
* @param col string
* @return string
*/
invert : function(col){
var col1	= colorTools.hexToRgb(col);
var col2	= new Array();

col2['r']	= 255 - col1['r'];
col2['g']	= 255 - col1['g'];
col2['b']	= 255 - col1['b'];

return colorTools.rgbToHex(col2);

},

/**
* Checks if two colors adhere to the W3C
* recommendations for brightness and color contrast
* @param col1 string
* @param col2 string
* @return bool
*/
compare : function(col1,col2){
var brightnessDifference	= colorTools.brightnessDifference(col1,col2);
var colorDifference			= colorTools.colorDifference(col1,col2);

return ( ( (brightnessDifference > 125) &amp;&amp; (colorDifference > 500) ) ? true : false );
},

/**
* Get the brightness value of a color
* @param col string
* @return float
*/
brightness : function(col){
col	= colorTools.hexToRgb(col);
return ( ( (col['r'] * 299) + ( col['g'] * 587 ) + ( col['b'] * 114 ) ) / 1000 );
},

/**
* Get the brightness difference of two colors
* @param col1 string
* @param col2 string
* @return int
*/
brightnessDifference : function(col1,col2){
return Math.abs( colorTools.brightness(col1) - colorTools.brightness(col2) );
},

/**
* Get the color difference of two colors
* @param col1 string
* @param col2 string
* @return int
*/
colorDifference : function(col1,col2){
col1	= colorTools.hexToRgb(col1);
col2	= colorTools.hexToRgb(col2);

return ( Math.max(col1['r'],col2['r']) - Math.min(col1['r'],col2['r']) ) + ( Math.max(col1['g'],col2['g']) - Math.min(col1['g'],col2['g']) ) + ( Math.max(col1['b'],col2['b']) - Math.min(col1['b'],col2['b']) );
},

/**
* Convert Decimal to Hexadecimal
* @param d decimal
* @return hexadecimal
*/
dechex : function(d) {
return d.toString(16);
},

/**
* Convert Hexadecimal to Decimal
* @param h hexadecimal
* @return int
*/
hexdec : function(h) {
return parseInt(h,16);
}

}

En especifico para el metodo gradient que estoy utilizando ingresas el código rgb del color inicial, el color final y la cantidad de pasos para llegar de uno a otro. Como yo estoy generando los tamaños de las letras en porcentaje generé 100 pasos para llegar de uno a otro, así obtengo un arreglo con los colores y lo asocio al porcentaje del tamaño de la letra.

Además agregué el método textGradient, genera un texto coloreado con un gradiente según el color inicial y final tal como en el método gradient.

Ejemplos:

Title: Iframe
Description: External

2 Responses to “Crear gradientes en PHP y Javascript”


Leave a Reply