New color picker component

This commit is contained in:
lone-cloud 2015-09-23 00:02:55 -07:00
parent b5a5178ec6
commit 48c38d630b
11 changed files with 121 additions and 182 deletions

View file

@ -107,7 +107,7 @@ export default Em.Component.extend({
},
tabList: ["Lights", "Music"],
selectedTab: 0,
selectedTab: 1,
tabData: function(){
var tabData = [], selectedTab = this.get('selectedTab');

View file

@ -0,0 +1,66 @@
import Em from 'ember';
export default Em.Component.extend({
classNames:['colorpicker'],
// https://dzone.com/articles/creating-your-own-html5
didInsertElement: function(){
// handle color changes
var self = this,
canvas = Em.$('#picker')[0].getContext('2d'),
image = new Image();
image.src ='assets/images/colorwheel.png';
image.onload = function () {
canvas.drawImage(image, 0, 0, image.width, image.height); // draw the image on the canvas
};
},
// http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
rgbToXy: function(red, green, blue){
var X, Y, Z, x, y;
// normalize
red = Number((red/255).toFixed(2));
green = Number((green/255).toFixed(2));
blue = Number((blue/255).toFixed(2));
// gamma correction
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
// RGB to XYZ
X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
Y = red * 0.283881 + green * 0.668433 + blue * 0.047685;
Z = red * 0.000088 + green * 0.072310 + blue * 0.986039;
x = X / (X + Y + Z);
y = Y / (X + Y + Z);
return [x,y];
},
xyTorgb: function(x, y){
var r, g, b, X, Y, Z, activeLights = this.get('activeLights'), lightsData = this.get('lightsData');
z = 1 - x - y;
Y = lightsData[activeLights[0]].state.bri;
X = (Y / y) * x;
Z = (Y / y) * z;
r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
r = Math.floor(r * 255);
g = Math.floor(g * 255);
b = Math.floor(b * 255);
return [r, g, b];
}
});

View file

@ -12,18 +12,6 @@ export default Em.Component.extend({
isShowingColorPicker: false,
didInsertElement: function(){
// handle color changes
var self = this,
canvas = $('#picker')[0].getContext('2d'),
image = new Image();
image.src ='assets/images/colorwheel.png';
image.onload = function () {
canvas.drawImage(image, 0, 0, image.width, image.height); // draw the image on the canvas
};
},
actions: {
clickLight: function(){
console.log('clickLight');

View file

@ -44,6 +44,12 @@ export default Em.Component.extend({
}
},
didInsertElement: function() {
if(this.get('lightsData')){
this.onLightsDataChange();
}
},
// list of all the lights in the hue system
onLightsDataChange: function(){
if(!this.get('isHovering')){

View file

@ -28,6 +28,10 @@ body {
position: static;
}
.relative {
position: relative;
}
#settings {
cursor: pointer;
position: absolute;
@ -62,7 +66,7 @@ body {
}
.appSettingsItem:hover {
background: darken(white, 10%);
background: darken(white, 20%);
}
.settingsItem.on md-icon.md-default-theme {
@ -188,20 +192,19 @@ md-list-item .md-no-style {
}
.colorpicker {
background-color: #222222;
border-radius: 5px 5px 5px 5px;
box-shadow: 2px 2px 2px #444444;
padding: 10px;
background: rgba(0, 0, 0, 0.6);
box-shadow: 5px 10px 15px 5px rgba(0, 0, 0, 0.3);
color: #FFFFFF;
font-size: 12px;
z-index: 2;
position: absolute;
width: 460px;
width: 220px;
right: 6px;
top: -6px;
}
#picker {
cursor: crosshair;
float: left;
margin: 10px;
border: 0;
}
// LIGHT GROUP
@ -239,7 +242,8 @@ md-slider.md-default-theme .md-thumb:after {
.lightGroup {
margin: 0 auto 0 auto;
.tooltip.top {
margin-top: 8px;
margin-top: -10px;
margin-left: -3px;
}
}
@ -271,7 +275,8 @@ md-slider.md-default-theme .md-thumb:after {
left: 6px;
}
.tooltip.top {
margin-top: -10px;
margin-top: 6px;
margin-left: -5px;
}
}

View file

@ -0,0 +1 @@
<canvas id="picker" width="200" height="200"></canvas>

View file

@ -19,12 +19,12 @@
{{paper-icon icon="color-lens"}}
<p>Color</p>
{{#paper-button raised=true class="color" action="toggleColorpicker"}}{{/paper-button}}
{{#if isShowingColorPicker}}
<div class="colorpicker">
<canvas id="picker" width="300" height="300"></canvas>
</div>
{{/if}}
{{/paper-item}}
<div class="relative">
{{#if isShowingColorPicker}}
{{color-picker lightsData=lightsData activeLights=activeLights}}
{{/if}}
</div>
{{#paper-item class="item"}}
{{paper-icon icon="flare"}}

View file

@ -7,7 +7,6 @@ module.exports = function(defaults) {
});
app.import('vendor/dancer.js');
app.import('vendor/colorpicker.js');
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js');
app.import('bower_components/JavaScript-ID3-Reader/dist/id3-minimized.js');
app.import('bower_components/jquery-mousewheel/jquery.mousewheel.js');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -0,0 +1,26 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('color-picker', 'Integration | Component | color picker', {
integration: true
});
test('it renders', function(assert) {
assert.expect(2);
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{color-picker}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#color-picker}}
template block text
{{/color-picker}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

152
vendor/colorpicker.js vendored
View file

@ -1,152 +0,0 @@
//https://github.com/bgrins/colorwheel-1k/tree/gh-pages
(function() {
// Declare constants and variables to help with minification
// Some of these are inlined (with comments to the side with the actual equation)
var doc = document;
doc.c = doc.createElement;
b.a = b.appendChild;
var width = c.width = c.height = 400,
label = b.a(doc.c("p")),
input = b.a(doc.c("input")),
imageData = a.createImageData(width, width),
pixels = imageData.data,
oneHundred = input.value = input.max = 100,
circleOffset = 10,
diameter = 380, //width-circleOffset*2,
radius = 190, //diameter / 2,
radiusPlusOffset = 200, //radius + circleOffset
radiusSquared = radius * radius,
two55 = 255,
currentY = oneHundred,
currentX = -currentY,
wheelPixel = 16040; // circleOffset*4*width+circleOffset*4;
// Math helpers
var math = Math,
PI = math.PI,
PI2 = PI * 2,
sqrt = math.sqrt,
atan2 = math.atan2;
// Setup DOM properties
b.style.textAlign="center";
label.style.font = "2em courier";
input.type = "range";
// Load color wheel data into memory.
for (y = input.min = 0; y < width; y++) {
for (x = 0; x < width; x++) {
var rx = x - radius,
ry = y - radius,
d = rx * rx + ry * ry,
rgb = hsvToRgb(
(atan2(ry, rx) + PI) / PI2, // Hue
sqrt(d) / radius, // Saturation
1 // Value
);
// Print current color, but hide if outside the area of the circle
pixels[wheelPixel++] = rgb[0];
pixels[wheelPixel++] = rgb[1];
pixels[wheelPixel++] = rgb[2];
pixels[wheelPixel++] = d > radiusSquared ? 0 : two55;
}
}
// Bind Event Handlers
input.onchange = redraw;
c.onmousedown = doc.onmouseup = function(e) {
// Unbind mousemove if this is a mouseup event, or bind mousemove if this a mousedown event
doc.onmousemove = /p/.test(e.type) ? 0 : (redraw(e), redraw);
}
// Handle manual calls + mousemove event handler + input change event handler all in one place.
function redraw(e) {
// Only process an actual change if it is triggered by the mousemove or mousedown event.
// Otherwise e.pageX will be undefined, which will cause the result to be NaN, so it will fallback to the current value
currentX = e.pageX - c.offsetLeft - radiusPlusOffset || currentX;
currentY = e.pageY - c.offsetTop - radiusPlusOffset || currentY;
// Scope these locally so the compiler will minify the names. Will manually remove the 'var' keyword in the minified version.
var theta = atan2(currentY, currentX),
d = currentX * currentX + currentY * currentY;
// If the x/y is not in the circle, find angle between center and mouse point:
// Draw a line at that angle from center with the distance of radius
// Use that point on the circumference as the draggable location
if (d > radiusSquared) {
currentX = radius * math.cos(theta);
currentY = radius * math.sin(theta);
theta = atan2(currentY, currentX);
d = currentX * currentX + currentY * currentY;
}
label.textContent = b.style.background = hsvToRgb(
(theta + PI) / PI2, // Current hue (how many degrees along the circle)
sqrt(d) / radius, // Current saturation (how close to the middle)
input.value / oneHundred // Current value (input type="range" slider value)
)[3];
// Reset to color wheel and draw a spot on the current location.
a.putImageData(imageData, 0, 0);
// Draw the current spot.
// I have tried a rectangle, circle, and heart shape.
/*
// Rectangle:
a.fillStyle = '#000';
a.fillRect(currentX+radiusPlusOffset,currentY+radiusPlusOffset, 6, 6);
*/
/*
// Circle:
a.beginPath();
a.strokeStyle = '#000';
a.arc(~~currentX+radiusPlusOffset,~~currentY+radiusPlusOffset, 4, 0, PI2);
a.stroke();
*/
// Heart:
a.font = "1em arial";
a.fillText("♥", currentX+radiusPlusOffset-4,currentY+radiusPlusOffset+4);
}
// Created a shorter version of the HSV to RGB conversion function in TinyColor
// https://github.com/bgrins/TinyColor/blob/master/tinycolor.js
function hsvToRgb(h, s, v) {
h*=6;
var i = ~~h,
f = h - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s),
mod = i % 6,
r = [v, q, p, p, t, v][mod] * two55,
g = [t, v, v, q, p, p][mod] * two55,
b = [p, p, t, v, v, q][mod] * two55;
return [r, g, b, "rgb("+ ~~r + "," + ~~g + "," + ~~b + ")"];
}
// Kick everything off
redraw(0);
/*
// Just an idea I had to kick everything off with some changing colors…
// Probably no way to squeeze this into 1k, but it could probably be a lot smaller than this:
currentX = currentY = 1;
var interval = setInterval(function() {
currentX--;
currentY*=1.05;
redraw(0)
}, 7);
setTimeout(function() {
clearInterval(interval)
}, 700)
*/
})();