Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial implementation based on jsbin

  • Loading branch information...
commit f8727a78212544491240046e3af99619b4101c50 0 parents
@bebraw authored
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012 Juho Vepsäläinen
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 README.md
@@ -0,0 +1,16 @@
+# colorjoe - The Scaleable Color Picker
+
+colorjoe was somewhat inspired by
+[ColorJack](http://www.dynamicdrive.com/dynamicindex11/colorjack/index.htm) and
+[RightJS Colorpicker](http://rightjs.org/ui/colorpicker). Unlike those it
+actually scales pretty well. Essentially this means that you'll be able to
+define its actual dimensions and layout using a bit of CSS. This way the widget
+fits well responsive layouts.
+
+In addition it's relatively easy to implement missing functionality (RGB fields,
+whatnot) thanks to the simple API it provides.
+
+## License
+
+colorjoe is available under MIT. See LICENSE for more details.
+
147 demo/demo.css
@@ -0,0 +1,147 @@
+.colorPicker {
+ background: #E8E6E0;
+ border: 1px solid #BBB;
+ -moz-border-radius: .3em;
+ border-radius: .3em;
+ margin: 1em;
+ display: inline-block;
+}
+
+.colorPicker .extras {
+ float: right;
+ margin: 0.5em;
+}
+
+.colorPicker .extras .currentColor {
+ width: 65px;
+ height: 30px;
+ border: 1px solid #BBB;
+ -moz-border-radius: .3em;
+ border-radius: .3em;
+}
+
+.colorPicker .extras .rgb {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+.colorPicker .extras .color {
+ text-align: right;
+}
+
+.colorPicker .extras .rgb input {
+ width: 40px;
+}
+
+.colorPicker .extras .hex {
+ float: right;
+}
+
+.colorPicker .extras .hex input {
+ width: 60px;
+}
+
+.colorPicker .twod {
+ float: left;
+ margin: 0.5em;
+}
+
+/* main dimensions */
+.colorPicker .twod, .colorPicker .twod .bg {
+ width: 200px;
+ height: 200px;
+}
+.colorPicker .oned, .colorPicker .oned .bg {
+ height: 200px;
+}
+
+.colorPicker .twod .bg {
+ position: absolute;
+ border: 1px solid #BBB;
+ -moz-border-radius: .3em;
+ border-radius: .3em;
+}
+.colorPicker .twod .pointer {
+ position: relative;
+ z-index: 2;
+}
+.colorPicker .twod .pointer .shape {
+ position: absolute;
+}
+.colorPicker .twod .pointer .shape1 {
+ margin-left: -7px;
+ margin-top: -7px;
+ width: 10px;
+ height: 10px;
+ border: 2px solid black;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.colorPicker .twod .pointer .shape2 {
+ margin-left: -6px;
+ margin-top: -6px;
+ width: 8px;
+ height: 8px;
+ border: 2px solid white;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.colorPicker .twod .bg1 {
+ z-index: 0;
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMCIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
+ background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0)));
+ background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
+ background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
+ background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
+ background: linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=1 );
+}
+.colorPicker .twod .bg2 {
+ z-index: 1;
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
+ background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1)));
+ background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
+ background: -o-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
+ background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
+ background: linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#000000',GradientType=0 );
+}
+
+.colorPicker .oned {
+ float: left;
+ margin: 0.5em;
+}
+.colorPicker .oned, .colorPicker .oned .bg, .colorPicker .oned .pointer .shape {
+ width: 20px;
+ }
+.colorPicker .oned .bg {
+ border: 1px solid #BBB;
+ -moz-border-radius: .3em;
+ border-radius: .3em;
+}
+.colorPicker .oned .pointer {
+ position: relative;
+ z-index: 2;
+}
+.colorPicker .oned .pointer .shape {
+ position: absolute;
+ margin-left: -1px;
+ margin-top: -4px;
+ height: 5px;
+ border: 2px solid black;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.colorPicker .oned .bg {
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjE3JSIgc3RvcC1jb2xvcj0iI2ZmZmYwMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjMzJSIgc3RvcC1jb2xvcj0iIzAwZmYwMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwZmZmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjY2JSIgc3RvcC1jb2xvcj0iIzAwMDBmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjgzJSIgc3RvcC1jb2xvcj0iI2ZmMDBmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZjAwMDAiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
+ background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 66%, #ff00ff 83%, #ff0000 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ff0000), color-stop(17%,#ffff00), color-stop(33%,#00ff00), color-stop(50%,#00ffff), color-stop(66%,#0000ff), color-stop(83%,#ff00ff), color-stop(100%,#ff0000));
+ background: -webkit-linear-gradient(top, #ff0000 0%,#ffff00 17%,#00ff00 33%,#00ffff 50%,#0000ff 66%,#ff00ff 83%,#ff0000 100%);
+ background: -o-linear-gradient(top, #ff0000 0%,#ffff00 17%,#00ff00 33%,#00ffff 50%,#0000ff 66%,#ff00ff 83%,#ff0000 100%);
+ background: -ms-linear-gradient(top, #ff0000 0%,#ffff00 17%,#00ff00 33%,#00ffff 50%,#0000ff 66%,#ff00ff 83%,#ff0000 100%);
+ background: linear-gradient(top, #ff0000 0%,#ffff00 17%,#00ff00 33%,#00ffff 50%,#0000ff 66%,#ff00ff 83%,#ff0000 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ff0000', endColorstr='#ff0000',GradientType=0 );
+}
+
94 demo/demo.js
@@ -0,0 +1,94 @@
+window.onload = main;
+
+function main() {
+ // TODO: wrap this as rgbjoe to a separate file?
+ var joe = colorjoe({element: 'myPicker', initialColor: 'green', cbs: {
+ change: function(c) {
+ setBg(c);
+ var rgba = color.rgba(c);
+ r.input.value = Math.round(rgba.r() * 255);
+ g.input.value = Math.round(rgba.g() * 255);
+ b.input.value = Math.round(rgba.b() * 255);
+ hex.input.value = c.toHex();
+ }
+ }});
+
+ var extras = div('extras', joe.e);
+ var curColor = div('currentColor', extras);
+ var rgb = div('rgb', extras);
+
+ var r = labelInput('color r', 'R', rgb, 3);
+ r.input.onkeyup = updateJoe;
+
+ var g = labelInput('color g', 'G', rgb, 3);
+ g.input.onkeyup = updateJoe;
+
+ var b = labelInput('color b', 'B', rgb, 3);
+ b.input.onkeyup = updateJoe;
+
+ var hex = labelInput('hex', '', extras, 6);
+ hex.input.onkeyup = function(e) {
+ var val = e.target.value;
+ var hsva = color.hsva(val);
+ var rgba = color.rgba(val);
+
+ joe.set(hsva);
+ setBg(hsva);
+ r.input.value = Math.round(rgba.r() * 255);
+ g.input.value = Math.round(rgba.g() * 255);
+ b.input.value = Math.round(rgba.b() * 255);
+ };
+
+ joe.update();
+
+ function updateJoe(e) {
+ var val = e.target.value;
+ var hsva = color.hsva(color.rgba({
+ r: r.input.value / 255,
+ g: g.input.value / 255,
+ b: b.input.value / 255
+ }));
+
+ joe.set(hsva);
+ setBg(hsva);
+ }
+
+ function setBg(c) {
+ curColor.style.background = c.toCSS();
+ }
+}
+
+function labelInput(klass, n, p, maxLen) {
+ var d = div(klass, p);
+ var l = label(n, d);
+ var i = input('text', d, maxLen);
+
+ return {label: l, input: i};
+}
+
+function label(c, p) {
+ var e = document.createElement('label');
+ e.innerHTML = c;
+ p.appendChild(e);
+
+ return e;
+}
+
+function input(t, p, maxLen) {
+ var e = document.createElement('input');
+ e.type = t;
+ if(maxLen) e.maxLength = maxLen;
+ p.appendChild(e);
+
+ return e;
+}
+
+// TODO: move to utils
+function div(klass, p) {
+ var elem = document.createElement('div');
+ elem.className = klass;
+ p.appendChild(elem);
+
+ return elem;
+}
+
25 index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="format-detection" content="telephone=no">
+ <meta charset=utf-8 />
+
+ <link rel="stylesheet" type="text/css" href="demo/demo.css" />
+
+ <script src="https://raw.github.com/bebraw/colorjs/master/src/utils.js"></script>
+ <script src="https://raw.github.com/bebraw/colorjs/master/src/color.js"></script>
+ <script src="src/colorjoe.js"></script>
+ <script src="demo/demo.js"></script>
+
+ <title>Colorjoe demo</title>
+ <!--[if IE]>
+ <script
+ src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>script>
+ <![endif]-->
+</head>
+<body>
+ <div id="myPicker"></div>
+</body>
+</html>
147 src/colorjoe.js
@@ -0,0 +1,147 @@
+function colorjoe(o) {
+ o.cbs = o.cbs || {};
+
+ // XXX: works only with element id now
+ var picker = document.getElementById(o.element);
+
+ if(picker) return setup(picker, o.initialColor, o.cbs.change);
+
+ function setup(picker, col, chg) {
+ chg = chg || function() {};
+
+ var hsv = color.hsva(col);
+
+ picker.className = 'colorPicker';
+
+ var twod = div('twod', picker);
+ var p1 = div('pointer', twod);
+ div('shape shape1', p1);
+ div('shape shape2', p1);
+ div('bg bg1', twod);
+ div('bg bg2', twod);
+
+ var oned = div('oned', picker);
+ var p2 = div('pointer', oned);
+ div('shape', p2);
+ div('bg', oned);
+
+ drag(oned, function(p) {
+ hsv.h(p.y);
+ H(p.y);
+ chg(hsv);
+ });
+
+ drag(twod, function(p) {
+ hsv.s(p.x);
+ hsv.v(1 - p.y);
+ SV(p.x, p.y);
+ chg(hsv);
+ });
+
+ H(hsv.h());
+ SV(hsv.s(), hsv.v());
+
+ function H(h) {
+ p2.style.top = utils.clamp(h * 100, 0, 100) + '%';
+ twod.style.background = color.hsva({h: h, s: 1, v: 1}).toCSS();
+ }
+
+ function SV(s, v) {
+ p1.style.left = utils.clamp(s * 100, 0, 100) + '%';
+ p1.style.top = utils.clamp(v * 100, 0, 100) + '%';
+ }
+
+ return {
+ e: picker,
+ update: function() {
+ chg(hsv);
+ },
+ set: function(c) {
+ hsv = c;
+ H(c.h());
+ SV(c.s(), c.v());
+ }
+ };
+ }
+}
+
+function div(klass, p) {
+ var elem = document.createElement('div');
+ elem.className = klass;
+ p.appendChild(elem);
+
+ return elem;
+}
+
+function drag(elem, fn) {
+ var dragging = false;
+
+ // http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
+ var isTouch = typeof(window.ontouchstart) != 'undefined';
+
+ if(isTouch) {
+ elem.ontouchstart = function(e) {
+ e.preventDefault();
+ dragging = true;
+
+ document.ontouchend = '';
+ document.ontouchmove = '';
+
+ callCb(e);
+ };
+ elem.ontouchmove = callCb;
+ elem.ontouchend = function(e) {
+ e.preventDefault();
+ dragging = false;
+ };
+ }
+ else {
+ elem.onmousedown = function(e) {
+ e.preventDefault();
+ dragging = true;
+
+ document.onmouseup = function() {
+ dragging = false;
+
+ document.onmouseup = '';
+ document.onmousemove = '';
+ };
+
+ document.onmousemove = callCb;
+ };
+ elem.onmouseup = function(e) {
+ e.preventDefault();
+ dragging = false;
+
+ callCb(e);
+ };
+ }
+
+ function callCb(e) {
+ e.preventDefault();
+
+ var xOffset = elem.offsetLeft;
+ var yOffset = elem.offsetTop;
+ var width = elem.clientWidth;
+ var height = elem.clientHeight;
+
+ fn({x: (mouseX(e) - xOffset) / width, y: (mouseY(e) - yOffset) / height});
+ }
+}
+
+// http://javascript.about.com/library/blmousepos.htm
+function mouseX(evt) {
+ if(evt.pageX) return evt.pageX;
+ else if(evt.clientX)
+ return evt.clientX + (document.documentElement.scrollLeft ?
+ document.documentElement.scrollLeft :
+ document.body.scrollLeft);
+}
+function mouseY(evt) {
+ if(evt.pageY) return evt.pageY;
+ else if(evt.clientY)
+ return evt.clientY + (document.documentElement.scrollTop ?
+ document.documentElement.scrollTop :
+ document.body.scrollTop);
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.