Changeset - a67d1a95ebf9
[Not reviewed]
default
0 1 0
drewp@bigasterisk.com - 7 years ago 2018-05-31 08:14:37
drewp@bigasterisk.com
1 file changed with 208 insertions and 208 deletions:
0 comments (0 inline, 0 general)
light9/web/light9-color-picker.html
Show inline comments
 
@@ -19,7 +19,7 @@
 
         width: 1000px; 
 
         height: 1000px;
 
     }
 
    #largeRainbowComp {
 
     #largeRainbowComp {
 
         display: inline-block;
 
         overflow: hidden;
 
         position: relative;
 
@@ -48,8 +48,8 @@
 
  </template>
 
  <script>
 
   class Light9ColorPickerFloat extends Polymer.Element {
 
       static get is() { return "light9-color-picker-float"; }
 
       // more methods get added by Light9ColorPicker
 
     static get is() { return "light9-color-picker-float"; }
 
     // more methods get added by Light9ColorPicker
 
   }
 
   customElements.define(Light9ColorPickerFloat.is, Light9ColorPickerFloat);
 
  </script>
 
@@ -103,236 +103,236 @@
 
     #large {
 
         display: none;
 
     }
 
     </style>
 
     <div id="smallRainbowComp">
 
     <div id="smallRainbow" on-mouseenter="onEnterSmall"></div>
 
     <div id="smallCrosshair"></div>
 
     </div>
 
     <span id="vee">
 
     V:
 
       <paper-slider min="0"
 
                     max="255"
 
                     step="1"
 
                     value="{{sliderWriteValue}}"
 
                     immediate-value="{{value}}"></paper-slider>
 
     </span>
 
     <!--  Large might span multiple columns, and chrome won't
 
      send events for those parts. Workaround: take it out of
 
      the columns. -->
 
     <light9-color-picker-float id="large"></light9-color-picker-float>
 
    </style>
 
    <div id="smallRainbowComp">
 
      <div id="smallRainbow" on-mouseenter="onEnterSmall"></div>
 
      <div id="smallCrosshair"></div>
 
    </div>
 
    <span id="vee">
 
      V:
 
      <paper-slider min="0"
 
                    max="255"
 
                    step="1"
 
                    value="{{sliderWriteValue}}"
 
                    immediate-value="{{value}}"></paper-slider>
 
    </span>
 
    <!--  Large might span multiple columns, and chrome won't
 
         send events for those parts. Workaround: take it out of
 
         the columns. -->
 
    <light9-color-picker-float id="large"></light9-color-picker-float>
 
  </template>
 
  <script src="/lib/color/one-color.js"></script>
 
  <script>
 
   class RainbowCanvas {
 
       constructor(url, size, onReady) {
 
           this.size = size;
 
           var elem = document.createElement('canvas');
 
           elem.width = size[0];
 
           elem.height = size[1];
 
     constructor(url, size, onReady) {
 
       this.size = size;
 
       var elem = document.createElement('canvas');
 
       elem.width = size[0];
 
       elem.height = size[1];
 

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

	
 
           this.colorPos = {} // color: pos
 
           this._loaded = false;
 
           this._loadWatchers = []; // callbacks
 
           
 
           var img = new Image();
 
           img.onload = function() {
 
               this.ctx.drawImage(img, 0, 0);
 
               this._readImage();
 
               this._loaded = true;
 
               this._loadWatchers.forEach(function(cb) { cb(); });
 
               this._loadWatchers = [];
 
           }.bind(this);
 
           img.src = url;
 
       }
 
       onLoad(cb) {
 
           // we'll call this when posFor is available
 
           if (this._loaded) {
 
               cb();
 
               return;
 
           }
 
           this._loadWatchers.push(cb);
 
       this.colorPos = {} // color: pos
 
       this._loaded = false;
 
       this._loadWatchers = []; // callbacks
 
       
 
       var img = new Image();
 
       img.onload = function() {
 
         this.ctx.drawImage(img, 0, 0);
 
         this._readImage();
 
         this._loaded = true;
 
         this._loadWatchers.forEach(function(cb) { cb(); });
 
         this._loadWatchers = [];
 
       }.bind(this);
 
       img.src = url;
 
     }
 
     onLoad(cb) {
 
       // we'll call this when posFor is available
 
       if (this._loaded) {
 
         cb();
 
         return;
 
       }
 
       _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 = one.color([data[base + 0],
 
                                      data[base + 1],
 
                                      data[base + 2], 255]).hex();
 
                   this.colorPos[c] = [x, y];
 
               }
 
           }
 
       }       
 
       colorAt(pos) {
 
           var data = this.ctx.getImageData(pos[0], pos[1], 1, 1).data;
 
           return one.color([data[0], data[1], data[2], 255]).hex();
 
       this._loadWatchers.push(cb);
 
     }
 
     _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 = one.color([data[base + 0],
 
                              data[base + 1],
 
                              data[base + 2], 255]).hex();
 
           this.colorPos[c] = [x, y];
 
         }
 
       }
 
     }       
 
     colorAt(pos) {
 
       var data = this.ctx.getImageData(pos[0], pos[1], 1, 1).data;
 
       return one.color([data[0], data[1], data[2], 255]).hex();
 
     }
 
     posFor(color) {
 
       if (color == '#000000') {
 
         throw new Error('no match');
 
       }
 
       posFor(color) {
 
           if (color == '#000000') {
 
               throw new Error('no match');
 
       
 
       let bright = one.color(color).value(1).hex();
 
       let r = parseInt(bright.substr(1, 2), 16),
 
           g = parseInt(bright.substr(3, 2), 16),
 
           b = parseInt(bright.substr(5, 2), 16);
 
       
 
       // 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 = one.color([r + dr, g + dg, b + db, 255]).hex();
 
             var pos = this.colorPos[color];
 
             if (pos !== undefined) {
 
               return pos;
 
             }
 
           }
 
           
 
           let bright = one.color(color).value(1).hex();
 
           let r = parseInt(bright.substr(1, 2), 16),
 
               g = parseInt(bright.substr(3, 2), 16),
 
               b = parseInt(bright.substr(5, 2), 16);
 
           
 
           // 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 = one.color([r + dr, g + dg, b + db, 255]).hex();
 
                       var pos = this.colorPos[color];
 
                       if (pos !== undefined) {
 
                           return pos;
 
                       }
 
                   }
 
               }
 
           }
 
           throw new Error('no match');
 
         }
 
       }
 
       throw new Error('no match');
 
     }
 
   }
 

	
 
   
 
   class Light9ColorPicker extends Polymer.Element {
 
       static get is() { return "light9-color-picker"; }
 
       static get properties() { return {
 
           color: { type: String, notify: true },
 
           hueSatColor: { type: String, notify: true, value: null },
 
           value: { type: Number, notify: true }, // 0..255
 
           sliderWriteValue: { type: Number, notify: true },
 
       }; }
 
       static get observers() { return [
 
           'readColor(color)',
 
           'onValue(value)',
 
           'writeColor(hueSatColor, value)'
 
       ]; }
 
       displayed() {
 
           // call this when the smallcrosshair first has a size
 
           this._updateSmallCrosshair();
 
     static get is() { return "light9-color-picker"; }
 
     static get properties() { return {
 
       color: { type: String, notify: true },
 
       hueSatColor: { type: String, notify: true, value: null },
 
       value: { type: Number, notify: true }, // 0..255
 
       sliderWriteValue: { type: Number, notify: true },
 
     }; }
 
     static get observers() { return [
 
       'readColor(color)',
 
       'onValue(value)',
 
       'writeColor(hueSatColor, value)'
 
     ]; }
 
     displayed() {
 
       // call this when the smallcrosshair first has a size
 
       this._updateSmallCrosshair();
 
     }
 
     ready() {
 
       super.ready();
 
       if (!window.pickerCanvases) {
 
         window.pickerCanvases = {
 
           large: new RainbowCanvas(
 
             '/colorpick_rainbow_large.png', [400, 200]),
 
           small: new RainbowCanvas(
 
             '/colorpick_rainbow_small.png', [150, 30]),
 
         };
 
       }
 
       ready() {
 
           super.ready();
 
           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;
 
           this.small.onLoad(function() {
 
               // color may have been set before our image came
 
               this._updateSmallCrosshair();
 
           }.bind(this));
 
           this.$.large.onCanvasMove = this.onCanvasMove.bind(this);
 
           this.$.large.hideLarge = this.hideLarge.bind(this);
 
           document.body.append(this.$.large);
 
           this.$.large.style.display = 'none';
 
       this.large = window.pickerCanvases.large;
 
       this.small = window.pickerCanvases.small;
 
       this.small.onLoad(function() {
 
         // color may have been set before our image came
 
         this._updateSmallCrosshair();
 
       }.bind(this));
 
       this.$.large.onCanvasMove = this.onCanvasMove.bind(this);
 
       this.$.large.hideLarge = this.hideLarge.bind(this);
 
       document.body.append(this.$.large);
 
       this.$.large.style.display = 'none';
 
     }
 
     disconnectedCallback() {
 
       super.disconnectedCallback();
 
       document.body.removeChild(this.$.large);
 
     }
 
     onValue(value) {
 
       let neverBlack = .1 + .9 * value / 255;
 
       this.$.smallRainbow.style.filter = `brightness(${neverBlack})`;
 
     }
 
     writeColor(hueSatColor, value) {
 
       if (hueSatColor === null || this.pauseWrites) { return; }
 
       this.color = one.color(hueSatColor).value(value / 255).hex();
 
     }
 
     readColor(color) {
 
       if (this.$.large.style.display == 'block') {
 
         // for performance, don't do color searches on covered widget
 
         return;
 
       }
 
       disconnectedCallback() {
 
           super.disconnectedCallback();
 
           document.body.removeChild(this.$.large);
 
       }
 
       onValue(value) {
 
           let neverBlack = .1 + .9 * value / 255;
 
           this.$.smallRainbow.style.filter = `brightness(${neverBlack})`;
 
       }
 
       writeColor(hueSatColor, value) {
 
           if (hueSatColor === null || this.pauseWrites) { return; }
 
           this.color = one.color(hueSatColor).value(value / 255).hex();
 
       }
 
       readColor(color) {
 
           if (this.$.large.style.display == 'block') {
 
               // for performance, don't do color searches on covered widget
 
               return;
 
           }
 

	
 
           this.pauseWrites = true;
 
           var colorValue = one.color(color).value() * 255;
 
           // writing back to immediate-value doesn't work on paper-slider
 
           this.sliderWriteValue = colorValue;
 
       this.pauseWrites = true;
 
       var colorValue = one.color(color).value() * 255;
 
       // writing back to immediate-value doesn't work on paper-slider
 
       this.sliderWriteValue = colorValue;
 

	
 
           // don't update this if only the value changed, or we desaturate
 
           this.hueSatColor = one.color(color).value(1).hex();
 
       // don't update this if only the value changed, or we desaturate
 
       this.hueSatColor = one.color(color).value(1).hex();
 

	
 
           this._updateSmallCrosshair();
 
           this.pauseWrites = false;
 
       }
 
       _updateSmallCrosshair() {
 
           try {
 
               var pos = this.small.posFor(this.color);
 
           } catch(e) {
 
               this.moveSmallCrosshair([-999, -999]);
 
               return;
 
           }
 
           this.moveSmallCrosshair(pos);
 
       this._updateSmallCrosshair();
 
       this.pauseWrites = false;
 
     }
 
     _updateSmallCrosshair() {
 
       try {
 
         var pos = this.small.posFor(this.color);
 
       } catch(e) {
 
         this.moveSmallCrosshair([-999, -999]);
 
         return;
 
       }
 
       showLarge(x, y) {
 
           this.$.large.style.display = 'block';
 
           try {
 
               const pos = this.large.posFor(this.color);
 
               this.moveLargeCrosshair(pos);
 
               this.$.large.style.left = (x - Math.max(20, Math.min(380, pos[0]))) + 'px';
 
               this.$.large.style.top = (y - Math.max(20, Math.min(180, pos[1]))) + 'px';
 
           } catch(e) {
 
               this.moveLargeCrosshair([-999, -999]);
 
               this.$.large.style.left = (400 / 2) + 'px';
 
               this.$.large.style.top = (200 / 2) + 'px';
 
               return;
 
           }
 
       }
 
       hideLarge() {
 
           this.$.large.style.display = 'none';
 
           this.readColor(this.color);
 
           this.closeTime = Date.now();
 
       this.moveSmallCrosshair(pos);
 
     }
 
     showLarge(x, y) {
 
       this.$.large.style.display = 'block';
 
       try {
 
         const pos = this.large.posFor(this.color);
 
         this.moveLargeCrosshair(pos);
 
         this.$.large.style.left = (x - Math.max(20, Math.min(380, pos[0]))) + 'px';
 
         this.$.large.style.top = (y - Math.max(20, Math.min(180, pos[1]))) + 'px';
 
       } catch(e) {
 
         this.moveLargeCrosshair([-999, -999]);
 
         this.$.large.style.left = (400 / 2) + 'px';
 
         this.$.large.style.top = (200 / 2) + 'px';
 
         return;
 
       }
 
       onEnterSmall(ev) {
 
           if (this.closeTime && this.closeTime > Date.now() - 500) {
 
               return;
 
           }
 

	
 
           // if scrolling put us here, don't open large. require deliberate entering motion.
 
           
 
           this.showLarge(ev.pageX, ev.pageY);
 
       }
 
       moveLargeCrosshair(pos, _elem) {
 
           _elem = _elem || this.$.large.shadowRoot.querySelector("#largeCrosshair");
 
           _elem.style.left = (pos[0] - _elem.offsetWidth / 2) + 'px';
 
           _elem.style.top = (pos[1] - _elem.offsetHeight / 2) + 'px';
 
     }
 
     hideLarge() {
 
       this.$.large.style.display = 'none';
 
       this.readColor(this.color);
 
       this.closeTime = Date.now();
 
     }
 
     onEnterSmall(ev) {
 
       if (this.closeTime && this.closeTime > Date.now() - 500) {
 
         return;
 
       }
 
       moveSmallCrosshair(pos) {
 
           this.moveLargeCrosshair(pos, this.$.smallCrosshair);
 

	
 
       // if scrolling put us here, don't open large. require deliberate entering motion.
 
       
 
       this.showLarge(ev.pageX, ev.pageY);
 
     }
 
     moveLargeCrosshair(pos, _elem) {
 
       _elem = _elem || this.$.large.shadowRoot.querySelector("#largeCrosshair");
 
       _elem.style.left = (pos[0] - _elem.offsetWidth / 2) + 'px';
 
       _elem.style.top = (pos[1] - _elem.offsetHeight / 2) + 'px';
 
     }
 
     moveSmallCrosshair(pos) {
 
       this.moveLargeCrosshair(pos, this.$.smallCrosshair);
 
     }
 
     onCanvasMove(ev) {
 
       if (ev.buttons != 1) {
 
         return;
 
       }
 
       onCanvasMove(ev) {
 
           if (ev.buttons != 1) {
 
               return;
 
           }
 
           var canvas = this.$.large.shadowRoot.querySelector('#largeRainbow');
 
           var pos = [ev.offsetX - canvas.offsetLeft,
 
                      ev.offsetY - canvas.offsetTop];
 
           this.moveLargeCrosshair(pos);
 
           this.hueSatColor = this.large.colorAt(pos);
 
       var canvas = this.$.large.shadowRoot.querySelector('#largeRainbow');
 
       var pos = [ev.offsetX - canvas.offsetLeft,
 
                  ev.offsetY - canvas.offsetTop];
 
       this.moveLargeCrosshair(pos);
 
       this.hueSatColor = this.large.colorAt(pos);
 

	
 
           // special case: it's useless to adjust the hue/sat of black
 
           if (this.value == 0) {
 
               this.value = 255;
 
           }
 
       // special case: it's useless to adjust the hue/sat of black
 
       if (this.value == 0) {
 
         this.value = 255;
 
       }
 
     }
 
   }
 
   customElements.define(Light9ColorPicker.is, Light9ColorPicker);
 
  </script>
0 comments (0 inline, 0 general)