/*
* @title カラーピッカーを作ってみる
* @description ほげぇ
* @include http://*
* @license MIT License
* @require
*/
(function(){
var TEST_MODE = true;
var L_R = 0.29891;
var L_G = 0.58661;
var L_B = 0.11448;
var NS = 'sa-ka-na.info,2010/js/ColorPicker';
var HTML_Container =
"<div class='head' " +
" <div class='tabs' " +
" ><span class='tabHls tab'>HLS</span " +
" ><canvas class='closer' width='16' height='16' " +
" ></canvas " +
"></div " +
"><div class='panes' " +
" ><div class='paneHls pane' " +
" ><canvas class='canvasHls' " +
" width='360' " +
" height='360'></canvas></div " +
" ></div " +
">";
var CSS_CONTAINER = {
head: "" +
"display: table;"+
"",
tabs: "" +
"display: table-cell;"+
"",
closer: ""+
"display: table-cell;"+
""
};
var walkNode = function(node, cb){
switch(cb(node)){
case 'BREAK':
return 'BREAK';
case 'STOP':
return undefined;
}
var cn = node.childNodes;
for(var i=0, l = cn ? cn.length : 0; i < l; i++){
switch(walkNode(cn[i],cb)){
case 'BREAK':
return 'BREAK';
case 'STOP':
return 'STOP';
}
}
return undefined;
};
var walkElem = function(elem, cb){
walkNode(
elem, function(node){
if(node.nodeType !== document.ELEMENT_NODE) return undefined;
return cb(node);
}
);
};
var makeClassNameDic = function(elem){
var dic = {};
walkElem(
elem,
function(elem){
if(elem.className){
var cns = elem.className.split(/\s+/);
cns.forEach(
function(cn){
if(cn.length){
if(dic[cn]){
dic[cn].push(elem);
} else {
dic[cn] = [elem];
}
}
}
);
}
}
);
return dic;
};
var makeContainer = function(){
var container = document.createElement('div');
container.innerHTML = HTML_Container;
var dic = makeClassNameDic(container);
for(var cn in CSS_CONTAINER){
var css = CSS_CONTAINER[cn];
var slot = dic[cn] || [];
for(var i=0,l=slot.length;i<l;i++){
slot[i].style.cssText = css;
}
}
return {
root: container,
elements: dic,
element: function(name){ return this.elements[name][0]; }
};
};
var rgb2l = function(rgb){
var r = rgb.r;
var g = rgb.g;
var b = rgb.b;
return (L_R * r / 255) + (L_G * g / 255) + (L_B * b / 255);
};
var rgb2hls = function(rgb){
var r = rgb.r;
var g = rgb.g;
var b = rgb.b;
var max = null;
var min = null;
var max_r = false;
var max_g = false;
var max_b = false;
if(r >= g){
if(r >= b){
max_r = true;
if( g >= b){
max = r;
min = b;
} else {
max = r;
min = g;
}
} else {
max_b = true;
max = b;
min = g;
}
} else {
if(g >= b){
max_g = true;
if(r >= b) {
max = g;
min = b;
} else {
max = g;
min = r;
}
} else {
max_b = true;
max = b;
min = r;
}
}
var d = (max - min);
var h = null;
if(d == 0){
h = 0;
} else {
if(max_r){
h = (g - b) / d;
} else if(max_g){
h = (b - r) / d + 1 / 3;
} else {
h = (r - g) / d + 2 / 3;
}
if(h < 0) h += 1; else if(h > 1) h -= 1;
h *= 360;
}
var v = max;
var s = max == 0 ? 0 : 255 * (max - min) / max;
return {h:h,v:v,s:s};
};
var hls2rgb = function(hls){
var h = hls.h;
var l = hls.l;
var s = hls.s;
var m2 = ( l <= 0.5 ) ? (l*(1+s)) : (l - l*s + s);
var m1 = 2.0*l - m2;
var _value = function(n1,n2,hue){
if( hue > 360 ) hue -= 360; else
if( hue < 0 ) hue += 360;
if( hue < 60 ) return n1 + hue * ( n2-n1 )/60.0;
if( hue < 180 ) return n2;
if( hue < 240 ) return n1 + ( 240-hue ) * ( n2-n1 )/60.0;
return n1;
};
var r = _value( m1, m2, h + 120 );
var g = _value( m1, m2, h );
var b = _value( m1, m2, h - 120 );
return {
r:Math.round(255 * r), g:Math.round(255 * g), b:Math.round(255 * b)
};
};
var Grabbed = function(){};
var initHlsTab = function(container, cb){
var canvas = container.element('canvasHls');
var context = canvas.getContext('2d');
var hls_blank = {r:0, g:0, b:0};
var LSs = [];
var H = 0;
var S = 0;
var V = 0;
canvas.addEventListener(
'mousedown', function(ev){
var px = ev.clientX;
var py = ev.clientY;
Grabbed = function(ev){
var mx = ev.clientX;
var my = ev.clientY;
var w = px - mx;
px = mx;
py = my;
H = (H + w) % 360;
update(H);
};
}, false
);
var grayImage = (
function(){
var R = context.createImageData(256,256);
var data = R.data;
return R;
}
)();
var drawH = function(imageData, opt){
var hue = opt.hue;
var dWidth = imageData.width;
var dHeight = imageData.height;
var data = imageData.data;
var rox = opt.ox;
var roy = opt.oy;
var width = opt.width;
var height = opt.height;
var ox = Math.floor(width * 0.5);
var oy = Math.floor(height * 0.45);
var rad2 = Math.PI * 2;
for(var y=0; y < height; y++){
var ry = (
(y <= oy)
? (oy - y - 1) / oy
: (y - oy) / (height - oy)
);
var ew = Math.round(width * Math.sqrt(1 - ry * ry));
var minX = Math.floor((width - ew) / 2);
var maxX = width - minX;
var yy = y - oy;
for(var x=minX; x < maxX; x++){
var base = 4 * (rox + x + (roy + y) * dWidth);
var r = 255;
var g = 127;
var b = 0;
var xx = (x - ox) * height / width;
var rr = Math.sqrt(xx * xx + yy * yy);
var rad = Math.acos(xx / rr);
var deg = Math.round(rad / rad2 * 360);
//var alp = Math.round(255 * rr / ew * 2);
var alp = 255;
if(yy > 1) deg = 180 - (180 + deg) % 360;
var rgb = hls2rgb({h: (hue + deg) % 360, s:1, l:0.5});
data[base + 0] = rgb.r;
data[base + 1] = rgb.g;
data[base + 2] = rgb.b;
data[base + 3] = alp;
}
}
};
var drawLs = function(imageData, opt){
var data = imageData.data;
var dWidth = imageData.width;
var dHeight = imageData.height;
var h = opt.hue;
var ox = opt.ox;
var oy = opt.oy;
var size = opt.size;
var hsize = Math.floor(size / 2);
var size_ = size - 1;
var hsize_ = hsize - 1;
var rgb = hls2rgb({h:h,s:1,l:0.5});
var nrgb = hls2rgb({h:(h + 180) % 360, s:1, l:0.5});
for(var y = 0; y < size; y++){
var l = size_ - y;
var base = null;
var xa = null, ya = null;
var r, g, b, gg;
var ww = Math.round(size * (hsize - Math.abs(y - hsize)) / hsize);
var x = Math.floor( (size - ww) / 2 );
var xMax = size - x;
for(; x < hsize; x++){
xa = 1 - (hsize - x) / ww * 2;
if(y < hsize_){
ya = y / hsize_;
r = (nrgb.r * ya + size_ * (1 - ya)) * (1 - xa);
g = (nrgb.g * ya + size_ * (1 - ya)) * (1 - xa);
b = (nrgb.b * ya + size_ * (1 - ya)) * (1 - xa);
} else {
ya = (size - y) / hsize_;
r = nrgb.r * ya * (1 - xa);
g = nrgb.g * ya * (1 - xa);
b = nrgb.b * ya * (1 - xa);
}
gg = l * xa;
base = 4 * ((ox + x) + (oy + y) * dWidth);
data[base + 0] = Math.round(gg + r);
data[base + 1] = Math.round(gg + g);
data[base + 2] = Math.round(gg + b);
data[base + 3] = 255;
}
for(;x < xMax; x++){
xa = x == xMax ? 0 : 1 - (x - hsize) / ww * 2;
if(y < hsize_){
ya = y / hsize_;
r = (rgb.r * ya + size_ * (1 - ya)) * (1 - xa);
g = (rgb.g * ya + size_ * (1 - ya)) * (1 - xa);
b = (rgb.b * ya + size_ * (1 - ya)) * (1 - xa);
} else {
ya = (size - y) / hsize_;
r = rgb.r * ya * (1 - xa);
g = rgb.g * ya * (1 - xa);
b = rgb.b * ya * (1 - xa);
}
gg = l * xa;
base = 4 * ((ox + x) + (oy + y) * dWidth);
data[base + 0] = Math.round(gg + r);
data[base + 1] = Math.round(gg + g);
data[base + 2] = Math.round(gg + b);
data[base + 3] = 255;
}
}
};
var update = function(h){
var imageData = context.createImageData(360,360);
drawH(imageData, {
hue: h,
ox: 84,
oy: 272,
width: 192,
height: 64
});
drawLs(imageData, {
hue: h,
ox: 52,
oy: 32,
size: 256
});
context.putImageData(imageData, 0, 0);
};
update(H);
};
var writeCloser = function(canvas){
var context = canvas.getContext('2d');
context.fillStyle = 'rgb(255,0,0)';
context.fillRect(0,0,canvas.width,canvas.height);
context.strokeStyle = 'rgb(255,255,255)';
context.lineWidth = 2;
context.beginPath();
context.moveTo(4,4);
context.lineTo(canvas.width - 4, canvas.height - 4);
context.stroke();
context.beginPath();
context.moveTo(4,canvas.width - 4);
context.lineTo(canvas.height - 4, 4);
context.stroke();
};
var createColorPicker = function(cb){
var container = makeContainer();
var elem = container.root;
initHlsTab(container, cb);
var closer = container.element('closer');
writeCloser(closer);
var listeners = [
['mouseup', function(){
Grabbed = function(){};
}, false],
['mousemove', function(ev){
Grabbed(ev);
}, false]
];
for(var i=0,l=listeners.length;i<l;i++){
window.addEventListener.apply(window, listeners[i]);
}
return {
close: function(){
cb(null);
this.hide();
},
show: function(){
elem.style.display = 'block';
},
hide: function(){
elem.style.display = 'none';
},
remove: function(){
for(var i=0,l=listeners.length;i<l;i++){
window.removeEventListener.apply(window, listeners[i]);
}
elem.parentNode.removeChild(elem);
},
closer: closer,
container: container,
element: elem
};
};
if(TEST_MODE)(
function(picker){
var elem = picker.element;
picker.closer.addEventListener(
'mousedown', function(ev){
picker.close();
picker.remove();
}, false
);
document.body.appendChild(elem);
elem.style.cssText = ""+
" position: fixed; " +
" z-index: 65536; " +
" border: solid black 2px; " +
" border-color: #DDD #666 #666 #DDD; " +
" background: #CCC; " +
" background: rgba(191,191,191,0.85);" +
" -moz-border-radius: 10px; " +
"-webkit-border-radius: 10px; " +
" border-radius: 10px; " +
" padding: 5px " +
"";
var y = ((window.innerHeight-elem.offsetHeight)/2);
var x = ((window.innerWidth -elem.offsetWidth) /2);
elem.style.top = y + 'px';
elem.style.left = x + 'px';
})(createColorPicker(
function(rgb){
if(window.console) console.log(rgb);
}
));
window[NS] = {
create: createColorPicker
};
})();