Skip to content

Instantly share code, notes, and snippets.

@espetro
Last active October 30, 2020 09:11
Show Gist options
  • Save espetro/bd7555f4363af4f5a81bb324eae76912 to your computer and use it in GitHub Desktop.
Save espetro/bd7555f4363af4f5a81bb324eae76912 to your computer and use it in GitHub Desktop.
JS color interpolation

JS Color Interpolator

Generate a simple sequential color palette given two colors and the number of steps. You can modify both of them to suit your taste. Credits to @nmjcode for making this wonderful script.

You can run and bookmark it.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Color Interpolator</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
Interim steps: <input type="range" id="numsteps" value="10" min="1" max="20" />
<ul id="list">
<li id="start"><input type="color" value="#5E4FA2" size="7" /><span></span></li>
<li id="end"><input type="color" value="#F79459" size="7" /><span></span></li>
</ul>
RGB <input type="radio" name="intype" value="interpolateColor" checked />
HSL <input type="radio" name="intype" value="interpolateHSL" />
<br/>
Use hex inputs <input type="checkbox" id="usehex" />
<script src="main.js"></script>
</body>
</html>
body {
text-align:center;
background: #111;
color: #999;
font-family: Arial,sans-serif;
}
ul {
margin: 20px auto;
width: 300px;
padding:0;
list-style-type:none;
}
li {
/*border: 1px solid #222;*/
height: 30px;
line-height: 30px;
text-align:right;
background-color: #000;
transition:background-color 1s ease-in-out;
position: relative;
}
li span {
font-family:monospace;
font-size:15px;
margin-right:10px;
color: #fff;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}
li input {
position:absolute;
right:310px;
}
/**
* TODO - refactor this as a (jQuery?) plugin!
**/
// Converts a #ffffff hex string into an [r,g,b] array
var h2r = function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
] : null;
};
// Inverse of the above
var r2h = function(rgb) {
return "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
};
// Interpolates two [r,g,b] colors and returns an [r,g,b] of the result
// Taken from the awesome ROT.js roguelike dev library at
// https://github.com/ondras/rot.js
var _interpolateColor = function(color1, color2, factor) {
if (arguments.length < 3) { factor = 0.5; }
var result = color1.slice();
for (var i=0;i<3;i++) {
result[i] = Math.round(result[i] + factor*(color2[i]-color1[i]));
}
return result;
};
var rgb2hsl = function(color) {
var r = color[0]/255;
var g = color[1]/255;
var b = color[2]/255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = (l > 0.5 ? d / (2 - max - min) : d / (max + min));
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
};
var hsl2rgb = function(color) {
var l = color[2];
if (color[1] == 0) {
l = Math.round(l*255);
return [l, l, l];
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var s = color[1];
var q = (l < 0.5 ? l * (1 + s) : l + s - l * s);
var p = 2 * l - q;
var r = hue2rgb(p, q, color[0] + 1/3);
var g = hue2rgb(p, q, color[0]);
var b = hue2rgb(p, q, color[0] - 1/3);
return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
}
};
var _interpolateHSL = function(color1, color2, factor) {
if (arguments.length < 3) { factor = 0.5; }
var hsl1 = rgb2hsl(color1);
var hsl2 = rgb2hsl(color2);
for (var i=0;i<3;i++) {
hsl1[i] += factor*(hsl2[i]-hsl1[i]);
}
return hsl2rgb(hsl1);
};
(function($) {
var $list = $('#list'),
$start = $('#start'),
$end = $('#end'),
$intype = $('input[name="intype"]'),
$usehex = $('#usehex');
// Add li elements between the start and end ones
var _createSteps = function(numSteps) {
$list.find('li.interim').remove();
for(var i = 0; i < numSteps; i++) {
$end.before('<li class="interim"><span></span></li>');
}
};
// Color each li by interpolating between the start and end colors
var _styleSteps = function() {
var $items = $('li'),
scol = h2r($start.find('input').val()),
ecol = h2r($end.find('input').val());
var fn = '_' + $('input[name="intype"]:checked').val();
console.log('fn', fn);
var factorStep = 1 / ($items.length - 1);
$items.each(function(idx) {
var icol = window[fn](scol, ecol, factorStep * idx),
hcol = r2h(icol);
$(this).css('background-color', hcol);
$(this).find('span').text(hcol);
});
}
// Re-render on change
$('#usehex').on('change', function() {
var ct = $('ul input').eq(0).attr('type');
var scol = $start.find('input').val(),
ecol = $end.find('input').val();
$('ul input').attr('type', (ct == 'color') ? 'text' : 'color');
$start.find('input').val(scol);
$end.find('input').val(ecol);
});
$('input').not('#usehex').on('change', _styleSteps);
$('#numsteps').on('change', function() {
_createSteps($(this).val());
_styleSteps();
}).trigger('change');
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment