/* * Colorwheel * Copyright (c) 2010 John Weir (http://famedriver.com) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. * * requires jQuery & Raphael * http://jquery.com http://raphaeljs.com * * see http://jweir.github.com/colorwheel for Usage * */ Raphael.colorwheel = function(target, color_wheel_size, no_segments){ var canvas, current_color, size, segments = no_segments || 60, bs_square = {}, hue_ring = {}, tri_size, cursor = {}, drag_target, input_target, center, parent, change_callback, drag_callbacks = [function(){}, function(){}], offset, padding = 2, sdim; // holds the dimensions for the saturation square function point(x, y){ return {x:x, y:y};} function radians(a){ return a * (Math.PI/180);} function angle(x,y){ var q = x > 0 ? 0 : 180; return q+Math.atan((0 - y)/(0 - x))*180/(Math.PI); } function create(target, color_wheel_size){ size = color_wheel_size; tri_size = size/20; center = size/2; parent = $(target); canvas = Raphael(parent[0],size, size); canvas.safari(); create_bs_square(); create_hue_ring(); hue_ring.cursor = cursor_create(tri_size); bs_square.cursor = cursor_create(tri_size*0.5); events_setup(); parent.css({height:size+"px", width:size+"px"}); disable_select(parent); return public_methods(); } function disable_select(target){ $(target).css({"unselectable": "on","-moz-user-select": "none","-webkit-user-select": "none"}); } function public_methods(){ return { input: input, onchange: onchange, ondrag : ondrag, color : public_set_color }; } // Sets a textfield for user input of hex color values // TODO don't clear the change callback // TODO allow a null target to unbind the input function input(target){ change_callback = null; input_target = target; $(target).keyup(function(){ if(this.value.match(/^#([0-9A-F]){3}$|^#([0-9A-F]){6}$/img)){ set_color(this.value); update_color(true); run_onchange_event(); } }); set_color(target.value); update_color(true); return public_methods(); } function onchange(callback){ change_callback = callback; update_color(false); return public_methods(); } function ondrag(start_callback, end_callback){ drag_callbacks = [start_callback || function(){}, end_callback || function(){}]; return public_methods(); } function drag(e){ var x, y, page; e.preventDefault(); // prevents scrolling on touch page = e.originalEvent.touches ? e.originalEvent.touches[0] : e; x = page.pageX - (parent.offset().left + center); y = page.pageY - (parent.offset().top + center); if(drag_target == hue_ring){ set_hue_cursor(x,y); update_color(); run_onchange_event(); return true; } if(drag_target == bs_square){ set_bs_cursor(x,y); update_color(); run_onchange_event(); return true; } } function start_drag(event, target){ event.preventDefault(); // prevents scrolling on touch $(document).on('mouseup touchend',stop_drag); $(document).on('mousemove touchmove',drag); drag_target = target; drag(event); drag_callbacks[0](current_color); } function stop_drag(event){ event.preventDefault(); // prevents scrolling on touch $(document).off("mouseup touchend",stop_drag); $(document).off("mousemove touchmove",drag); drag_callbacks[1](current_color); run_onchange_event(); } function events_setup(){ $([hue_ring.event.node,hue_ring.cursor[0].node]).on("mousedown touchstart", function(e){start_drag(e,hue_ring);}); $([bs_square.b.node, bs_square.cursor[0].node]).on("mousedown touchstart", function(e){start_drag(e,bs_square);}); } function cursor_create(size){ var set = canvas.set().push( canvas.circle(0, 0, size).attr({"stroke-width":4, stroke:"#333"}), canvas.circle(0, 0, size+2).attr({"stroke-width":1, stroke:"#FFF", opacity:0.5}) ); set[0].node.style.cursor = "crosshair"; return set; } function set_bs_cursor(x,y){ x = x+center; y = y+center; if(x < sdim.x){x = sdim.x} if(x > sdim.x+sdim.l){x = sdim.x+sdim.l} if(y < sdim.y){y = sdim.y} if(y > sdim.y+sdim.l){y = sdim.y + sdim.l} bs_square.cursor.attr({cx:x, cy:y}).transform("t0,0"); } function set_hue(color){ var hex = Raphael.getRGB(color).hex; bs_square.h.attr("fill", hex); } function hue(){ return Raphael.rgb2hsb(bs_square.h.attr("fill")).h; } function public_set_color(value){ var ret = set_color(value); update_color(false); return ret; } function set_color(value){ if(value === undefined){ return current_color; } var temp = canvas.rect(1,1,1,1).attr({fill:value}), hsb = canvas.raphael.rgb2hsb(temp.attr("fill")); set_bs_cursor( (0-sdim.l/2) + (sdim.l*hsb.s), sdim.l/2 - (sdim.l*hsb.b)); set_hue_cursor((360*(hsb.h))-90); temp.remove(); return public_methods(); } // Could optimize this method function update_color(dont_replace_input_value){ var x = bs_square.cursor.items[0].attr("cx"), y = bs_square.cursor.items[0].attr("cy"), hsb = { b: 1-(y-sdim.y)/sdim.l, s: (x-sdim.x)/sdim.l, h: hue() }; current_color = Raphael.hsb2rgb(hsb.h, hsb.s,hsb.b); if(input_target){ var c = current_color.hex; if(dont_replace_input_value !== true) { input_target.value = c;} if(hsb.b < 0.5){ $(input_target).css("color", "#FFF"); } else { $(input_target).css("color", "#000"); } input_target.style.background = c; } } // accepts either x,y or d (degrees) function set_hue_cursor(mixed_args){ var d; if(arguments.length == 2){ d = angle(arguments[0],arguments[1]); } else { d = arguments[0]; } var x = Math.cos(radians(d)) * (center-tri_size-padding); var y = Math.sin(radians(d)) * (center-tri_size-padding); hue_ring.cursor.attr({cx:x+center, cy:y+center}).transform("t0,0"); set_hue("hsb("+(d+90)/360+",1,1)"); } function bs_square_dim(){ if(sdim){ return sdim;} var s = size - (tri_size * 4); sdim = { x:(s/6)+tri_size*2+padding, y:(s/6)+tri_size*2+padding, l:(s * 2/3)-padding*2 }; return sdim; } function create_bs_square(){ bs_square_dim(); box = [sdim.x, sdim.y, sdim.l, sdim.l]; bs_square.h = canvas.rect.apply(canvas, box).attr({ stroke:"#EEE", gradient: "0-#FFF-#000", opacity:1}); bs_square.s = canvas.rect.apply(canvas, box).attr({ stroke:null, gradient: "0-#FFF-#FFF", opacity:0}); bs_square.b = canvas.rect.apply(canvas, box).attr({ stroke:null, gradient: "90-#000-#FFF", opacity:0}); bs_square.b.node.style.cursor = "crosshair"; } function hue_segement_shape(){ var path = "M -@W 0 L @W 0 L @W @H L -@W @H z"; return path.replace(/@H/img, tri_size*2).replace(/@W/img,tri_size); } function copy_segment(r, d, k){ var n = r.clone(); var hue = d*(255/k); var s = size/2, t = tri_size, p = padding; n.transform("t"+s+","+(s-t)+"r"+(360/k)*d+"t0,-"+(s-t-p)+""); n.attr({"stroke-width":0, fill:"hsb("+d*(1/k)+", 1, 0.85)"}); hue_ring.hues.push(n); } function create_hue_ring(){ var s = hue_segement_shape(), tri = canvas.path(s).attr({stroke:"rgba(0,0,0,0)"}).transform("t"+(size/2)+","+padding), k = segments; // # of segments to use to generate the hues hue_ring.hues = canvas.set(); for(n=0; n