Color mixer

Combine up to 10 colors and get the RGB and HEX of the final mixed color.

by Lucas Krause

HTML

<div class="final-color"></div>
<div class="options">
    <p><label for="layer-color">Color</label> <input type="text" id="layer-color" /></p>
    <p>Opacity <input type="range" min="0" max="100" id="layer-opacity" /></p>
    <p><input type="button" id="save-layer" value="Save settings" /> <input type="button" id="delete-layer" value="Delete layer" /></p>
    <p><input type="button" id="add-layer" value="Add new layer" /></p>
</div>
<div class="layers"></div>

CSS

:root {
	font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
	line-height:1.5;
	color:#333;
	font-size:100%;
	font-weight:400;
}

:first-child {
	margin-top:0 !important;
}
:last-child {
	margin-bottom:0 !important;
}

html, body, .options, .layers {
	height:100%;
}

body {
	margin:0;
}



.options {
	float:left;
	width:25%;
	padding:1em;
	background-color:#fff;
	position:relative;
	z-index:1;
	
	-webkit-box-shadow:1px -1px 8px rgba(0,0,0,.3);
	   -moz-box-shadow:1px -1px 8px rgba(0,0,0,.3);
		-ms-box-shadow:1px -1px 8px rgba(0,0,0,.3);
		 -o-box-shadow:1px -1px 8px rgba(0,0,0,.3);
			box-shadow:1px -1px 8px rgba(0,0,0,.3);
	
	-webkit-box-sizing:border-box;
	   -moz-box-sizing:border-box;
		-ms-box-sizing:border-box;
		 -o-box-sizing:border-box;
			box-sizing:border-box;
}

.options--min {
	position:absolute;
	left:0;
	bottom:0;
	height:auto;
}

	.options--min > * {
		display:none;
	}
	.options--min > :last-child {
		display:block;
		margin-top:0;
	}



/*.options + */.layers {
	overflow:hidden;
	position:relative;
}
	
	.layers__item {
		position:absolute;
		left:0;
		width:100%;
		cursor:pointer;
		
		-webkit-box-shadow:0 -1px 8px rgba(0,0,0,.3);
		   -moz-box-shadow:0 -1px 8px rgba(0,0,0,.3);
			-ms-box-shadow:0 -1px 8px rgba(0,0,0,.3);
			 -o-box-shadow:0 -1px 8px rgba(0,0,0,.3);
				box-shadow:0 -1px 8px rgba(0,0,0,.3);
	}
	.layers__item:first-child {
		top:0;
		height:100%;
	}
	.layers__item:nth-child(2) {
		top:10%;
		height:90%;
	}
	.layers__item:nth-child(3) {
		top:20%;
		height:80%;
	}
	.layers__item:nth-child(4) {
		top:30%;
		height:70%;
	}
	.layers__item:nth-child(5) {
		top:40%;
		height:60%;
	}
	.layers__item:nth-child(6) {
		top:50%;
		height:50%;
	}
	.layers__item:nth-child(7) {
		top:60%;
		height:40%;
	}
	.layers__item:nth-child(8) {
		top:70%;
		height:30%;
	}
	.layers__item:nth-child(9) {
		top:80%;
		height:20%;
	}
	.layers__item:nth-child(10) {
		top:90%;
		height:10%;
	}
	.layers__item:nth-child(10) ~ .layers__item...

JavaScript

var jq_curLayer=null,
	fn_updateFinalColor=function(){
		var arr_rgb;
		
		$(".layers__item").each(function(i){
			var arr_rgb_b,
				str_color=$(this).attr("data-color"),
				int_opacity=parseInt($(this).attr("data-opacity"));
			
			if(str_color.match(reg_hex)!==null){
				if(str_color.length==3){
					var str_a="";
					
					$.each(str_color, function(i, val){
						str_a+=val+val;
					});
					str_color=str_a;
				}
				
				var arr_a=[];
				
				(function(){
					for(var i=0; i<str_color.length; i+=2){
						arr_a.push(parseInt(str_color.substr(i, 2), 16));
					}
				})();
				arr_rgb_b=arr_a;
			}else{ //matches `reg_rgb`
				arr_rgb_b=$.map(str_color.split(","), function(val){
					return parseInt(val);
				});
			}
			if(i==0){
				arr_rgb=arr_rgb_b;
			}else{
				arr_rgb=$.map(arr_rgb, function(val, i){
					return val+(arr_rgb_b[i]-val)*(int_opacity/100);
				});
			}
		});
		arr_rgb=$.map(arr_rgb, Math.round);
		
		var str_rgb="rgb("+arr_rgb.join(", ")+")",
			str_hex="#"+$.map(arr_rgb, function(val){
				return val.toString(16);
			}).join("").toUpperCase();
		
		$(".final-color").css("background-color", str_rgb).html('<span class="final-color__text">'+str_rgb+'</span><br /><span class="final-color__text">'+str_hex+'</span>');
	},
	reg_rgb=/^\d{1,3}( *, *\d{1,3}){2}$/,
	reg_hex=/^([a-f0-9]{3}|[a-f0-9]{6})$/i,
	fn_updateButtons=function(){
		$("#add-layer").prop("disabled", $(".layers__item").length<10 ? false : true);
		$("#delete-layer").prop("disabled", $(".layers__item").length>1 ? false : true);
	},
	fn_hideOptions=function(){
		$(".options").addClass("options--min");
		jq_curLayer=null;
	};

$("#layer-color").bind("change", function(){
	if($(this).val().match(reg_rgb)===null && $(this).val().match(reg_hex)===null){
		$(this).val(jq_curLayer.attr("data-color"));
	}
	$("+ .color-preview", this).remove();
	$('<span class="color-preview" />').css("background-color", $(this).val().match(reg_rgb)!==null ? "rgb("+$(this).val()+")" :...