Changeset - 0bb3051fd6fd
[Not reviewed]
default
0 1 1
Drew Perttula - 8 years ago 2017-06-05 08:28:50
drewp@bigasterisk.com
move colorpick to its own file
Ignore-this: 1e6572cf19a17c861e22ed1789206c39
2 files changed with 223 insertions and 225 deletions:
0 comments (0 inline, 0 general)
light9/web/light9-color-picker.html
Show inline comments
 
new file 100644
 
<link rel="import" href="/lib/polymer/polymer.html">
 

	
 
<dom-module id="light9-color-picker">
 
  <template>
 
    <style>
 
     :host {
 
         position: relative;
 
     }
 
     #smallRainbowComp, #largeRainbowComp {
 
         display: inline-block;
 
         overflow: hidden;
 
         position: relative;
 
     }
 
     #smallRainbow {
 
         background: url(/colorpick_rainbow_small.png);
 
         width: 150px; 
 
         height: 30px;
 
     }
 
     #largeRainbow {
 
         background: url(/colorpick_rainbow_large.png);
 
         width: 400px; 
 
         height: 200px;
 
     }
 
     #smallCrosshair, #largeCrosshair {
 
         position: absolute;
 
         left: -60px;
 
         top: -62px;
 
         pointer-events: none;
 
     }
 
     #smallCrosshair {
 
         background: url(/colorpick_crosshair_small.svg);
 
         /* this can't be too tall, or chrome will cull it in the 
 
            second column if its top goes above the top of the columns */
 
         width: 400px; 
 
         height: 60px;
 
     }
 
     #largeCrosshair {
 
         background: url(/colorpick_crosshair_large.svg);
 
         width: 1000px; 
 
         height: 1000px;
 
     }
 
     #largeRainbowComp {
 
         display: none;
 
         position: absolute;
 
         border: 4px solid #545454;
 
         box-shadow: 8px 11px 40px 0px rgba(0, 0, 0, 0.74);
 
         z-index: 10;
 
         left: -50px;
 
         top: -110px;
 
     }
 
    </style>
 
    <div id="smallRainbowComp">
 
      <div id="smallRainbow" on-mouseenter="onEnterSmall"></div>
 
      <div id="smallCrosshair"></div>
 
    </div>
 
    <div id="largeRainbowComp">
 
      <div id="largeRainbow"
 
           on-mousemove="onCanvasMove"
 
           on-mouseup="hideLarge"
 
           on-mouseleave="hideLarge"></div>
 
      <div id="largeCrosshair"></div>
 
    </div>
 
  </template>
 
  <script>
 
   class RainbowCanvas {
 
       constructor(url, size) {
 
           this.size = size;
 
           var elem = document.createElement('canvas');
 
           elem.width = size[0];
 
           elem.height = size[1];
 

	
 
           this.ctx = elem.getContext('2d');
 

	
 
           this.colorPos = {} // color: pos
 
           
 
           var img = new Image();
 
           img.onload = function() {
 
               this.ctx.drawImage(img, 0, 0);
 
               this._readImage();
 
           }.bind(this);
 
           img.src = url;
 
       }
 
       _readImage() {
 
           var data = this.ctx.getImageData(
 
               0, 0, this.size[0], this.size[1]).data;
 
           for (var y = 0; y < this.size[1]; y+=1) {
 
               for (var x = 0; x < this.size[0]; x+=1) {
 
                   var base = (y * this.size[0] + x) * 4;
 
                   var c = this._hexFromRgb(
 
                       data[base + 0], data[base + 1], data[base + 2]);
 
                   this.colorPos[c] = [x, y];
 
               }
 
           }
 
       }
 
       _hexFromRgb(r, g, b) {
 
           var hex = function (x) {
 
               return ('00' + x.toString(16)).slice(-2);
 
           };
 
           return '#' + hex(r) + hex(g) + hex(b);
 
       }
 
       colorAt(pos) {
 
           var data = this.ctx.getImageData(pos[0], pos[1], 1, 1).data;
 
           return this._hexFromRgb(data[0], data[1], data[2]);
 
       }
 
       _fullBrightness(r, g, b) {
 
           var high = Math.max(r, g, b);
 
           if (high == 0) {
 
               return [0, 0, 0];
 
           }
 
           return [Math.floor(255 * r / high),
 
                   Math.floor(255 * g / high),
 
                   Math.floor(255 * b / high)];
 
       }
 
       posFor(color) {
 
           let r = parseInt(color.substr(1, 2), 16),
 
               g = parseInt(color.substr(3, 2), 16),
 
               b = parseInt(color.substr(5, 2), 16);
 

	
 
           let bright = this._fullBrightness(r, g, b);
 
           r = bright[0]; g = bright[1]; b = bright[2];
 
           
 
           // We may not have a match for this color exactly (e.g. on
 
           // the small image), so we have to search for a near one.
 
           
 
           // 0, 1, -1, 2, -2, ...
 
           let walk = function(x) { return -x + (x > 0 ? 0 : 1); }
 
           
 
           var radius = 8;
 
           for (var dr = 0; dr < radius; dr = walk(dr)) {
 
               for (var dg = 0; dg < radius; dg = walk(dg)) {
 
                   for (var db = 0; db < radius; db = walk(db)) {
 
                       // Don't need bounds check- out of range
 
                       // corrupt colors just won't match.
 
                       color = this._hexFromRgb(r + dr, g + dg, b + db);
 
                       var pos = this.colorPos[color];
 
                       if (pos !== undefined) {
 
                           return pos;
 
                       }
 
                   }
 
               }
 
           }
 
           throw new Error('no match');
 
       }
 
   }
 
   
 
   Polymer({
 
       is: "light9-color-picker",
 
       properties: {
 
           value: { type: String, notify: true },
 
       },
 
       observers: ['updateSmall(value)'],
 
       attached: function() {
 
           if (!window.pickerCanvases) {
 
               window.pickerCanvases = {
 
                   large: new RainbowCanvas(
 
                       '/colorpick_rainbow_large.png', [400, 200]),
 
                   small: new RainbowCanvas(
 
                       '/colorpick_rainbow_small.png', [150, 30]),
 
               };
 
           }
 
           this.large = window.pickerCanvases.large;
 
           this.small = window.pickerCanvases.small;
 
       },
 
       updateSmall: function(value) {
 
           try {
 
               var pos = this.small.posFor(value);
 
           } catch(e) {
 
               this.moveSmallCrosshair([-999, -999]);
 
               return;
 
           }
 
           this.moveSmallCrosshair(pos);
 
       },
 
       _floatLarge: function() {
 
           // Large might span multiple columns, and chrome won't
 
           // send events for those parts. Workaround: take it out of
 
           // the columns.
 
           let large = this.$.largeRainbowComp;
 
           let rect = this.$.smallRainbowComp.getBoundingClientRect();
 
           document.body.append(large);
 
           large.style.position = 'fixed';
 
           large.style.left = (rect.left - 100) + 'px';
 
           large.style.top = (rect.top - 50) + 'px';
 
       },
 
       showLarge: function() {
 
           this._floatLarge();
 
           this.$.largeRainbowComp.style.display = 'block';
 
           try {
 
               this.moveLargeCrosshair(this.large.posFor(this.value));
 
           } catch(e) {
 
               this.moveLargeCrosshair([-999, -999]);
 
               return;
 
           }
 
       },
 
       hideLarge: function() {
 
           this.$.largeRainbowComp.style.display = 'none';
 
       },
 
       onEnterSmall: function() {
 
           // not if we just closed the large one
 
           this.showLarge();
 
       },
 
       moveLargeCrosshair: function(pos, _elem) {
 
           _elem = _elem || this.$.largeCrosshair;
 
           _elem.style.left = (pos[0] - _elem.offsetWidth / 2) + 'px';
 
           _elem.style.top = (pos[1] - _elem.offsetHeight / 2) + 'px';
 
       },
 
       moveSmallCrosshair: function(pos) {
 
           this.moveLargeCrosshair(pos, this.$.smallCrosshair);
 
       },
 
       onCanvasMove: function(ev) {
 
           if (ev.buttons != 1) {
 
               return;
 
           }
 
           var canvas = this.$.largeRainbow;
 
           var pos = [ev.offsetX - canvas.offsetLeft,
 
                      ev.offsetY - canvas.offsetTop];
 
           this.moveLargeCrosshair(pos);
 
           this.value = this.large.colorAt(pos);
 
       },
 
   });
 
  </script>
 
</dom-module>
 

	
light9/web/live/index.html
Show inline comments
 
@@ -16,234 +16,10 @@
 
    <script src="/lib/underscore/underscore-min.js"></script>
 
    <link rel="import" href="../rdfdb-synced-graph.html">
 
    <link rel="import" href="/resource-display.html">
 
    <link rel="import" href="/light9-color-picker.html">
 
  </head>
 
  <body>
 

	
 
    <dom-module id="light9-color-picker">
 
      <template>
 
        <style>
 
         :host {
 
             position: relative;
 
         }
 
         #smallRainbowComp, #largeRainbowComp {
 
             display: inline-block;
 
             overflow: hidden;
 
             position: relative;
 
         }
 
         #smallRainbow {
 
             background: url(/colorpick_rainbow_small.png);
 
             width: 150px; 
 
             height: 30px;
 
         }
 
         #largeRainbow {
 
             background: url(/colorpick_rainbow_large.png);
 
             width: 400px; 
 
             height: 200px;
 
         }
 
         #smallCrosshair, #largeCrosshair {
 
             position: absolute;
 
             left: -60px;
 
             top: -62px;
 
             pointer-events: none;
 
         }
 
         #smallCrosshair {
 
             background: url(/colorpick_crosshair_small.svg);
 
             /* this can't be too tall, or chrome will cull it in the 
 
                second column if its top goes above the top of the columns */
 
             width: 400px; 
 
             height: 60px;
 
         }
 
         #largeCrosshair {
 
             background: url(/colorpick_crosshair_large.svg);
 
             width: 1000px; 
 
             height: 1000px;
 
         }
 
         #largeRainbowComp {
 
             display: none;
 
             position: absolute;
 
             border: 4px solid #545454;
 
             box-shadow: 8px 11px 40px 0px rgba(0, 0, 0, 0.74);
 
             z-index: 10;
 
             left: -50px;
 
             top: -110px;
 
         }
 
        </style>
 
        <div id="smallRainbowComp">
 
          <div id="smallRainbow" on-mouseenter="onEnterSmall"></div>
 
          <div id="smallCrosshair"></div>
 
        </div>
 
        <div id="largeRainbowComp">
 
          <div id="largeRainbow"
 
                  on-mousemove="onCanvasMove"
 
                  on-mouseup="hideLarge"
 
                  on-mouseleave="hideLarge"></div>
 
          <div id="largeCrosshair"></div>
 
        </div>
 
      </template>
 
      <script>
 
       HTMLImports.whenReady(function () {
 
           class RainbowCanvas {
 
               constructor(url, size) {
 
                   this.size = size;
 
                   var elem = document.createElement('canvas');
 
                   elem.width = size[0];
 
                   elem.height = size[1];
 

	
 
                   this.ctx = elem.getContext('2d');
 

	
 
                   this.colorPos = {} // color: pos
 
                   
 
                   var img = new Image();
 
                   img.onload = function() {
 
                       this.ctx.drawImage(img, 0, 0);
 
                       this._readImage();
 
                   }.bind(this);
 
                   img.src = url;
 
               }
 
               _readImage() {
 
                   var data = this.ctx.getImageData(
 
                       0, 0, this.size[0], this.size[1]).data;
 
                   for (var y = 0; y < this.size[1]; y+=1) {
 
                       for (var x = 0; x < this.size[0]; x+=1) {
 
                           var base = (y * this.size[0] + x) * 4;
 
                           var c = this._hexFromRgb(data[base + 0],
 
                                                    data[base + 1],
 
                                                    data[base + 2]);
 
                           this.colorPos[c] = [x, y];
 
                       }
 
                   }
 
               }
 
               _hexFromRgb(r, g, b) {
 
                   var hex = function (x) {
 
                       return ('00' + x.toString(16)).slice(-2);
 
                   };
 
                   return '#' + hex(r) + hex(g) + hex(b);
 
               }
 
               colorAt(pos) {
 
                   var data = this.ctx.getImageData(pos[0], pos[1], 1, 1).data;
 
                   return this._hexFromRgb(data[0], data[1], data[2]);
 
               }
 
               _fullBrightness(r, g, b) {
 
                   var high = Math.max(r, g, b);
 
                   if (high == 0) {
 
                       return [0, 0, 0];
 
                   }
 
                   return [Math.floor(255 * r / high),
 
                           Math.floor(255 * g / high),
 
                           Math.floor(255 * b / high)];
 
               }
 
               posFor(color) {
 
                   let r = parseInt(color.substr(1, 2), 16),
 
                       g = parseInt(color.substr(3, 2), 16),
 
                       b = parseInt(color.substr(5, 2), 16);
 

	
 
                   let bright = this._fullBrightness(r, g, b);
 
                   r = bright[0]; g = bright[1]; b = bright[2];
 
                   
 
                   // We may not have a match for this color exactly (e.g. on
 
                   // the small image), so we have to search for a near one.
 
                   
 
                   // 0, 1, -1, 2, -2, ...
 
                   let walk = function(x) { return -x + (x > 0 ? 0 : 1); }
 
                   
 
                   var radius = 8;
 
                   for (var dr = 0; dr < radius; dr = walk(dr)) {
 
                       for (var dg = 0; dg < radius; dg = walk(dg)) {
 
                           for (var db = 0; db < radius; db = walk(db)) {
 
                               // Don't need bounds check- out of range
 
                               // corrupt colors just won't match.
 
                               color = this._hexFromRgb(r + dr, g + dg, b + db);
 
                               var pos = this.colorPos[color];
 
                               if (pos !== undefined) {
 
                                   return pos;
 
                               }
 
                           }
 
                       }
 
                   }
 
                   throw new Error('no match');
 
               }
 
           }
 
           
 
           Polymer({
 
               is: "light9-color-picker",
 
               properties: {
 
                   value: { type: String, notify: true },
 
               },
 
               observers: ['updateSmall(value)'],
 
               attached: function() {
 
                   if (!window.pickerCanvases) {
 
                       window.pickerCanvases = {
 
                           large: new RainbowCanvas(
 
                               '/colorpick_rainbow_large.png', [400, 200]),
 
                           small: new RainbowCanvas(
 
                               '/colorpick_rainbow_small.png', [150, 30]),
 
                       };
 
                   }
 
                   this.large = window.pickerCanvases.large;
 
                   this.small = window.pickerCanvases.small;
 
               },
 
               updateSmall: function(value) {
 
                   try {
 
                       var pos = this.small.posFor(value);
 
                   } catch(e) {
 
                       this.moveSmallCrosshair([-999, -999]);
 
                       return;
 
                   }
 
                   this.moveSmallCrosshair(pos);
 
               },
 
               _floatLarge: function() {
 
                   // Large might span multiple columns, and chrome won't
 
                   // send events for those parts. Workaround: take it out of
 
                   // the columns.
 
                   let large = this.$.largeRainbowComp;
 
                   let rect = this.$.smallRainbowComp.getBoundingClientRect();
 
                   document.body.append(large);
 
                   large.style.position = 'fixed';
 
                   large.style.left = (rect.left - 100) + 'px';
 
                   large.style.top = (rect.top - 50) + 'px';
 

	
 
               },
 
               showLarge: function() {
 
                   this._floatLarge();
 
                   this.$.largeRainbowComp.style.display = 'block';
 
                   try {
 
                       this.moveLargeCrosshair(this.large.posFor(this.value));
 
                   } catch(e) {
 
                       this.moveLargeCrosshair([-999, -999]);
 
                       return;
 
                   }
 
               },
 
               hideLarge: function() {
 
                   this.$.largeRainbowComp.style.display = 'none';
 
               },
 
               onEnterSmall: function() {
 
                   // not if we just closed the large one
 
                   this.showLarge();
 
               },
 
               moveLargeCrosshair: function(pos, _elem) {
 
                   _elem = _elem || this.$.largeCrosshair;
 
                   _elem.style.left = (pos[0] - _elem.offsetWidth / 2) + 'px';
 
                   _elem.style.top = (pos[1] - _elem.offsetHeight / 2) + 'px';
 
               },
 
               moveSmallCrosshair: function(pos) {
 
                   this.moveLargeCrosshair(pos, this.$.smallCrosshair);
 
               },
 
               onCanvasMove: function(ev) {
 
                   if (ev.buttons != 1) {
 
                       return;
 
                   }
 
                   var canvas = this.$.largeRainbow;
 
                   var pos = [ev.offsetX - canvas.offsetLeft,
 
                              ev.offsetY - canvas.offsetTop];
 
                   this.moveLargeCrosshair(pos);
 
                   this.value = this.large.colorAt(pos);
 
               },
 
           });
 
       });
 
      </script>
 
    </dom-module>
 
    
 
       
 
    <dom-module id="light9-live-control">
 
      <template>
 
        <style>
0 comments (0 inline, 0 general)