libfn/web/js/colorwheel.js

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);
};