Skip to content

Instantly share code, notes, and snippets.

@lovasoa
Last active February 28, 2024 19:53
Show Gist options
  • Save lovasoa/8141243 to your computer and use it in GitHub Desktop.
Save lovasoa/8141243 to your computer and use it in GitHub Desktop.
JS color picker: HTML5 & javascript color picker that uses HTML5 canvas. User can choose hue and value, saturation is set to 1. It has color history management.
<!doctype html>
<html>
<head>
<meta charset="utf8" />
<title>Canvascolor</title>
<style>
body {
background-image:url(http://github.com/favicon.ico); /*Just for fun*/
}
main {
margin-top:50px;
margin-left:25%;
margin-right:25%;
padding:6%;
width:auto;
height:1000px;
background-color:#ccabfd;
text-align:justify;
box-shadow: 0 0 12px black;
}
.fixed {
position:fixed;
top:0;
left:0;
width:100%;
background-color:black;
color:white;
box-shadow:0 0 12px grey;
}
</style>
</head>
<body>
<div class="fixed" >
<span>This div is positioned in fixed coordinates.
The color picker always appears at the right place.
The color picker is added to a normal input of type <code>text</code>:
</span>
<input type="text" class="canvascolor" value="red" />
</div>
<main>
<p>
This color picker is just an <code>&lt;input type="color"&gt;</code>
If your browser supports a native color picker, it will be used.
Else, canvascolor will be used as a fallback.
</p>
<input type="color" value="#fada55" />
</main>
<script src="canvascolor.js"></script>
</body>
</html>
/**
* CANVASCOLOR color picker
*********************************************************
* @licstart The following is the entire license notice for the
* JavaScript code in this page.
*
* Copyright (C) 2013-2014 Ophir LOJKINE
*
*
* The JavaScript code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License (GNU GPL) as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. The code is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*
* As additional permission under GNU GPL version 3 section 7, you
* may distribute non-source (e.g., minimized or compacted) forms of
* that code without the copy of the GNU GPL normally required by
* section 4, provided you include this license notice and a URL
* through which recipients can access the Corresponding Source.
*
* @licend
*/
/*jshint bitwise:false*/
// ==ClosureCompiler==
// @output_file_name canvascolor.js
// @compilation_level ADVANCED_OPTIMIZATIONS
// @js_externs var canvascolor;
// @language ecmascript5_strict
// @use_types_for_optimization true
// ==/ClosureCompiler==
var canvascolor = (function() {//Code Isolation
"use strict";
(function addCSS () {
var styleTag = document.createElement("style");
styleTag.innerHTML = [".canvascolor-container{",
"background-color:black;",
"border-radius:5px;",
"overflow:hidden;",
"width:179px;",
"padding:2px;",
"display:none;",
"}",
".canvascolor-container canvas{",
"cursor:crosshair;",
"}",
".canvascolor-history{",
"overflow:auto;",
"}",
".canvascolor-history > div{",
"margin:2px;",
"display:inline-block;",
"}"].join("");
document.head.appendChild(styleTag);
})();
function hsv2rgb (h,s,v) {
if( s === 0 ) return [v,v,v]; // achromatic (grey)
h /= (Math.PI/6); // sector 0 to 5
var i = h|0,
f = h - i, // factorial part of h
p = v * ( 1 - s ),
q = v * ( 1 - s * f ),
t = v * ( 1 - s * ( 1 - f ) );
switch( i%6 ) {
case 0: return [v,t,p];
case 1: return [q,v,p];
case 2: return [p,v,t];
case 3: return [p,q,v];
case 4: return [t,p,v];
case 5:return [v,p,q];
}
}
function isFixedPosition(elem) {
do {
if (getComputedStyle(elem).position === "fixed") return true;
} while ( (elem = elem.parentElement) !== null );
return false;
}
var containerTemplate;
(function createContainer(){
containerTemplate = document.createElement("div");
containerTemplate.className = "canvascolor-container";
var canvas = document.createElement("canvas");
var historyDiv = document.createElement("div");
historyDiv.className = "canvascolor-history";
containerTemplate.appendChild(canvas);
containerTemplate.appendChild(historyDiv);
})();
function canvascolor(elem) {
var curcolor = elem.value || "#000";
var w=200, h=w/2;
var container = containerTemplate.cloneNode(true);
container.style.width = w+"px";
container.style.position = isFixedPosition(elem) ? "fixed" : "absolute";
var canvas = container.getElementsByTagName("canvas")[0];
var ctx = canvas.getContext("2d");
canvas.width = w; canvas.height=h;
var prevcolorsDiv = container.getElementsByClassName("canvascolor-history")[0];
prevcolorsDiv.style.width=w+"px";
prevcolorsDiv.style.maxHeight=h+"px";
var previewdiv = createColorDiv(curcolor);
previewdiv.style.border = "1px solid white";
previewdiv.style.borderRadius = "5px";
document.body.appendChild(container);
function displayContainer(){
var rect = elem.getBoundingClientRect();
var conttop=(rect.top+rect.height+3),
contleft=rect.left;
if (container.style.position !== "fixed") {
conttop += window.scrollY;
contleft += window.scrollX;
}
container.style.top = conttop+"px";
container.style.left = contleft+"px";
container.style.display = "block";
}
function hideContainer(){
container.style.display = "none";
}
elem.addEventListener("mouseover", displayContainer, true);
container.addEventListener("mouseleave", hideContainer, false);
elem.addEventListener("keyup", function(){
changeColor(elem.value, true);
}, true);
changeColor(elem.value, true);
var idata = ctx.createImageData(w,h);
function rgb2hex (rgb) {
function num2hex (c) {return (c*15/255|0).toString(16);}
return "#"+num2hex(rgb[0])+num2hex(rgb[1])+num2hex(rgb[2]);
}
function colorAt(coords) {
var x=coords[0], y=coords[1];
return hsv2rgb(x/w*Math.PI, 1, (1-y/h)*255);
}
function render() {
for (var x=0; x<w; x++) {
for (var y=0;y<h; y++) {
var i = 4*(x+y*w);
var rgb = colorAt([x,y]);
idata.data[i] = rgb[0];//Red
idata.data[i+1] = rgb[1];//Green
idata.data[i+2] = rgb[2];//Blue
idata.data[i+3] = 255;
}
}
ctx.putImageData(idata,0,0);
}
render();
/** Changes the current color (the value of the input field) and updates other variables accordingly
* @param {string} color The new color. Must be a valid CSS color string if ensureValid is not specified
* @param {boolean} [ensureValid=false] Do not make the change if color is not a valid CSS color
*/
function changeColor(color, ensureValid) {
elem.style.backgroundColor = color;
if (ensureValid && elem.style.backgroundColor.length === 0) {
elem.style.backgroundColor = curcolor;
return;
}
previewdiv.style.backgroundColor = color;
curcolor = color;
elem.value = color;
elem.focus();
}
function createColorDiv (color) {
var div = document.createElement("div");
div.style.width = (w/3-10)+"px";
div.style.height = (h/3-8)+"px";
div.style.backgroundColor = color;
div.addEventListener("click", function(){
changeColor(color);
}, true);
if (prevcolorsDiv.childElementCount <= 1) prevcolorsDiv.appendChild(div);
else prevcolorsDiv.insertBefore(div,prevcolorsDiv.children[1]);
return div;
}
function canvasPos(evt) {
var canvasrect = canvas.getBoundingClientRect();
return [evt.clientX - canvasrect.left, evt.clientY - canvasrect.top];
}
canvas.addEventListener("mousemove", function(evt){
var coords = canvasPos(evt);
previewdiv.style.backgroundColor = rgb2hex(colorAt(coords));
}, true);
canvas.addEventListener("click", function(evt){
var coords = canvasPos(evt);
var color = rgb2hex(colorAt(coords));
createColorDiv(color);
changeColor(color);
}, true);
canvas.addEventListener("mouseleave", function(){
previewdiv.style.backgroundColor = curcolor;
}, true);
}
//Put a color picker on every input[type=color] if the browser doesn't support this input type
//and on every input with the class canvascolor
var pickers = document.querySelectorAll("input.canvascolor, input[type=color]");
for (var i=0;i <pickers.length; i++) {
var input = pickers.item(i);
//If the browser supports native color picker and the user didn't
//explicitly added canvascolor to the element, we do not add a custom color picker
if (input.type !== "color" ||
input.className.split(" ").indexOf("canvascolor") !== -1) {
canvascolor(input);
}
}
return canvascolor;
}());
var canvascolor=(function(){function x(a,c){a/=Math.PI/6;var k=a|0,d=a-k,f=0*c,l=c*(1-1*d),d=c*(1-1*(1-d));switch(k%6){case 0:return[c,d,f];case 1:return[l,c,f];case 2:return[f,c,d];case 3:return[f,l,c];case 4:return[d,f,c];case 5:return[c,f,l]}}function y(a){do if("fixed"===getComputedStyle(a).position)return!0;while(null!==(a=a.parentElement));return!1}function v(a){function c(b){return"#"+(15*b[0]/255|0).toString(16)+(15*b[1]/255|0).toString(16)+(15*b[2]/255|0).toString(16)}function k(b){return x(b[0]/ g*Math.PI,255*(1-b[1]/m))}function d(b,z){a.style.backgroundColor=b;z&&0===a.style.backgroundColor.length?a.style.backgroundColor=t:(t=q.style.backgroundColor=b,a.value=b,a.focus())}function f(b){var a=document.createElement("div");a.style.width=g/3-10+"px";a.style.height=m/3-8+"px";a.style.backgroundColor=b;a.addEventListener("click",function(){d(b)},!0);1>=n.childElementCount?n.appendChild(a):n.insertBefore(a,n.children[1]);return a}function l(b){var a=h.getBoundingClientRect();return[b.clientX- a.left,b.clientY-a.top]}var t=a.value||"#000",g=200,m=g/2,e=r.cloneNode(!0);e.style.width=g+"px";e.style.position=y(a)?"fixed":"absolute";var h=e.getElementsByTagName("canvas")[0],p=h.getContext("2d");h.width=g;h.height=m;var n=e.getElementsByClassName("canvascolor-history")[0];n.style.width=g+"px";n.style.maxHeight=m+"px";var q=f(t);q.style.border="1px solid white";q.style.a="5px";document.body.appendChild(e);a.addEventListener("mouseover",function(){var b=a.getBoundingClientRect(),c=b.top+b.height+ 3,b=b.left;"fixed"!==e.style.position&&(c+=window.scrollY,b+=window.scrollX);e.style.top=c+"px";e.style.left=b+"px";e.style.display="block"},!0);e.addEventListener("mouseleave",function(){e.style.display="none"},!1);a.addEventListener("keyup",function(){d(a.value,!0)},!0);d(a.value,!0);var s=p.createImageData(g,m);(function(){for(var b=0;b<g;b++)for(var a=0;a<m;a++){var c=4*(b+a*g),d=k([b,a]);s.data[c]=d[0];s.data[c+1]=d[1];s.data[c+2]=d[2];s.data[c+3]=255}p.putImageData(s,0,0)})();h.addEventListener("mousemove", function(a){a=l(a);q.style.backgroundColor=c(k(a))},!0);h.addEventListener("click",function(a){a=l(a);a=c(k(a));f(a);d(a)},!0);h.addEventListener("mouseleave",function(){q.style.backgroundColor=t},!0)}(function(){var a=document.createElement("style");a.innerHTML=".canvascolor-container{background-color:black;border-radius:5px;overflow:hidden;width:179px;padding:2px;display:none;}.canvascolor-container canvas{cursor:crosshair;}.canvascolor-history{overflow:auto;}.canvascolor-history > div{margin:2px;display:inline-block;}"; document.head.appendChild(a)})();var r;(function(){r=document.createElement("div");r.className="canvascolor-container";var a=document.createElement("canvas"),c=document.createElement("div");c.className="canvascolor-history";r.appendChild(a);r.appendChild(c)})();for(var w=document.querySelectorAll("input.canvascolor, input[type=color]"),p=0;p<w.length;p++){var u=w.item(p);"color"===u.type&&-1===u.className.split(" ").indexOf("canvascolor")||v(u)}return v})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment