Created
December 16, 2015 22:41
-
-
Save tonypatton/bf4e13cabf03fc4a1777 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
// This is a gist of https://github.com/bgrins/colorwheel-1k/blob/master/1k.js | |
// this was created to answer a SO question | |
// http://stackoverflow.com/questions/34321546/change-value-programmatically | |
// 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; | |
// Removed the NOT bitwise operators to reduce rounding issues | |
return [r, g, b, "rgb("+ math.round(r) + "," + math.round(g) + "," + math.round(b) + ")"]; | |
} | |
// Created a modified version of the HSV to RGB conversion function in TinyColor | |
// https://github.com/bgrins/TinyColor/blob/master/tinycolor.js | |
function rgbToHsv(r, g, b) { | |
r = r / two55; | |
g = g / two55; | |
b = b / two55; | |
var max = math.max(r, g, b), min = math.min(r, g, b); | |
var h, s, v = max; | |
var d = max - min; | |
s = max === 0 ? 0 : d / max; | |
if(max == min) { | |
h = 0; // achromatic | |
} | |
else { | |
switch(max) { | |
case r: h = (g - b) / d + (g < b ? 6 : 0); break; | |
case g: h = (b - r) / d + 2; break; | |
case b: h = (r - g) / d + 4; break; | |
} | |
h /= 6; | |
} | |
return { h: h, s: s, v: v }; | |
} | |
// This function converts the RGB to HSV and uses the HSV to set the | |
// currentX, currentY, and input.value (saturation) values. | |
function update(r,g,b){ | |
var hsv = rgbToHsv(r,g,b); | |
var newD = math.round(math.pow(radius * hsv.s, 2)); | |
var newTheta = (hsv.h * PI2) - PI; | |
currentX = math.round(math.sqrt(newD) * math.cos(newTheta)); | |
currentY = math.round(math.sqrt(newD) * math.sin(newTheta)); | |
input.value = hsv.v * 100; | |
redraw(0); | |
} | |
// Kick everything off with our color | |
update(255, 255, 80); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment