New color picker component
This commit is contained in:
parent
4d96c30b8d
commit
9823c91d5e
11 changed files with 121 additions and 182 deletions
|
|
@ -107,7 +107,7 @@ export default Em.Component.extend({
|
|||
},
|
||||
|
||||
tabList: ["Lights", "Music"],
|
||||
selectedTab: 0,
|
||||
selectedTab: 1,
|
||||
tabData: function(){
|
||||
var tabData = [], selectedTab = this.get('selectedTab');
|
||||
|
||||
|
|
|
|||
66
app/components/color-picker.js
Normal file
66
app/components/color-picker.js
Normal 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];
|
||||
}
|
||||
});
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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')){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1
app/templates/components/color-picker.hbs
Normal file
1
app/templates/components/color-picker.hbs
Normal file
|
|
@ -0,0 +1 @@
|
|||
<canvas id="picker" width="200" height="200"></canvas>
|
||||
|
|
@ -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"}}
|
||||
|
|
|
|||
|
|
@ -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 |
26
tests/integration/components/color-picker-test.js
Normal file
26
tests/integration/components/color-picker-test.js
Normal 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
152
vendor/colorpicker.js
vendored
|
|
@ -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)
|
||||
*/
|
||||
|
||||
})();
|
||||
Reference in a new issue