317 lines
8.4 KiB
JavaScript
317 lines
8.4 KiB
JavaScript
/*
|
|
* 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<k; n++){ copy_segment(tri, n, k); }
|
|
|
|
// IE needs a slight opacity to assign events
|
|
hue_ring.event = canvas.circle(
|
|
center,
|
|
center,
|
|
center-tri_size-padding).attr({"stroke-width":tri_size*2, opacity:0.01});
|
|
|
|
hue_ring.outline = canvas.circle(
|
|
center,
|
|
center,
|
|
center-tri_size-padding).attr({"stroke":"#000", "stroke-width":(tri_size*2)+3, opacity:0.1});
|
|
hue_ring.outline.toBack();
|
|
hue_ring.event.node.style.cursor = "crosshair";
|
|
}
|
|
|
|
function run_onchange_event(){
|
|
if (change_callback !== undefined){
|
|
change_callback(current_color);
|
|
}
|
|
}
|
|
|
|
return create(target, color_wheel_size);
|
|
};
|
|
|