Mercurial > code > home > repos > light9
changeset 1609:0bb3051fd6fd
move colorpick to its own file
Ignore-this: 1e6572cf19a17c861e22ed1789206c39
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Mon, 05 Jun 2017 08:28:50 +0000 |
parents | faa62d5e84d8 |
children | b0846845c772 |
files | light9/web/light9-color-picker.html light9/web/live/index.html |
diffstat | 2 files changed, 223 insertions(+), 225 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/light9/web/light9-color-picker.html Mon Jun 05 08:28:50 2017 +0000 @@ -0,0 +1,222 @@ +<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> +
--- a/light9/web/live/index.html Mon Jun 05 08:25:28 2017 +0000 +++ b/light9/web/live/index.html Mon Jun 05 08:28:50 2017 +0000 @@ -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>