/* Purty Picker Copyright 2013 Jayden Seric (MIT license). A super lightweight visual HSL, RGB and hex color picker with a responsive, touch-friendly and customizable UI. Requires jQuery or Zepto with core and event modules. https://github.com/jaydenseric/Purty-Picker */ // DOM ready $(function() { 'use strict'; //-------------------------------------------- Color conversions //---------------------- Convert HSL to RGB // Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c function HSLToRGB(h, s, l) { h /= 360; s /= 100; l /= 100; var r, g, b; if(s == 0) { r = g = b = l; // Achromatic } else { var hueToRGB = function(p, q, t) { if(t < 0) t += 1; if(t > 1) t -= 1; if(t < 1/6) return p + (q - p) * 6 * t; if(t < 1/2) return q; if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; }; var q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q; r = hueToRGB(p, q, h + 1/3); g = hueToRGB(p, q, h); b = hueToRGB(p, q, h - 1/3); } return { red: Math.round(r * 255), green: Math.round(g * 255), blue: Math.round(b * 255) }; } //---------------------- Convert RGB to HSL // Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c function RGBToHSL(r, g, b) { r /= 255, g /= 255, b /= 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if (max == min) { h = s = 0; // Achromatic } else { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 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 { hue: Math.round(h * 360), saturation: Math.round(s * 100), luminosity: Math.round(l * 100) }; } //---------------------- Convert RGB to Hex // Source: http://stackoverflow.com/a/5624139 function RGBToHex(r, g, b) { return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } //---------------------- Convert hex to RGB // Source: http://stackoverflow.com/a/11508164 function hexToRGB(hex) { var bigInt = parseInt(hex.replace('#', ''), 16), r = (bigInt >> 16) & 255, g = (bigInt >> 8) & 255, b = bigInt & 255; return { red: r, green: g, blue: b }; } //---------------------- Convert hex to HSL function hexToHSL(hex) { var RGB = hexToRGB(hex); return RGBToHSL(RGB.red, RGB.green, RGB.blue); } //---------------------- Convert HSL to hex function HSLToHex(h, s, l) { var RGB = HSLToRGB(h, s, l); return RGBToHex(RGB.red, RGB.green, RGB.blue); } //-------------------------------------------- Setup each color picker $.each($('.color-picker'), function() { //---------------------- Find componenets var picker = $(this), formatInput = picker.find('.format'), colorInput = picker.find('.color'), luminosityInput = picker.find('input[type=range]'), spectrum = picker.find('.spectrum'), pin = picker.find('.pin'); //---------------------- Get current color in HSL function getHSL() { var position = picker.find('.pin').position(), width = spectrum.width(), height = spectrum.height(); return { hue: Math.round(position.left / width * 360), saturation: Math.round(position.top / height * 100), luminosity: luminosityInput.val() }; } //---------------------- Output color in desired format function updateColorInput() { var HSL = getHSL(); switch (formatInput.val()) { case 'HSL': colorInput.val('hsl(' + HSL.hue + ', ' + HSL.saturation + '%, ' + HSL.luminosity + '%)'); break; case 'RGB': var RGB = HSLToRGB(HSL.hue, HSL.saturation, HSL.luminosity); colorInput.val('rgb(' + RGB.red + ', ' + RGB.green + ', ' + RGB.blue + ')'); break; case 'Hex': colorInput.val(HSLToHex(HSL.hue, HSL.saturation, HSL.luminosity)); break; } // Trigger color picker change event for custom callbacks picker.trigger('change'); } //---------------------- Set color format formatInput.on('change', function() { updateColorInput(); }); //---------------------- Set color colorInput.on('change', function() { // Get the color values in HSL format var HSL; switch (formatInput.val()) { case 'HSL': var values = $(this).val().match(/\d+/g); HSL = { hue: values[0], saturation: values[1], luminosity: values[2] }; break; case 'RGB': var values = $(this).val().match(/\d+/g); HSL = RGBToHSL(values[0], values[1], values[2]); break; case 'Hex': HSL = hexToHSL($(this).val()); break; } // Set the luminosity luminosityInput.val(HSL.luminosity); setLuminosity(HSL.luminosity); // Place the pin pin.css({ left: HSL.hue / 360 * 100 + '%', top: HSL.saturation + '%' }); // Trigger color picker change event for custom callbacks picker.trigger('change'); }); //---------------------- Set luminosity //---------- Set the luminosity spectrum overlay function setLuminosity(luminosity) { var color, alpha; if (luminosity <= 50) { color = '0, 0, 0'; alpha = 1 - luminosity / 100 * 2; } else { color = '255, 255, 255'; alpha = luminosity / 100 * 2 - 1; } // Apply luminosity to the spectrum spectrum.children().css('background-color', 'rgba(' + color + ', ' + alpha + ')'); } //---------- Luminosity input interaction luminosityInput.on('change', function() { setLuminosity($(this).val()); updateColorInput(); }); //---------------------- Set hue, saturation via pin //---------- Move the pin var movePin = function(event) { var offset = spectrum.offset(), width = spectrum.width(), height = spectrum.height(), x = event.changedTouches[0].clientX - offset.left, y = event.changedTouches[0].clientY - offset.top; // Account for pin being dragged outside the spectrum area // Sanatize x if (x < 0) { x = 0; } else if (x >= width) { x = width; } // Sanatize y if (y < 0) { y = 0; } else if (y >= height) { y = height; } // Place the pin pin.css({ left: x / width * 100 + '%', top: y / height * 100 + '%' }); // Output new color value updateColorInput(); }; //---------- Pin interaction spectrum.on('touchstart', function(event) { event.preventDefault(); movePin(event); spectrum.addClass('active'); $(document).on('touchmove', movePin); }); $(document).on('touchend', function() { spectrum.removeClass('active'); $(document).off('touchmove', movePin); }); spectrum.on('touchmove touchstart', movePin); //---------------------- Output color preview picker.on('change', function() { colorInput.css('background-color', colorInput.val()).toggleClass('dark', luminosityInput.val() <= 50); }); //---------------------- Initialize this color picker colorInput.trigger('change'); }); });