From: Dmitry Baranovskiy Date: Thu, 4 Mar 2010 23:20:46 +0000 (+1100) Subject: Color Picker & Color Wheel X-Git-Tag: v1.3.2~1^2~3 X-Git-Url: http://git.roojs.org/?p=raphael;a=commitdiff_plain;h=c2ee52f065ec561e408be775cc922c2673048b3a Color Picker & Color Wheel --- diff --git a/plugins/colorpicker.js b/plugins/colorpicker.js new file mode 100644 index 0000000..dce88ec --- /dev/null +++ b/plugins/colorpicker.js @@ -0,0 +1,259 @@ +/*! + * Color Picker 0.1.0 - Raphael plugin + * + * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com) + * Based on Color Wheel (http://jweir.github.com/colorwheel) by John Weir (http://famedriver.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ + + /* + * Usage + * var cp = Raphael.colorpicker(x, y, size, "#fff"); // #fff is optional init color + * cp.color(); // returns "#fff" + * cp.color("#fc0"); // sets new color + * cp.onchange = function (color) { + * // do something with the color when user change it + * } + * cp.remove(); // removes widget + */ +(function (Raphael) { + Raphael.colorpicker = function (x, y, size, initcolor, element) { + return new ColorPicker(x, y, size, initcolor, element); + }; + Raphael.fn.colorPickerIcon = function (x, y, r) { + var segments = pi * r * 2 / Math.min(r / 8, 4); + var a = pi / 2 - pi * 2 / segments * 1.5, + 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(); + for (var i = 0; i < segments; i++) { + this.path(path).attr({ + stroke: "none", + fill: "hsb(" + (segments - i) * (255 / segments) + ", 255, 255)", + rotation: [90 + (360 / segments) * i, x, y] + }); + } + return this.circle(x, y, r).attr({ + fill: "r#fff-#fff", + "fill-opacity": 0, + "stroke-width": Math.round(r * .03), + stroke: "#fff" + }); + }; + var pi = Math.PI; + function angle(x, y) { + return (x < 0) * 180 + Math.atan(-y / -x) * 180 / pi; + } + var doc = document, win = window, + addEvent = (function () { + if (doc.addEventListener) { + return function (obj, type, fn, element) { + var f = function (e) { + return fn.call(element, e); + }; + obj.addEventListener(type, f, false); + return function () { + obj.removeEventListener(type, f, false); + return true; + }; + }; + } else if (doc.attachEvent) { + return function (obj, type, fn, element) { + var f = function (e) { + return fn.call(element, e || win.event); + }; + obj.attachEvent("on" + type, f); + var detacher = function () { + obj.detachEvent("on" + type, f); + return true; + }; + return detacher; + }; + } + })(), + ColorPicker = function (x, y, size, initcolor, element) { + size = size || 200; + var w3 = 3 * size / 200, + w1 = size / 200, + fi = 1.6180339887, + size20 = size / 20, + size2 = size / 2, + padding = 2 * size / 200, + height = size + size20 * 2 + padding * 3, + t = this, + H = 1, S = 1, B = 1, s = size - (size20 * 4), + r = element ? Raphael(element, size, height) : Raphael(x, y, size, height), + xy = s / 6 + size20 * 2 + padding, + wh = s * 2 / 3 - padding * 2; + w1 < 1 && (w1 = 1); + w3 < 1 && (w3 = 1); + + + r.colorPickerIcon(size2, size2, size2 - padding); + + t.cursor = r.set(); + t.cursor.push(r.circle(size2, size2, size20 / 2).attr({ + stroke: "#000", + opacity: .5, + "stroke-width": w3 + })); + t.cursor.push(t.cursor[0].clone().attr({ + stroke: "#fff", + opacity: 1, + "stroke-width": w1 + })); + t.disc = r.circle(size2, size2, size2 - padding).attr({ + fill: "#000", + "fill-opacity": 0, + stroke: "none", + cursor: "crosshair" + }); + var style = t.disc.node.style; + style.unselectable = "on"; + style.MozUserSelect = "none"; + style.WebkitUserSelect= "none"; + + // brightness drawing + var h = size20 * 2 + 2; + t.brect = r.rect(padding + h / fi / 2, size + padding * 2, size - padding * 2 - h / fi, h - padding * 2).attr({ + stroke: "#fff", + fill: "180-#fff-#000" + }); + t.cursorb = r.set(); + t.cursorb.push(r.rect(size - padding - h / fi, size + padding, ~~(h / fi), h, w3).attr({ + stroke: "#000", + opacity: .5, + "stroke-width": w3 + })); + t.cursorb.push(t.cursorb[0].clone().attr({ + stroke: "#fff", + opacity: 1, + "stroke-width": w1 + })); + t.btop = t.brect.clone().attr({ + stroke: "#000", + fill: "#000", + opacity: 0 + }); + style = t.btop.node.style; + style.unselectable = "on"; + style.MozUserSelect = "none"; + style.WebkitUserSelect= "none"; + + t.bwidth = ~~(h / fi) / 2; + t.minx = padding + t.bwidth; + t.maxx = size - h / fi - padding + t.bwidth; + + t.H = t.S = t.B = 1; + t.padding = padding; + t.raphael = r; + t.size2 = size2; + t.size20 = size20; + t.x = x; + t.y = y; + + // events + t.hson = addEvent(t.disc.node, "mousedown", function (e) { + var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop, + scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + this.hsOnTheMove = true; + this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y); + this.docmove = addEvent(doc, "mousemove", this.docOnMove, this); + this.docup = addEvent(doc, "mouseup", this.docOnUp, this); + }, t); + t.bon = addEvent(t.btop.node, "mousedown", function (e) { + var scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + this.bOnTheMove = true; + this.setB(e.clientX + scrollX - this.x); + this.docmove = addEvent(doc, "mousemove", this.docOnMove, this); + this.docup = addEvent(doc, "mouseup", this.docOnUp, this); + }, t); + t.winunload = addEvent(win, "unload", function () { + this.hson(); + this.bon(); + this.docmove && this.docmove(); + this.docup && this.docup(); + this.winunload(); + }, t); + + t.color(initcolor || "#fff"); + this.onchanged && this.onchanged(this.color()); + }; + ColorPicker.prototype.setB = function (x) { + x < this.minx && (x = this.minx); + x > this.maxx && (x = this.maxx); + this.cursorb.attr({x: x - this.bwidth}); + this.B = (x - this.minx) / (this.maxx - this.minx); + this.onchange && this.onchange(this.color()); + }; + ColorPicker.prototype.setHS = function (x, y) { + var X = x - this.size2, + Y = y - this.size2, + R = this.size2 - this.size20 / 2 - this.padding, + d = angle(X, Y), + rd = d * pi / 180; + isNaN(d) && (d = 0); + if (X * X + Y * Y > R * R) { + x = R * Math.cos(rd) + this.size2; + y = R * Math.sin(rd) + this.size2; + } + this.cursor.attr({cx: x, cy: y}); + this.H = (1 - d / 360) % 1; + this.S = Math.min((X * X + Y * Y) / R / R, 1); + this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"}); + this.onchange && this.onchange(this.color()); + }; + ColorPicker.prototype.docOnMove = function (e) { + var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop, + scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + if (this.hsOnTheMove) { + this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y); + } + if (this.bOnTheMove) { + this.setB(e.clientX + scrollX - this.x); + } + e.preventDefault && e.preventDefault(); + e.returnValue = false; + return false; + }; + ColorPicker.prototype.docOnUp = function (e) { + this.hsOnTheMove = this.bOnTheMove = false; + this.docmove(); + delete this.docmove; + this.docup(); + delete this.docup; + this.onchanged && this.onchanged(this.color()); + e.preventDefault && e.preventDefault(); + e.stopPropagation && e.stopPropagation(); + e.returnValue = false; + return false; + }; + ColorPicker.prototype.remove = function () { + this.raphael.remove(); + this.color = function () { + return false; + }; + }; + ColorPicker.prototype.color = function (color) { + if (color) { + color = Raphael.getRGB(color); + var hex = color.hex; + color = Raphael.rgb2hsb(color.r, color.g, color.b); + d = color.h * 360; + this.H = color.h; + this.S = color.s; + this.B = color.b; + + this.cursorb.attr({x: this.B * (this.maxx - this.minx) + this.minx - this.bwidth}); + this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"}); + + var d = (1 - this.H) * 360, + rd = d * pi / 180, + R = (this.size2 - this.size20 / 2 - this.padding) * this.S, + x = Math.cos(rd) * R + this.size2, + y = Math.sin(rd) * R + this.size2; + this.cursor.attr({cx: x, cy: y}); + return this; + } else { + return Raphael.hsb2rgb(this.H, this.S, this.B).hex; + } + }; +})(window.Raphael); \ No newline at end of file diff --git a/plugins/colorwheel.js b/plugins/colorwheel.js new file mode 100644 index 0000000..ecdae52 --- /dev/null +++ b/plugins/colorwheel.js @@ -0,0 +1,242 @@ +/*! + * Color Wheel 0.1.0 - Raphael plugin + * + * Copyright (c) 2010 John Weir (http://famedriver.com) & Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +(function (Raphael) { + Raphael.colorpicker = function (x, y, size, initcolor, element) { + return new ColorPicker(x, y, size, initcolor, element); + }; + var pi = Math.PI; + function angle(x, y) { + return (x < 0) * 180 + Math.atan(-y / -x) * 180 / pi; + } + var doc = document, win = window, + addEvent = (function () { + if (doc.addEventListener) { + return function (obj, type, fn, element) { + var f = function (e) { + return fn.call(element, e); + }; + obj.addEventListener(type, f, false); + return function () { + obj.removeEventListener(type, f, false); + return true; + }; + }; + } else if (doc.attachEvent) { + return function (obj, type, fn, element) { + var f = function (e) { + return fn.call(element, e || win.event); + }; + obj.attachEvent("on" + type, f); + var detacher = function () { + obj.detachEvent("on" + type, f); + return true; + }; + return detacher; + }; + } + })(), + ColorPicker = function (x, y, size, initcolor, element) { + size = size || 200; + var w3 = 3 * size / 200, + w1 = size / 200, + fi = 1.6180339887, + segments = pi * size / 4, + size20 = size / 20, + size2 = size / 2, + padding = 2 * size / 200, + height = size + size20 * 2 + padding * 3, + t = this, + H = 1, S = 1, B = 1, s = size - (size20 * 4), + r = element ? Raphael(element, size, height) : Raphael(x, y, size, height), + xy = s / 6 + size20 * 2 + padding, + wh = s * 2 / 3 - padding * 2; + w1 < 1 && (w1 = 1); + w3 < 1 && (w3 = 1); + + + // ring drawing + var a = pi / 2 - pi * 2 / segments * 1.3, + R = size2 - padding, + R2 = size2 - padding - size20 * 2, + path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", size2, size2, "z"].join(); + for (var i = 0; i < segments; i++) { + r.path(path).attr({ + stroke: "none", + fill: "hsb(" + (segments - i) * (255 / segments) + ", 255, 200)", + rotation: [90 + (360 / segments) * i, size2, size2] + }); + } + r.circle(size2, size2, R).attr({ + fill: "r#fff-#fff", + "fill-opacity": 0, + "stroke-width": 3, + stroke: "#fff" + }); + + t.cursor = r.set(); + t.cursor.push(r.circle(size2, size2, size20 / 2).attr({ + stroke: "#000", + opacity: .5, + "stroke-width": w3 + })); + t.cursor.push(t.cursor[0].clone().attr({ + stroke: "#fff", + opacity: 1, + "stroke-width": w1 + })); + t.disc = r.circle(size2, size2, R).attr({ + fill: "#000", + "fill-opacity": 0, + stroke: "none", + cursor: "crosshair" + }); + var style = t.disc.node.style; + style.unselectable = "on"; + style.MozUserSelect = "none"; + style.WebkitUserSelect= "none"; + + // brightness drawing + var h = size20 * 2 + 2; + t.brect = r.rect(padding + h / fi / 2, size + padding * 2, size - padding * 2 - h / fi, h - padding * 2).attr({ + stroke: "#fff", + fill: "180-#fff-#000" + }); + t.cursorb = r.set(); + t.cursorb.push(r.rect(size - padding - h / fi, size + padding, ~~(h / fi), h, 3 * size / 200).attr({ + stroke: "#000", + opacity: .5, + "stroke-width": w3 + })); + t.cursorb.push(t.cursorb[0].clone().attr({ + stroke: "#fff", + opacity: 1, + "stroke-width": w1 + })); + t.btop = t.brect.clone().attr({ + stroke: "#000", + fill: "#000", + opacity: 0 + }); + style = t.btop.node.style; + style.unselectable = "on"; + style.MozUserSelect = "none"; + style.WebkitUserSelect= "none"; + + t.minx = padding; + t.maxx = size - h / fi - padding; + + t.H = t.S = t.B = 1; + t.padding = padding; + t.raphael = r; + t.size2 = size2; + t.size20 = size20; + t.x = x; + t.y = y; + + // events + t.hson = addEvent(t.disc.node, "mousedown", function (e) { + var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop, + scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + this.hsOnTheMove = true; + this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y); + this.docmove = addEvent(doc, "mousemove", this.docOnMove, this); + this.docup = addEvent(doc, "mouseup", this.docOnUp, this); + }, t); + t.bon = addEvent(t.btop.node, "mousedown", function (e) { + var scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + this.bOnTheMove = true; + this.setB(e.clientX + scrollX - this.x); + this.docmove = addEvent(doc, "mousemove", this.docOnMove, this); + this.docup = addEvent(doc, "mouseup", this.docOnUp, this); + }, t); + t.winunload = addEvent(win, "unload", function () { + this.hson(); + this.bon(); + this.docmove && this.docmove(); + this.docup && this.docup(); + this.winunload(); + }, t); + + t.color(initcolor || "#fff"); + this.onchanged && this.onchanged(this.color()); + }; + ColorPicker.prototype.setB = function (x) { + x < this.minx && (x = this.minx); + x > this.maxx && (x = this.maxx); + this.cursorb.attr({x: x}); + this.B = (x - this.minx) / (this.maxx - this.minx); + this.onchange && this.onchange(this.color()); + }; + ColorPicker.prototype.setHS = function (x, y) { + var X = x - this.size2, + Y = y - this.size2, + R = this.size2 - this.size20 / 2 - this.padding, + d = angle(X, Y), + rd = d * pi / 180; + isNaN(d) && (d = 0); + if (X * X + Y * Y > R * R) { + x = R * Math.cos(rd) + this.size2; + y = R * Math.sin(rd) + this.size2; + } + this.cursor.attr({cx: x, cy: y}); + this.H = (1 - d / 360) % 1; + this.S = Math.min((X * X + Y * Y) / R / R, 1); + this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"}); + this.onchange && this.onchange(this.color()); + }; + ColorPicker.prototype.docOnMove = function (e) { + var scrollY = doc.documentElement.scrollTop || doc.body.scrollTop, + scrollX = doc.documentElement.scrollLeft || doc.body.scrollLeft; + if (this.hsOnTheMove) { + this.setHS(e.clientX + scrollX - this.x, e.clientY + scrollY - this.y); + } + if (this.bOnTheMove) { + this.setB(e.clientX + scrollX - this.x); + } + e.preventDefault && e.preventDefault(); + e.returnValue = false; + return false; + }; + ColorPicker.prototype.docOnUp = function (e) { + this.hsOnTheMove = this.bOnTheMove = false; + this.docmove(); + delete this.docmove; + this.docup(); + delete this.docup; + this.onchanged && this.onchanged(this.color()); + }; + ColorPicker.prototype.remove = function () { + this.raphael.remove(); + this.color = function () { + return false; + }; + }; + ColorPicker.prototype.color = function (color) { + if (color) { + color = Raphael.getRGB(color); + var hex = color.hex; + color = Raphael.rgb2hsb(color.r, color.g, color.b); + d = color.h * 360; + this.H = color.h; + this.S = color.s; + this.B = color.b; + + this.cursorb.attr({x: this.B * (this.maxx - this.minx) + this.minx}); + this.brect.attr({fill: "180-hsb(" + [this.H, this.S] + ",1)-#000"}); + + var d = (1 - this.H) * 360, + rd = d * pi / 180, + R = (this.size2 - this.size20 / 2 - this.padding) * this.S, + x = Math.cos(rd) * R + this.size2, + y = Math.sin(rd) * R + this.size2; + this.cursor.attr({cx: x, cy: y}); + return this; + } else { + return Raphael.hsb2rgb(this.H, this.S, this.B).hex; + } + }; +})(window.Raphael); \ No newline at end of file diff --git a/plugins/jquery.colorpicker.js b/plugins/jquery.colorpicker.js new file mode 100644 index 0000000..9c3f7f2 --- /dev/null +++ b/plugins/jquery.colorpicker.js @@ -0,0 +1,16 @@ +/*! + * Color Picker 0.1.0 - Raphael plugin + * + * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com) + * Based on Color Wheel (http://jweir.github.com/colorwheel) by John Weir (http://famedriver.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +(function ($, R) { + $.fn.colorpicker = function (size, initcolor) { + if (R) { + var offset = this.offset(); + return R.colorpicker(offset.left, offset.top, size, initcolor, this[0]); + } + return null; + }; +})(window.jQuery, window.Raphael); \ No newline at end of file diff --git a/plugins/jquery.colorwheel.js b/plugins/jquery.colorwheel.js new file mode 100644 index 0000000..b8f504d --- /dev/null +++ b/plugins/jquery.colorwheel.js @@ -0,0 +1,15 @@ +/*! + * Color Wheel 0.1.0 - Raphael plugin + * + * Copyright (c) 2010 John Weir (http://famedriver.com) & Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +(function ($, R) { + $.fn.colorwheel = function (size, initcolor) { + if (R) { + var offset = this.offset(); + return R.colorpicker(offset.left, offset.top, size, initcolor, this[0]); + } + return null; + }; +})(window.jQuery, window.Raphael); \ No newline at end of file