You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tinyColorPicker/jqColorPicker.js

300 lines
9.5 KiB
JavaScript

;(function($, Colors, undefined){
'use strict';
var $document = $(document),
_instance,
_colorPicker,
_color,
_options,
_selector = '',
_$trigger,
_$UI, _$xy_slider, _$xy_cursor, _$z_cursor , _$alpha , _$alpha_cursor,
_pointermove = 'touchmove mousemove pointermove',
_pointerup = 'touchend mouseup pointerup',
_GPU = false,
_animate = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame || function(cb){cb()},
_html = '<div class="cp-color-picker"><div class="cp-z-slider"><div c' +
'lass="cp-z-cursor"></div></div><div class="cp-xy-slider"><div cl' +
'ass="cp-white"></div><div class="cp-xy-cursor"></div></div><div ' +
'class="cp-alpha"><div class="cp-alpha-cursor"></div></div></div>',
// 'grunt-contrib-uglify' puts all this back to one single string...
_css = '.cp-color-picker{position:absolute;overflow:hidden;padding:6p' +
'x 6px 0;background-color:#444;color:#bbb;font-family:Arial,Helve' +
'tica,sans-serif;font-size:12px;font-weight:400;cursor:default;bo' +
'rder-radius:5px}.cp-color-picker>div{position:relative;overflow:' +
'hidden}.cp-xy-slider{float:left;height:128px;width:128px;margin-' +
'bottom:6px;background:linear-gradient(to right,#FFF,rgba(255,255' +
',255,0))}.cp-white{height:100%;width:100%;background:linear-grad' +
'ient(rgba(0,0,0,0),#000)}.cp-xy-cursor{position:absolute;top:0;w' +
'idth:10px;height:10px;margin:-5px;border:1px solid #fff;border-r' +
'adius:100%;box-sizing:border-box}.cp-z-slider{float:right;margin' +
'-left:6px;height:128px;width:20px;background:linear-gradient(red' +
' 0,#f0f 17%,#00f 33%,#0ff 50%,#0f0 67%,#ff0 83%,red 100%)}.cp-z-' +
'cursor{position:absolute;margin-top:-4px;width:100%;border:4px s' +
'olid #fff;border-color:transparent #fff;box-sizing:border-box}.c' +
'p-alpha{clear:both;width:100%;height:16px;margin:6px 0;backgroun' +
'd:linear-gradient(to right,#444),rgba(0,0,0,0))}.cp-alpha-cursor' +
'{position:absolute;margin-left:-4px;height:100%;border:4px solid' +
' #fff;border-color:#fff transparent;box-sizing:border-box}',
ColorPicker = function(options) {
_color = this.color = new Colors(options);
_options = _color.options;
};
ColorPicker.prototype = {
render: preRender,
toggle: toggle
}
function extractValue(elm) {
return elm.value || elm.getAttribute('value') ||
$(elm).css('background-color') || '#fff';
}
function resolveEventType(event) {
return event.originalEvent.touches ?
event.originalEvent.touches[0] : event;
}
function findElement($elm) {
return $($elm.find(_options.doRender)[0] || $elm[0]);
}
function toggle(event) {
var $this = $(this),
position,
$window = $(window);
if (event) {
position = $this.offset();
_$trigger = findElement($this);
(_$UI || build()).show(_options.animationSpeed, function() {
_$alpha._width = _$alpha.width();
_$xy_slider._width = _$xy_slider.width();
_$xy_slider._height = _$xy_slider.height();
_color.setColor(extractValue(_$trigger[0]));
preRender(true);
}).css({
'left': !(_$UI[0]._right = position.left + _$UI[0]._width >
$window.scrollLeft() + $window.width() ? 4 : '') ?
position.left : '',
'top': !(_$UI[0]._bottom = position.top + $this.outerHeight() +
_$UI[0]._height > $window.scrollTop() + $window.height() ?
-$window.scrollTop() + 4 : '') ?
position.top + $this.outerHeight() : '',
'right': _$UI[0]._right,
'bottom': _$UI[0]._bottom
});
} else {
$(_$UI).hide(_options.animationSpeed, function() {
_$trigger.blur();
preRender(false);
});
}
};
function build() {
$('head').append('<style type="text/css">' +
(_options.css || _css) + (_options.cssAddon || '') + '</style>');
return _$UI = $(_html).css({'margin': _options.margin}).
appendTo('body').
show(0, function() {
_GPU = _options.GPU && $(this).css('perspective') === '';
_$xy_slider = $('.cp-xy-slider', this);
_$xy_cursor = $('.cp-xy-cursor', this);
_$z_cursor = $('.cp-z-cursor', this);
_$alpha = $('.cp-alpha', this).toggle(!!_options.opacity);
_$alpha_cursor = $('.cp-alpha-cursor', this);
_options.buidCallback.call(_colorPicker, $(this));
this._width = this.offsetWidth;
this._height = this.offsetHeight;
}).hide().
on('touchstart mousedown pointerdown',
'.cp-xy-slider,.cp-z-slider,.cp-alpha', pointerdown)
;
}
function pointerdown(e) {
var action = this.className.
replace(/cp-(.*?)(?:\s*|$)/, '$1').replace('-', '_');
e.preventDefault();
_$trigger._offset = $(this).offset();
(action = action === 'xy_slider' ? xy_slider :
action === 'z_slider' ? z_slider : alpha)(e);
$document.on(_pointerup, pointerup).on(_pointermove, action);
}
function pointerup(e) {
$document.off(_pointermove).off(_pointerup);
}
function xy_slider(event) {
var e = resolveEventType(event),
x = e.pageX - _$trigger._offset.left,
y = e.pageY - _$trigger._offset.top;
_color.setColor({
s: x / _$xy_slider._width * 100,
v: 100 - (y / _$xy_slider._height * 100)
}, 'hsv');
preRender();
}
function z_slider(event) {
var z = resolveEventType(event).pageY - _$trigger._offset.top,
hsv = _color.colors.hsv;
_color.setColor({h: 360 - (z / _$xy_slider._height * 360)}, 'hsv');
preRender();
}
function alpha(event) {
var x = resolveEventType(event).pageX - _$trigger._offset.left,
alpha = x / _$alpha._width;
_color.setColor({}, 'rgb', alpha > 1 ? 1 : alpha < 0 ? 0 : alpha);
preRender();
}
function preRender(toggled) {
var colors = _color.colors,
hueRGB = colors.hueRGB,
RGB = colors.RND.rgb,
HSL = colors.RND.hsl,
dark = '#222',
light = '#ddd',
colorMode = _$trigger.data('colorMode'),
isAlpha = colors.alpha !== 1,
alpha = Math.round(colors.alpha * 100) / 100,
RGBInnerText = RGB.r + ', ' + RGB.g + ', ' + RGB.b,
text = (colorMode === 'HEX' && !isAlpha ? '#' + colors.HEX :
colorMode === 'rgb' || (colorMode === 'HEX' && isAlpha) ?
(!isAlpha ? 'rgb(' + RGBInnerText + ')' :
'rgba(' + RGBInnerText + ', ' + alpha + ')') :
('hsl' + (isAlpha ? 'a(' : '(') + HSL.h + ', ' + HSL.s + '%, ' +
HSL.l + '%' + (isAlpha ? ', ' + alpha : '') + ')')),
HUEContrast = colors.HUELuminance > 0.22 ? dark : light,
alphaContrast = colors.rgbaMixBlack.luminance > 0.22 ? dark : light,
h = (1 - colors.hsv.h) * _$xy_slider._height,
s = colors.hsv.s * _$xy_slider._width,
v = (1 - colors.hsv.v) * _$xy_slider._height,
a = alpha * _$alpha._width,
t3d = _GPU ? 'translate3d' : '';
_$xy_slider._css = {
backgroundColor: 'rgb(' +
hueRGB.r + ',' + hueRGB.g + ',' + hueRGB.b + ')'};
_$xy_cursor._css = {
transform: t3d + '(' + s + 'px, ' + v + 'px, 0)',
left: !_GPU ? s : '',
top: !_GPU ? v : '',
borderColor : colors.RGBLuminance > 0.22 ? dark : light
};
_$z_cursor._css = {
transform: t3d + '(0, ' + h + 'px, 0)',
top: !_GPU ? h : '',
borderColor : 'transparent ' + HUEContrast
};
_$alpha._css = {backgroundColor: 'rgb(' + RGBInnerText + ')'};
_$alpha_cursor._css = {
transform: t3d + '(' + a + 'px, 0, 0)',
left: !_GPU ? a : '',
borderColor : alphaContrast + ' transparent'
};
_$trigger._css = {
backgroundColor : text,
color: colors.rgbaMixBGMixCustom.luminance > 0.22 ? dark : light
};
_$trigger.text = _$trigger.val() !== text ? text : '';
toggled !== undefined ? render(toggled) : _animate(render);
}
// As _animate() is actually requestAnimationFrame(), render() gets called
// decoupled from any pointer action (whenever the browser decides to do
// so) as an event. preRender() is coupled to toggle() and all pointermove
// actions; that's where all the calculations happen. render() can now be
// called without extra calculations which results in faster rendering.
function render(toggled) {
_$xy_slider.css(_$xy_slider._css);
_$xy_cursor.css(_$xy_cursor._css);
_$z_cursor.css(_$z_cursor._css);
_$alpha.css(_$alpha._css);
_$alpha_cursor.css(_$alpha_cursor._css);
_options.doRender && _$trigger.css(_$trigger._css);
_$trigger.text && _$trigger.val(_$trigger.text);
_options.renderCallback.call(
_colorPicker,
_$trigger,
typeof toggled === 'boolean' ? toggled : undefined
);
}
$.fn.colorPicker = function(options) {
var noop = function(){};
options = $.extend({
animationSpeed: 150,
GPU: true,
doRender: true,
customBG: '#FFF',
opacity: true,
renderCallback: noop,
buidCallback: noop,
body: document.body
// css: '',
// cssAddon: '',
// margin: '',
// preventFocus: false
}, options);
_instance = _instance ? _instance.add(this) : this;
_instance.colorPicker = _colorPicker ||
(_colorPicker = new ColorPicker(options));
_selector += (_selector ? ', ' : '') + this.selector;
$(options.body).off('.a').
on('touchstart.a mousedown.a pointerdown.a', function(e) {
var $target = $(e.target);
if ($.inArray($target.closest(_selector)[0],
_instance) === -1 &&
!$target.closest(_$UI).length) {
toggle();
}
}).
on('focus.a click.a', _selector, toggle).
on('change.a', _selector, function() {
_color.setColor(this.value);
_instance.colorPicker.render();
});
return this.each(function() {
var value = extractValue(this),
mode = value.split('('),
$elm = findElement($(this));
$elm.data('colorMode', mode[1] ? mode[0].substr(0, 3) : 'HEX').
attr('readonly', _options.preventFocus);
options.doRender && $elm.
css({'background-color': value,
'color': function() {
return _color.setColor(value).
rgbaMixBGMixCustom.luminance > 0.22 ? '#222' : '#ddd'
}
});
});
};
})(window.jQuery, Colors);