/** * @class ep * */ /* * @copyright © Copyright 2006-2010, epages GmbH, All Rights Reserved. * * @module ep.colorpicker * * @based ColorJack (http://www.colorjack.com/software/dhtml+color+picker.html) license (http://creativecommons.org/licenses/by/3.0/) */ define("ep/colorpicker", [ "jquery", "ep", "ep/color" ], function ($, ep) { /** * Create a canvas color picker. * * Create a "canvas" color picker. * * The `.elem` property of an instance is the main element of the color picker. * Use `.elem` to append the color picker to any DOM element. * * ### Examples * Create a color picker and change body background on select color. * * JavaScript: * * var picker = new ep.Colorpicker({ * hex: '#330099', * callback: function( color ){ * $('body').css( 'background-color', '#'+color ); * } * }); * * $('body').append(picker.elem); * * * ### Dependencies * * + `ep` * + `ep.color` * * @param {Object} [options] A map of additional options pass to the method. * @param {String} [options.hex] A hex formated startup color. * @param {Integer} [options.size] Size of the main color select field. * @param {Integer} [options.margin] Margin around the color select fields. * @param {Function} [options.callback] A function to be called after each step of the color selection. Receives the selected color as argument: `callback( color )`. * * @method Colorpicker * @static * @member ep * * @since 6.12.0 */ ep.Colorpicker = function( options ){ /// loading properties var self = this, o = self.options = $.extend({ hex: '#ffffff', // default color size: 200, // size of colorpicker margin: 10 // margin around colorpicker },options); // public self.setHex = function( color, redraw ){ var hsb = $.extend( {h:0,s:0,b:100}, ep.color.hexToHsb(color) ); if( hsb ){ self.options.hex = color; self.hue = hsb.h; self.sat = hsb.s; self.lum = hsb.b; if( redraw ){ self.drawSample( true ); } } }; self.setHex( self.options.hex ); self.size = o.size; self.margin = o.margin; self.offset = self.margin / 2; self.hueWidth = 30; /// creating media-resources var arrows = document.createElement("canvas"); arrows.width = 40; arrows.height = 5; arrows.distance = 6; (function () { // creating arrows var ctx = arrows.getContext("2d"); var width = 3; var height = 5; var size = 9; var top = -size / 4; var left = 1; for (var n = 0; n < 20; n++) { // multiply anti-aliasing ctx.beginPath(); ctx.fillStyle = "#000"; ctx.moveTo(left + size / 4, size / 2 + top); ctx.lineTo(left, size / 4 + top); ctx.lineTo(left, size / 4 * 3 + top); ctx.fill(); } ctx.translate(width, height); ctx.rotate(180 * Math.PI / 180); // rotate arrows ctx.drawImage(arrows, -29, 0); ctx.translate(-width, -height); })(); var circle = document.createElement("canvas"); circle.width = 10; circle.height = 10; (function () { // creating circle-selection var ctx = circle.getContext("2d"); ctx.lineWidth = 1; ctx.beginPath(); var x = circle.width / 2; var y = circle.width / 2; ctx.arc(x, y, 4.5, 0, Math.PI * 2, true); ctx.strokeStyle = '#000'; ctx.stroke(); ctx.beginPath(); ctx.arc(x, y, 3.5, 0, Math.PI * 2, true); ctx.strokeStyle = '#FFF'; ctx.stroke(); })(); /// creating colorpicker sliders var canvas = (self.elem = $('<canvas>'))[0]; var ctx = canvas.getContext("2d"); canvas.className = 'ep-colorpicker'; canvas.width = this.size + this.hueWidth + this.margin; canvas.height = this.size + this.margin; canvas.onmousedown = function (e) { var down = (e.type == "mousedown"); var offset = self.margin / 2; var abs = abPos(canvas); var x0 = (e.pageX - abs.x) - offset; var y0 = (e.pageY - abs.y) - offset; var x = clamp(x0, 0, canvas.width); var y = clamp(y0, 0, self.size); if (x <= self.size) { // saturation-value selection canvas.style.cursor = 'crosshair'; if( down ){ dragElement({ type: "relative", event: e, element: canvas, callback: function (coords, state) { var x = clamp(coords.x - self.offset, 0, self.size); var y = clamp(coords.y - self.offset, 0, self.size); self.sat = x / self.size * 100; // scale saturation self.lum = 100 - (y / self.size * 100); // scale value self.drawSample(); } }); } } else if (x > self.size + self.margin - arrows.distance && x <= self.size + self.hueWidth + arrows.distance) { // hue + arrows selection canvas.style.cursor = 'crosshair'; if( down ){ dragElement({ type: "relative", event: e, element: canvas, callback: function (coords, state) { var y = clamp(coords.y - self.offset, 0, self.size); self.hue = Math.min(1, y / self.size) * 360; self.drawSample(); } }); } } else { // margin between hue/saturation-value canvas.style.cursor = 'default'; } return false; // prevent selection }; /// helper functions this.drawSample = function( prevent ){ // clearing canvas ctx.clearRect(0, 0, canvas.width, canvas.height); self.drawSquare(); self.drawHue(); // retrieving hex-code var hex = ep.color.hsbToHex({ h: self.hue, s: self.sat, b: self.lum }); // arrow-selection var y = (self.hue / 362) * self.size - 2; ctx.drawImage(arrows, self.size + self.offset + 4, Math.round(y) + self.offset); // circle-selection var x = self.sat / 100 * self.size; y = (1 - (self.lum / 100)) * self.size; x = x - circle.width / 2; y = y - circle.height / 2; ctx.drawImage(circle, Math.round(x) + self.offset, Math.round(y) + self.offset); // run custom code if( self.options.callback && !prevent ){ self.options.callback(hex); } }; this.drawSquare = function () { // retrieving hex-code var hex = ep.color.hsbToHex({ h: self.hue, s: 100, b: 100 }); var offset = self.offset; var size = self.size; // drawing color ctx.fillStyle = "#" + hex; ctx.fillRect(offset, offset, size, size); // overlaying saturation var gradient = ctx.createLinearGradient(offset, offset, size + offset, 0); gradient.addColorStop(0, "rgba(255, 255, 255, 1)"); gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); ctx.fillStyle = gradient; ctx.fillRect(offset, offset, size, size); // overlaying value gradient = ctx.createLinearGradient(offset, offset, 0, size + offset); gradient.addColorStop(0, "rgba(0, 0, 0, 0)"); gradient.addColorStop(1, "rgba(0, 0, 0, 1)"); ctx.fillStyle = gradient; ctx.fillRect(offset, offset, size, size); // drawing outer bounds ctx.strokeStyle = "rgba(255,255,255,0.15)"; ctx.strokeRect(offset+0.5, offset+0.5, size-1, size-1); }; this.drawHue = function () { // drawing hue selector var left = self.size + self.margin + self.offset; var gradient = ctx.createLinearGradient(0, 0, 0, self.size); gradient.addColorStop(0, "rgba(255, 0, 0, 1)"); gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)"); gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)"); gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)"); gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)"); gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)"); gradient.addColorStop(1, "rgba(255, 0, 0, 1)"); ctx.fillStyle = gradient; ctx.fillRect(left, self.offset, 20, self.size); // drawing outer bounds ctx.strokeStyle = "rgba(255,255,255,0.2)"; ctx.strokeRect(left + 0.5, self.offset + 0.5, 19, self.size-1); }; this.destory = function (){ for( var key in self ){ delete self[key]; } }; // drawing color selection this.drawSample(true); return this; }; /* GLOBALS LIBRARY */ var dragElement = function(props) { function mouseMove(e, state) { if( state === undefined ){ state = "move"; } var coord = XY(e); switch (props.type) { // execute test only for "relative" because the other types aren't used in this context //#JSCOVERAGE_IF false case "difference": props.callback({ x: coord.x + oX - eX, y: coord.y + oY - eY }, state); break; //#JSCOVERAGE_ENDIF case "relative": props.callback({ x: coord.x - oX, y: coord.y - oY }, state); break; //#JSCOVERAGE_IF false default: // "absolute" props.callback({ x: coord.x, y: coord.y }, state); break; //#JSCOVERAGE_ENDIF } } function mouseUp(e) { window.removeEventListener("mousemove", mouseMove, false); window.removeEventListener("mouseup", mouseUp, false); mouseMove(e, "up"); } // current element position var el = props.element; var origin = abPos(el); var oX = origin.x; var oY = origin.y; // current mouse position var e = props.event; var coord = XY(e); var eX = coord.x; var eY = coord.y; // events window.addEventListener("mousemove", mouseMove, false); window.addEventListener("mouseup", mouseUp, false); mouseMove(e, "down"); // run mouse-down }; var clamp = function(n, min, max) { return (n < min) ? min : ((n > max) ? max : n); }; var XY = window.ActiveXObject ? // fix XY to work in various browsers function(event) { return { x: event.clientX + document.documentElement.scrollLeft, y: event.clientY + document.documentElement.scrollTop }; } : function(event) { return { x: event.pageX, y: event.pageY }; }; var abPos = function(o) { o = typeof(o) == 'object' ? o : $(o); var offset = { x: 0, y: 0 }; while(o != null) { offset.x += o.offsetLeft; offset.y += o.offsetTop; o = o.offsetParent; } return offset; }; return ep; });