カラーピッカーを作ってみる

  • /*
     * @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
       };
     })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2010/12/28 18:40:37 - 2010-12-28
  2. 2010/12/28 18:37:31 - 2010-12-28
  3. 2010/12/26 17:50:45 - 2010-12-26