2 * Color Picker 0.1.0 - Raphael plugin
4 * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)
5 * Based on Color Wheel (http://jweir.github.com/colorwheel) by John Weir (http://famedriver.com)
6 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
11 * var cp = Raphael.colorpicker(x, y, size, "#fff"); // #fff is optional init color
12 * cp.color(); // returns "#fff"
13 * cp.color("#fc0"); // sets new color
14 * cp.onchange = function (color) {
15 * // do something with the color when user change it
17 * cp.remove(); // removes widget
20 Raphael.colorpicker = function (x, y, size, initcolor, element) {
21 return new ColorPicker(x, y, size, initcolor, element);
23 Raphael.fn.colorPickerIcon = function (x, y, r) {
24 var segments = pi * r * 2 / Math.min(r / 8, 4);
25 var a = pi / 2 - pi * 2 / segments * 1.5,
26 path = ["M", x, y - r, "A", r, r, 0, 0, 1, r * Math.cos(a) + x, y - r * Math.sin(a), "L", x, y, "z"].join();
27 for (var i = 0; i < segments; i++) {
28 this.path(path).attr({
30 fill: "hsb(" + (segments - i) * (255 / segments) + ", 255, 255)",
31 rotation: [90 + (360 / segments) * i, x, y]
34 return this.circle(x, y, r).attr({
37 "stroke-width": Math.round(r * .03),
42 function angle(x, y) {
43 return (x < 0) * 180 + Math.atan(-y / -x) * 180 / pi;
45 var doc = document, win = window,
46 addEvent = (function () {
47 if (doc.addEventListener) {
48 return function (obj, type, fn, element) {
49 var f = function (e) {
50 return fn.call(element, e);
52 obj.addEventListener(type, f, false);
54 obj.removeEventListener(type, f, false);
58 } else if (doc.attachEvent) {
59 return function (obj, type, fn, element) {
60 var f = function (e) {
61 return fn.call(element, e || win.event);
63 obj.attachEvent("on" + type, f);
64 var detacher = function () {
65 obj.detachEvent("on" + type, f);
72 ColorPicker = function (x, y, size, initcolor, element) {
74 var w3 = 3 * size / 200,
79 padding = 2 * size / 200,
80 height = size + size20 * 2 + padding * 3,
82 H = 1, S = 1, B = 1, s = size - (size20 * 4),
83 r = element ? Raphael(element, size, height) : Raphael(x, y, size, height),
84 xy = s / 6 + size20 * 2 + padding,
85 wh = s * 2 / 3 - padding * 2;
90 r.colorPickerIcon(size2, size2, size2 - padding);
93 t.cursor.push(r.circle(size2, size2, size20 / 2).attr({
98 t.cursor.push(t.cursor[0].clone().attr({
103 t.disc = r.circle(size2, size2, size2 - padding).attr({
109 var style = t.disc.node.style;
110 style.unselectable = "on";
111 style.MozUserSelect = "none";
112 style.WebkitUserSelect= "none";
114 // brightness drawing
115 var h = size20 * 2 + 2;
116 t.brect = r.rect(padding + h / fi / 2, size + padding * 2, size - padding * 2 - h / fi, h - padding * 2).attr({
118 fill: "180-#fff-#000"
121 t.cursorb.push(r.rect(size - padding - h / fi, size + padding, ~~(h / fi), h, w3).attr({
126 t.cursorb.push(t.cursorb[0].clone().attr({
131 t.btop = t.brect.clone().attr({
136 style = t.btop.node.style;
137 style.unselectable = "on";
138 style.MozUserSelect = "none";
139 style.WebkitUserSelect= "none";
141 t.bwidth = ~~(h / fi) / 2;
142 t.minx = padding + t.bwidth;
143 t.maxx = size - h / fi - padding + t.bwidth;
154 t.hson = addEvent(t.disc.node, "mousedown", function (e) {
155 var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop,
156 scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft;
157 this.hsOnTheMove = true;
158 this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y);
159 this.docmove = addEvent(doc, "mousemove", this.docOnMove, this);
160 this.docup = addEvent(doc, "mouseup", this.docOnUp, this);
162 t.bon = addEvent(t.btop.node, "mousedown", function (e) {
163 var scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft;
164 this.bOnTheMove = true;
165 this.setB(e.clientX + scrollX - this.x);
166 this.docmove = addEvent(doc, "mousemove", this.docOnMove, this);
167 this.docup = addEvent(doc, "mouseup", this.docOnUp, this);
169 t.winunload = addEvent(win, "unload", function () {
172 this.docmove && this.docmove();
173 this.docup && this.docup();
177 t.color(initcolor || "#fff");
178 this.onchanged && this.onchanged(this.color());
180 ColorPicker.prototype.setB = function (x) {
181 x < this.minx && (x = this.minx);
182 x > this.maxx && (x = this.maxx);
183 this.cursorb.attr({x: x - this.bwidth});
184 this.B = (x - this.minx) / (this.maxx - this.minx);
185 this.onchange && this.onchange(this.color());
187 ColorPicker.prototype.setHS = function (x, y) {
188 var X = x - this.size2,
190 R = this.size2 - this.size20 / 2 - this.padding,
194 if (X * X + Y * Y > R * R) {
195 x = R * Math.cos(rd) + this.size2;
196 y = R * Math.sin(rd) + this.size2;
198 this.cursor.attr({cx: x, cy: y});
199 this.H = (1 - d / 360) % 1;
200 this.S = Math.min((X * X + Y * Y) / R / R, 1);
201 this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"});
202 this.onchange && this.onchange(this.color());
204 ColorPicker.prototype.docOnMove = function (e) {
205 var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop,
206 scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft;
207 if (this.hsOnTheMove) {
208 this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y);
210 if (this.bOnTheMove) {
211 this.setB(e.clientX + scrollX - this.x);
213 e.preventDefault && e.preventDefault();
214 e.returnValue = false;
217 ColorPicker.prototype.docOnUp = function (e) {
218 this.hsOnTheMove = this.bOnTheMove = false;
223 this.onchanged && this.onchanged(this.color());
224 e.preventDefault && e.preventDefault();
225 e.stopPropagation && e.stopPropagation();
226 e.returnValue = false;
229 ColorPicker.prototype.remove = function () {
230 this.raphael.remove();
231 this.color = function () {
235 ColorPicker.prototype.color = function (color) {
237 color = Raphael.getRGB(color);
239 color = Raphael.rgb2hsb(color.r, color.g, color.b);
245 this.cursorb.attr({x: this.B * (this.maxx - this.minx) + this.minx - this.bwidth});
246 this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"});
248 var d = (1 - this.H) * 360,
250 R = (this.size2 - this.size20 / 2 - this.padding) * this.S,
251 x = Math.cos(rd) * R + this.size2,
252 y = Math.sin(rd) * R + this.size2;
253 this.cursor.attr({cx: x, cy: y});
256 return Raphael.hsb2rgb(this.H, this.S, this.B).hex;