lu2
Version:
Simple and flexible UI component library based on native HTML and JavaScript
1,223 lines (1,043 loc) • 43.8 kB
JavaScript
/**
* @Color.js
* @author zhangxinxu
* @version
* Created: 16-06-03
*/
/* global module */
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
global.Drop = require('./Drop');
module.exports = factory();
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
define(factory);
} else {
global.Color = factory();
}
// eslint-disable-next-line
}((typeof global !== 'undefined') ? global
// eslint-disable-next-line
: ((typeof window !== 'undefined') ? window
: ((typeof self !== 'undefined') ? self : this)), function (require) {
// require
var Drop = (this || self).Drop;
if (typeof require == 'function' && !Drop) {
Drop = require('common/ui/Drop');
} else if (!Drop) {
window.console.error('need Drop.js');
return {};
}
/**
* 基于HTML原生color颜色选择
* 兼容IE9+
* type=color Hex format
*/
// 样式类名统一处理
var CL = {
add: function () {
return ['ui-color'].concat([].slice.call(arguments)).join('-');
},
toString: function () {
return 'ui-color';
}
};
var objEventType = {
start: 'mousedown',
move: 'mousemove',
end: 'mouseup'
};
if ('ontouchstart' in document) {
objEventType = {
start: 'touchstart',
move: 'touchmove',
end: 'touchend'
};
}
// 其他变量
var ACTIVE = 'active';
var BGCOLOR = 'background-color';
var defaultValue = '#000000';
/* 一些颜色间的相互转换的公用方法 */
// hsl颜色转换成十六进制颜色
var funHslToHex = function (h, s, l) {
var r, g, b;
if (s == 0) {
// 非彩色的
r = g = b = l;
} else {
var hue2rgb = function (p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
var arrRgb = [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
return arrRgb.map(function (rgb) {
rgb = rgb.toString(16);
if (rgb.length == 1) {
return '0' + rgb;
}
return rgb;
}).join('');
};
// 16进制颜色转换成hsl颜色表示
var funHexToHsl = function (hex) {
var r = parseInt(hex.slice(0, 2), 16) / 255;
var g = parseInt(hex.slice(2, 4), 16) / 255;
var b = parseInt(hex.slice(4, 6), 16) / 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s;
var l = (max + min) / 2;
if (max == min) {
// 非彩色
h = s = 0;
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
};
// rgb/rgba颜色转hex
var funRgbToHex = function (rgb) {
if (!rgb) {
return defaultValue;
}
var arr = [];
// 如果是不全的hex值,不全
// 有没有#都支持
rgb = rgb.replace('#', '').toLowerCase();
if (/^[0-9A-F]{1,6}$/i.test(rgb)) {
return '#' + rgb.repeat(Math.ceil(6 / rgb.length)).slice(0, 6);
}
// 如果是rgb(a)色值,防止不合法的值报错
arr = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i) || [];
var hex = function (x) {
return ('0' + parseInt(x, 10).toString(16)).slice(-2);
};
if (arr.length == 4) {
return '#' + hex(arr[1]) + hex(arr[2]) + hex(arr[3]);
}
return defaultValue;
};
/**
* 颜色选择实例方法
* @param {[type]} element [description]
* @param {[type]} options [description]
*/
var Color = function (element, options) {
// 参数
var defaults = {
offsets: {
x: 0,
y: 0
},
edgeAdjust: false,
position: '7-5',
onShow: function () {},
onHide: function () {}
};
var objParams = Object.assign({}, defaults, options || {});
// element如果是选择器
if (typeof element == 'string') {
element = document.querySelector(element);
}
// el需要是原生的type=color的输入框
if (!element) {
return;
}
// 必须是透明度为0
if (window.getComputedStyle(element).opacity !== '0') {
return;
}
var eleInput = element;
// 避免重复初始化
if (eleInput.data && eleInput.data.color) {
return eleInput.data.color;
}
// 一些默认的属性值
var id = eleInput.id;
if (!id) {
// 创建随机id
id = 'lulu_' + (Math.random() + '').split('.')[1];
eleInput.id = id;
}
// 只读
eleInput.setAttribute('readonly', 'readonly');
// 阻止默认的颜色选择出现
eleInput.addEventListener('click', function (event) {
event.preventDefault();
});
// Edge14-Edge18
if (eleInput.type == 'color' && window.msCredentials) {
eleInput.addEventListener('focus', function () {
this.blur();
});
}
// 元素构建
// track是替换输入框的色块元素的轨道
var eleTrack = document.createElement('label');
eleTrack.setAttribute('for', id);
eleTrack.classList.add(CL.add('track'));
// thumb是替换输入框的色块元素的色块区域
var eleThumb = document.createElement('span');
eleThumb.classList.add(CL.add('thumb'));
// 前置插入
eleTrack.appendChild(eleThumb);
eleInput.insertAdjacentElement('beforebegin', eleTrack);
// 浮层容器
var eleContainer = document.createElement('div');
eleContainer.classList.add(CL.add('container'));
// 全局暴露的一些元素
this.element = {
input: eleInput,
container: eleContainer,
track: eleTrack,
thumb: eleThumb
};
// 暴露的回调方法
this.callback = {
show: objParams.onShow,
hide: objParams.onHide
};
this.params = {
offsets: objParams.offsets,
edgeAdjust: objParams.edgeAdjust,
position: objParams.position
};
// 全局的基础色值
var arrBasicColor = ['0', '3', '6', '9', 'c', 'f'];
var arrFixedColor = arrBasicColor.concat('eb4646', '1cad70', '2a80eb', 'f59b00');
this.color = {
basic: arrBasicColor,
fixed: arrFixedColor
};
// 事件绑定
this.initEvents();
// 初始化
this.value(eleInput.value);
// data暴露
if (eleInput.data) {
eleInput.data = {};
}
eleInput.data.color = this;
return this;
};
/**
* 颜色选择器的事件们
* @return {[type]} [description]
*/
Color.prototype.initEvents = function () {
var objElement = this.element;
// 元素们
var eleInput = objElement.input;
var eleTrack = objElement.track;
var eleContainer = objElement.container;
// 参数
var objParams = this.params;
// 浮层显隐与定位
this.drop = new Drop(eleInput, eleContainer, {
eventType: 'click',
offsets: objParams.offsets,
edgeAdjust: objParams.edgeAdjust,
position: objParams.position,
onShow: function () {
this.show();
// 颜色面板边界超出的微调
if (objParams.edgeAdjust == false) {
var objRect = eleContainer.getBoundingClientRect();
if (objRect.left < 3) {
eleContainer.style.left = '3px';
} else if (objRect.right - screen.width > 3) {
eleContainer.style.left = (screen.width - objRect.width - 3) + 'px';
}
}
}.bind(this),
onHide: function () {
this.hide();
eleContainer.style.marginLeft = 0;
}.bind(this)
});
// 键盘无障碍访问的处理
eleInput.addEventListener('focus', function () {
if (window.isKeyEvent) {
eleTrack.classList.add('ui-outline');
}
});
eleInput.addEventListener('blur', function () {
eleTrack.classList.remove('ui-outline');
});
};
/**
* container内的一些事件
* @return {Object} 返回当前实例对象
*/
Color.prototype.events = function () {
var objElement = this.element;
// 元素
var eleContainer = objElement.container;
// 更多元素
// 元素
var eleCircle = objElement.circle;
var eleFill = objElement.fill;
var eleArrow = objElement.arrow;
// 面板内部唯一的输入框元素
var eleField = objElement.field;
// var keycode = {
// 37: 'left',
// 38: 'up',
// 39: 'right',
// 40: 'down',
// 13: 'enter'
// };
eleContainer.addEventListener('click', function (event) {
var eleTarget = event.target;
// IE可能是文件节点
if (!eleTarget.matches || !eleTarget.matches('a, button')) {
return;
}
// 选择的颜色值
var strValue = '';
// 当前类名
var strCl = eleTarget.className;
// 按钮分类别处理
if (/cancel/.test(strCl)) {
// 1. 取消按钮
this.hide();
} else if (/ensure/.test(strCl)) {
// 2. 确定按钮
// 赋值
strValue = eleField.value;
if (strValue) {
this.value('#' + strValue);
}
this.hide();
} else if (/lump/.test(strCl)) {
// 3. 小色块
strValue = eleTarget.getAttribute('data-color');
eleField.value = strValue;
this.match();
} else if (/switch/.test(strCl)) {
// 4. 面板类名切换按钮
if (eleTarget.innerHTML == '更多') {
objElement.more.style.display = 'block';
objElement.basic.style.display = 'none';
eleTarget.innerHTML = '基本';
} else {
objElement.more.style.display = 'none';
objElement.basic.style.display = 'block';
eleTarget.innerHTML = '更多';
}
// 面板的色块啊,圆和尖角位置匹配
this.match();
} else if (/cover/.test(strCl)) {
// 5. 渐变色的覆盖层
// offsetLeft, offsetTop
var objRect = eleTarget.getBoundingClientRect();
var numOffsetLeft = event.pageX - objRect.left;
var numOffsetTop = event.pageY - window.pageYOffset - objRect.top;
// width, height
var numWidth = eleTarget.clientWidth;
var numHeight = eleTarget.clientHeight;
// color
var numColorH, strColorS;
if (eleCircle && eleFill && eleArrow) {
if (/white/.test(strCl) == true) {
numColorH = numOffsetLeft / numWidth;
strColorS = 1 - numOffsetTop / numHeight;
// 圈圈定位
eleCircle.style.left = numOffsetLeft + 'px';
eleCircle.style.top = numOffsetTop + 'px';
var strHsl = 'hsl(' + [360 * numColorH, 100 * strColorS + '%', '50%'].join() + ')';
eleCircle.style[BGCOLOR] = strHsl;
} else {
eleArrow.style.top = numOffsetTop + 'px';
}
// 赋值
eleField.value = this.getValueByStyle().replace('#', '');
// UI变化
this.match();
}
}
}.bind(this));
// 输入框事件
eleField.addEventListener('input', function () {
// 获取面板中的input
var value = this.element.field.value;
if (/^[0-9A-F]{6}$/i.test(value)) {
this.match();
} else if (/^[0-9A-F]{3}$/i.test(value)) {
this.match(funRgbToHex('#' + value).replace('#', ''));
}
}.bind(this));
eleField.addEventListener('keyup', function (event) {
if (event.keyCode == 13) {
var strValue = eleField.value;
var strOldvalue = strValue;
if (strValue) {
strValue = funRgbToHex('#' + strValue);
if (strValue != strOldvalue) {
// 支持输入#000
eleField.value = strValue;
}
this.value(strValue);
}
this.hide();
}
}.bind(this));
// 滑块拖动事件
var objPosArrow = {};
var objPosCircle = {};
// 三角上下
eleArrow.addEventListener(objEventType.start, function (event) {
event.preventDefault();
if (event.touches && event.touches.length) {
event = event.touches[0];
}
objPosArrow.pageY = event.pageY;
objPosArrow.top = parseFloat(window.getComputedStyle(eleArrow).top);
});
eleFill.addEventListener(objEventType.start, function (event) {
event.preventDefault();
// 5. 渐变色的覆盖层
// offsetLeft, offsetTop
var eleTarget = event.target;
var objRect = eleTarget.getBoundingClientRect();
var numOffsetTop = event.pageY - window.pageYOffset - objRect.top;
eleArrow.style.top = numOffsetTop + 'px';
if (event.touches && event.touches.length) {
event = event.touches[0];
}
objPosArrow.pageY = event.pageY;
objPosArrow.top = parseFloat(window.getComputedStyle(eleArrow).top);
// 赋值,此次赋值,无需重定位
eleField.value = this.getValueByStyle().replace('#', '');
this.match(false);
}.bind(this));
// 圆圈移动
eleCircle.parentElement.querySelectorAll('a').forEach(function (eleRegion) {
eleRegion.addEventListener(objEventType.start, function (event) {
event.preventDefault();
if (event.touches && event.touches.length) {
event = event.touches[0];
}
objPosCircle.pageY = event.pageY;
objPosCircle.pageX = event.pageX;
// 当前位移位置
eleCircle.style.left = event.offsetX + 'px';
eleCircle.style.top = event.offsetY + 'px';
objPosCircle.top = parseFloat(event.offsetY);
objPosCircle.left = parseFloat(event.offsetX);
// 最大位置范围
var objMaxPos = {
top: eleCircle.parentElement.clientHeight,
left: eleCircle.parentElement.clientWidth
};
// 根据目标位置位置和变色
var numColorH = event.offsetX / objMaxPos.left;
var strColorS = 1 - event.offsetY / objMaxPos.top;
var strHsl = 'hsl(' + [360 * numColorH, 100 * strColorS + '%', '50%'].join() + ')';
eleCircle.style[BGCOLOR] = strHsl;
// 赋值,此次赋值,无需重定位
eleField.value = this.getValueByStyle().replace('#', '');
this.match(false);
}.bind(this));
}.bind(this));
// 圆圈移动
eleCircle.addEventListener(objEventType.start, function (event) {
event.preventDefault();
if (event.touches && event.touches.length) {
event = event.touches[0];
}
objPosCircle.pageY = event.pageY;
objPosCircle.pageX = event.pageX;
var objStyleCircle = window.getComputedStyle(eleCircle);
// 当前位移位置
objPosCircle.top = parseFloat(objStyleCircle.top);
objPosCircle.left = parseFloat(objStyleCircle.left);
});
document.addEventListener(objEventType.move, function (event) {
if (typeof objPosArrow.top == 'number') {
event.preventDefault();
if (event.touches && event.touches.length) {
event = event.touches[0];
}
var numTop = objPosArrow.top + (event.pageY - objPosArrow.pageY);
var numMaxTop = eleArrow.parentElement.clientHeight;
// 边界判断
if (numTop < 0) {
numTop = 0;
} else if (numTop > numMaxTop) {
numTop = numMaxTop;
}
eleArrow.style.top = numTop + 'px';
// 赋值,此次赋值,无需重定位
eleField.value = this.getValueByStyle().replace('#', '');
this.match(false);
} else if (typeof objPosCircle.top == 'number') {
event.preventDefault();
if (event.touches && event.touches.length) {
event = event.touches[0];
}
var objPos = {
top: objPosCircle.top + (event.pageY - objPosCircle.pageY),
left: objPosCircle.left + (event.pageX - objPosCircle.pageX)
};
// 最大位置范围
var objMaxPos = {
top: eleCircle.parentElement.clientHeight,
left: eleCircle.parentElement.clientWidth
};
// 边界判断
if (objPos.left < 0) {
objPos.left = 0;
} else if (objPos.left > objMaxPos.left) {
objPos.left = objMaxPos.left;
}
if (objPos.top < 0) {
objPos.top = 0;
} else if (objPos.top > objMaxPos.top) {
objPos.top = objMaxPos.top;
}
// 根据目标位置位置和变色
var numColorH = objPos.left / objMaxPos.left;
var strColorS = 1 - objPos.top / objMaxPos.top;
// 圈圈定位
eleCircle.style.left = objPos.left + 'px';
eleCircle.style.top = objPos.top + 'px';
var strHsl = 'hsl(' + [360 * numColorH, 100 * strColorS + '%', '50%'].join() + ')';
eleCircle.style[BGCOLOR] = strHsl;
// 赋值
eleField.value = this.getValueByStyle().replace('#', '');
// UI变化
this.match(false);
}
}.bind(this), {
passive: false
});
document.addEventListener(objEventType.end, function () {
objPosArrow.top = null;
objPosCircle.top = null;
});
// 滑块的键盘支持
eleFill.parentElement.querySelectorAll('a').forEach(function (eleButton) {
eleButton.addEventListener('keydown', function (event) {
// 上下控制
if (event.keyCode == 38 || event.keyCode == 40) {
event.preventDefault();
var numTop = parseFloat(window.getComputedStyle(eleArrow).top);
var numMaxTop = eleFill.clientHeight;
if (event.keyCode == 38) {
numTop--;
if (numTop < 0) {
numTop = 0;
}
} else {
numTop++;
if (numTop > numMaxTop) {
numTop = numMaxTop;
}
}
var ariaLabel = eleArrow.getAttribute('aria-label');
eleArrow.style.top = numTop + 'px';
eleArrow.setAttribute('aria-label', ariaLabel.replace(/\d+/, Math.round(100 * numTop / numMaxTop)));
// 赋值,此次赋值,无需重定位
eleField.value = this.getValueByStyle().replace('#', '');
this.match(false);
}
}.bind(this));
}.bind(this));
// 圈圈的键盘访问
// 区域背景的键盘支持
eleCircle.parentElement.querySelectorAll('a').forEach(function (eleRegion) {
eleRegion.addEventListener('keydown', function (event) {
// 上下左右控制
if (event.keyCode >= 37 && event.keyCode <= 40) {
event.preventDefault();
var objStyleCircle = window.getComputedStyle(eleCircle);
var numTop = parseFloat(objStyleCircle.top);
var numLeft = parseFloat(objStyleCircle.left);
var numMaxTop = eleRegion.clientHeight;
var numMaxLeft = eleRegion.clientWidth;
if (event.keyCode == 38) {
// up
numTop--;
if (numTop < 0) {
numTop = 0;
}
} else if (event.keyCode == 40) {
// down
numTop++;
if (numTop > numMaxTop) {
numTop = numMaxTop;
}
} else if (event.keyCode == 37) {
// left
numLeft--;
if (numLeft < 0) {
numLeft = 0;
}
} else if (event.keyCode == 39) {
// down
numLeft++;
if (numLeft > numMaxLeft) {
numLeft = numMaxLeft;
}
}
var numColorH = numLeft / numMaxLeft;
var numColorS = 1 - numTop / numMaxTop;
eleCircle.style.left = numLeft + 'px';
eleCircle.style.top = numTop + 'px';
var strHsl = 'hsl(' + [360 * numColorH, 100 * numColorS + '%', '50%'].join() + ')';
eleCircle.style[BGCOLOR] = strHsl;
// 赋值
eleField.value = this.getValueByStyle().replace('#', '');
this.match();
}
}.bind(this));
}.bind(this));
return this;
};
/**
* container内HTML的创建
* @return {Object} 返回当前实例对象
*/
Color.prototype.create = function () {
// 元素
var eleContainer = this.element.container;
var eleInput = this.element.input;
// switch button
var strHtmlConvert = '<button class="' + CL.add('switch') + '" role="button">更多</button>';
// current color
var strHtmlCurrent = '<div class="' + CL.add('current') + '">\
<i class="' + CL.add('current', 'square') + ' colorCurrent"></i>\
#<input class="' + CL.add('current', 'input') + '">\
</div>';
var arrBasicColor = this.color.basic;
var arrFixedColor = this.color.fixed;
// body
var strHtmlBody = '<div class="' + CL.add('body') + '">' +
(function () {
// basic color picker
var strHtml = '<div class="' + CL.add('basic') + ' colorBasicX" role="listbox">';
var arrCommonColors = (localStorage.commonColors || '').split(',');
// color left
strHtml = strHtml + '<aside class="' + CL.add('basic', 'l') + '">' + (function () {
return arrFixedColor.concat(arrCommonColors[0] || '0ff', arrCommonColors[1] || '800180').map(function (color) {
var strColor = funRgbToHex(color).replace('#', '');
return '<a href="javascript:" class="' + CL.add('lump') + '" data-color="' + strColor + '" aria-label="' + strColor + '" style="' + BGCOLOR + ':#' + strColor + '" role="option"></a>';
}).join('');
})() + '</aside>';
// color main
strHtml = strHtml + '<div class="' + CL.add('basic', 'r') + '">' + (function () {
var strHtmlR = '';
arrBasicColor.forEach(function (r) {
strHtmlR += '<div class="' + CL.add('lump', 'group') + '">';
arrBasicColor.forEach(function (g) {
arrBasicColor.forEach(function (b) {
var strColor = r + r + g + g + b + b;
strHtmlR = strHtmlR + '<a href="javascript:" class="' + CL.add('lump') + '" data-color="' + strColor + '" style="' + BGCOLOR + ':#' + strColor + '" aria-label="' + strColor + '" role="option"></a>';
});
});
strHtmlR += '</div>';
});
return strHtmlR;
})() + '</div>';
return strHtml + '</div>';
})() +
(function () {
var strIdGradient = 'lg-' + eleInput.id;
var strIdGradient2 = 'lg2-' + eleInput.id;
// more color picker
var html = '<div class="' + CL.add('more') + ' colorMoreX">';
// color left
html = html + '<div class="' + CL.add('more', 'l') + '">\
<a href="javascript:" class="' + CL.add('cover', 'white') + '" aria-label="色域背景块" role="region"></a><div class="' + CL.add('circle') + ' colorCircle"></div>\
<svg>\
<defs>\
<linearGradient x1="0" y1="0" x2="1" y2="0" id="' + strIdGradient + '">\
<stop offset="0%" stop-color="#ff0000"></stop>\
<stop offset="16.66%" stop-color="#ffff00"></stop>\
<stop offset="33.33%" stop-color="#00ff00"></stop>\
<stop offset="50%" stop-color="#00ffff"></stop>\
<stop offset="66.66%" stop-color="#0000ff"></stop>\
<stop offset="83.33%" stop-color="#ff00ff"></stop>\
<stop offset="100%" stop-color="#ff0000"></stop>\
</linearGradient>\
</defs>\
<rect x="0" y="0" width="180" height="100" fill="url(#' + strIdGradient + ')"></rect>\
</svg></div><div class="' + CL.add('more', 'r') + '">\
<div class="' + CL.add('more', 'fill') + ' colorFill">\
<a href="javascript:" class="' + CL.add('more', 'cover') + '" aria-label="明度控制背景条" role="region"></a>\
<svg>\
<defs>\
<linearGradient x1="0" y1="0" x2="0" y2="1" id="' + strIdGradient2 + '">\
<stop offset="0%" stop-color="#ffffff"></stop>\
<stop offset="50%" stop-color="rgba(255,255,255,0)"></stop>\
<stop offset="50%" stop-color="rgba(0,0,0,0)"></stop>\
<stop offset="100%" stop-color="' + defaultValue + '"></stop>\
</linearGradient>\
</defs>\
<rect x="0" y="0" width="16" height="100" fill="url(#' + strIdGradient2 + ')"></rect>\
</svg>\
</div>\
<a href="javascript:" class="' + CL.add('more', 'arrow') + ' colorArrow" role="slider" aria-label="明度控制按钮:100%"></a>\
</div>';
return html + '</div>';
})() + '</div>';
// footer
var strHtmlFooter = '<div class="' + CL.add('footer') + '">\
<button class="' + CL.add('button', 'cancel') + '">取消</button><button class="' + CL.add('button', 'ensure') + '">确定</button>\
</div>';
// append
eleContainer.innerHTML = strHtmlConvert + strHtmlCurrent + strHtmlBody + strHtmlFooter;
// 一些元素
Object.assign(this.element, {
field: eleContainer.querySelector('input'),
basic: eleContainer.querySelector('.colorBasicX'),
more: eleContainer.querySelector('.colorMoreX'),
circle: eleContainer.querySelector('.colorCircle'),
fill: eleContainer.querySelector('.colorFill'),
arrow: eleContainer.querySelector('.colorArrow'),
current: eleContainer.querySelector('.colorCurrent')
});
// 事件
this.events();
return this;
};
/**
* 输入框的赋值和取值,同时会更改对应的UI
* @param {String} value HEX颜色值,例如'#000000'。可缺省,表示取值
* @return {Object} 返回当前的实例对象
*/
Color.prototype.value = function (value) {
var strValue = value;
// 元素
var eleInput = this.element.input;
var eleThumb = this.element.thumb;
var eleField = this.element.field;
// 目前的颜色值
var strOldValue = eleInput.value;
// 取值还是赋值
if (typeof strValue == 'string') {
// 如果是纯字母,则认为是关键字
if (/^[a-z]{3,}$/.test(strValue)) {
document.head.style.backgroundColor = strValue;
strValue = window.getComputedStyle(document.head).backgroundColor;
document.head.style.backgroundColor = '';
}
// 使用hex值
strValue = funRgbToHex(strValue);
// 赋值
eleInput.value = strValue;
if (eleField) {
eleField.value = strValue.replace('#', '');
}
// 按钮上的色块值
eleThumb.style[BGCOLOR] = strValue;
// 作为常用颜色记录下来
var strCommonColors = localStorage.commonColors || '';
var arrCommonColors = strCommonColors.split(',');
// 前提颜色非纯灰色若干色值
var arrFixedColor = this.color.fixed;
if (arrFixedColor.some(function (strFixedColor) {
return funRgbToHex(strFixedColor) == strValue;
}) == false) {
// 过滤已经存在的相同颜色的色值
arrCommonColors = arrCommonColors.filter(function (strValueWithSharp) {
return strValueWithSharp && strValueWithSharp != strValue.replace('#', '');
});
// 从前面插入
arrCommonColors.unshift(strValue.replace('#', ''));
// 本地存储
localStorage.commonColors = arrCommonColors.join();
// 2个动态色值更新
var eleBasic = this.element.basic;
if (eleBasic) {
var eleAsideColors = eleBasic.querySelectorAll('aside a');
var eleBasicColorLast = eleAsideColors[eleAsideColors.length - 2];
var eleBasicColorSecond = eleAsideColors[eleAsideColors.length - 1];
eleBasicColorLast.setAttribute('data-color', arrCommonColors[0]);
eleBasicColorLast.setAttribute('aria-label', arrCommonColors[0]);
eleBasicColorLast.style[BGCOLOR] = strValue;
var strColorSecond = arrCommonColors[1] || '0ff';
eleBasicColorSecond.setAttribute('data-color', strColorSecond);
eleBasicColorSecond.setAttribute('aria-label', strColorSecond);
eleBasicColorSecond.style[BGCOLOR] = '#' + strColorSecond;
}
}
// 面板上的值,各种定位的匹配
this.match();
} else {
// 取值
// 如果默认无值,使用颜色作为色值,一般出现在初始化的时候
if (!strOldValue) {
strOldValue = defaultValue;
// 赋值
eleInput.value = strOldValue;
// 按钮上的色块值
eleThumb.style[BGCOLOR] = strOldValue;
}
return strOldValue;
}
if (strOldValue && strValue != strOldValue) {
eleInput.dispatchEvent(new CustomEvent('change', {
'bubbles': true
}));
}
return this;
};
/**
* 根据坐标位置获得hsl值
* 私有
* @return {String} [返回当前坐标对应的hex表示的颜色值]
*/
Object.defineProperty(Color.prototype, 'getValueByStyle', {
value: function () {
// 需要的元素
var eleCircle = this.element.circle;
var eleArrow = this.element.arrow;
if (eleCircle.length * eleArrow.length == 0) {
return defaultValue;
}
var numColorH, numColorS, numColorL;
// get color
// hsl color
if (eleCircle.style.left) {
numColorH = parseFloat(window.getComputedStyle(eleCircle).left) / eleCircle.parentElement.clientWidth;
} else {
numColorH = 0;
}
if (eleCircle.style.top) {
numColorS = 1 - parseFloat(window.getComputedStyle(eleCircle).top) / eleCircle.parentElement.clientHeight;
} else {
numColorS = 1;
}
if (eleArrow.style.top) {
numColorL = 1 - parseFloat(window.getComputedStyle(eleArrow).top) / eleArrow.parentElement.clientHeight;
} else {
numColorL = 0;
}
return '#' + funHslToHex(numColorH, numColorS, numColorL);
}
});
/**
* 面板的色块啊,圆和尖角位置匹配
* @param {String} value 面板UI相匹配的色值,可缺省,表示使用当前输入框的颜色值进行UI变化
* @return {Object} 返回当前实例对象
*/
Color.prototype.match = function (value) {
// 首先要面板显示
if (this.display != true) {
return this;
}
// 元素对象
var objElement = this.element;
// 元素
var eleContainer = objElement.container;
var eleCurrent = objElement.current;
// 更多元素
var eleMore = objElement.more;
// 元素
var eleCircle = objElement.circle;
var eleFill = objElement.fill;
var eleArrow = objElement.arrow;
// 面板内部唯一的输入框元素
var eleField = objElement.field;
// 重定位
var isRePosition = true;
if (value === false) {
isRePosition = value;
}
// 当前的颜色值
var strValue = value || eleField.value;
if (strValue == '') {
// 如果输入框没有值
// 使用之前一个合法的颜色值作为现在值
strValue = funRgbToHex(window.getComputedStyle(eleCurrent)[BGCOLOR]).replace('#', '');
eleField.value = strValue;
}
strValue = strValue.replace('#', '');
// 色块值示意
eleCurrent.style[BGCOLOR] = '#' + strValue;
// 当前是基本色面板还是任意色面板
if (window.getComputedStyle(eleMore).display == 'none') {
// 1. 基本色
// 所有当前高亮的元素不高亮
var eleActive = eleContainer.querySelector('.' + ACTIVE);
if (eleActive) {
eleActive.classList.remove(ACTIVE);
}
// 所有颜色一致的高亮
var eleColorMatch = eleContainer.querySelector('a[data-color="' + strValue.toUpperCase() + '"]');
if (eleColorMatch) {
eleColorMatch.classList.add(ACTIVE);
}
} else {
// to HSL
var arrHSL = funHexToHsl(strValue);
// hsl value
var numColorH = arrHSL[0];
var numColorS = arrHSL[1];
var numColorL = arrHSL[2];
// 滑块和尖角的颜色和位置
var strHsl = 'hsl(' + [360 * numColorH, 100 * numColorS + '%', '50%'].join() + ')';
// 如果不是默认黑色也不是纯白色
if (strValue != '000000' && strValue != 'ffffff') {
eleCircle.style[BGCOLOR] = strHsl;
eleFill.style[BGCOLOR] = strHsl;
}
if (isRePosition == true) {
if (numColorL != 0 && numColorL != 1) {
eleCircle.style.left = eleCircle.parentElement.clientWidth * numColorH + 'px';
eleCircle.style.top = eleCircle.parentElement.clientHeight * (1 - numColorS) + 'px';
}
eleArrow.style.top = eleArrow.parentElement.clientHeight * (1 - numColorL) + 'px';
}
}
return this;
};
/**
* 颜色面板显示
* @return {Object} 返回当前实例对象
*/
Color.prototype.show = function () {
// 元素
var eleContainer = this.element.container;
// 输入框赋值
if (eleContainer.innerHTML.trim() == '') {
this.create();
}
// 面板显示
if (this.drop.display == false) {
this.drop.show();
}
this.display = this.drop.display;
// 面板UI匹配
var eleCurrent = this.element.current;
if (!eleCurrent.getAttribute('style')) {
eleCurrent.style[BGCOLOR] = this.element.input.value;
}
this.match();
// onShow callback
if (typeof this.callback.show == 'function') {
this.callback.show.call(this, this.element.track, eleContainer);
}
return this;
};
/**
* 颜色面板隐藏
* @return {Object} 返回当前实例对象
*/
Color.prototype.hide = function () {
// 面板隐藏
if (this.drop.display == true) {
this.drop.hide();
}
this.display = this.drop.display;
this.element.input.focus();
// onShow callback
if (typeof this.callback.hide == 'function') {
this.callback.hide.call(this, this.element.trigger, this.element.container);
}
return this;
};
var funAutoInitAndWatching = function () {
// 如果没有开启自动初始化,则返回
if (window.autoInit === false) {
return;
}
// 遍历页面上的range元素
var strSelector = 'input[type="color"]';
var funSyncRefresh = function (nodes, action) {
if (!nodes) {
return;
}
if (!nodes.forEach) {
if (nodes.matches && nodes.matches(strSelector)) {
nodes = [nodes];
} else if (nodes.querySelector) {
nodes = nodes.querySelectorAll(strSelector);
}
}
if (!nodes.length || !nodes.forEach) {
return;
}
nodes.forEach(function (node) {
if (node.matches && node.matches(strSelector)) {
if (action == 'remove' && node.data && node.data.color) {
node.data.color.element.track.remove();
node.data.color.element.container.remove();
} else if (!node.data || !node.data.color) {
new Color(node);
}
}
});
};
funSyncRefresh(document.querySelectorAll(strSelector));
// 如果没有开启观察,不监听DOM变化
if (window.watching === false) {
return;
}
// DOM Insert自动初始化
// IE11+使用MutationObserver
// IE9-IE10使用Mutation Events
if (window.MutationObserver) {
var observerSelect = new MutationObserver(function (mutationsList) {
mutationsList.forEach(function (mutation) {
var nodeAdded = mutation.addedNodes;
var nodeRemoved = mutation.removedNodes;
if (nodeAdded.length) {
nodeAdded.forEach(function (eleAdd) {
funSyncRefresh(eleAdd, 'add');
});
}
if (nodeRemoved.length) {
nodeRemoved.forEach(function (eleRemove) {
funSyncRefresh.call(mutation, eleRemove, 'remove');
});
}
});
});
observerSelect.observe(document.body, {
childList: true,
subtree: true
});
} else {
document.body.addEventListener('DOMNodeInserted', function (event) {
// 插入节点
funSyncRefresh(event.target, 'add');
});
document.body.addEventListener('DOMNodeRemoved', function (event) {
// 删除节点
// 这里方法执行的时候,元素还在页面上
funSyncRefresh(event.target, 'remove');
});
}
};
// 监听-免初始绑定
if (document.readyState != 'loading') {
funAutoInitAndWatching();
} else {
window.addEventListener('DOMContentLoaded', funAutoInitAndWatching);
}
// 颜色转换静态方法暴露
Color.funHexToHsl = funHexToHsl;
Color.funHslToHex = funHslToHex;
Color.funRgbToHex = funRgbToHex;
return Color;
}));