.ew-pre-define-color:nth-child(10n + 1) {
margin-left: 0;
}
.ew-pre-define-color.ew-has-alpha {
background: url(“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==”);
}
.ew-pre-define-color.ew-pre-define-color-disabled {
cursor: not-allowed;
}
样式和布局就到此结束了,接下来才是我们的重点,也就是实现颜色选择器的功能。
JavaScript
==========
工具方法
首先用一个空对象来管理工具方法。如下:
const util = Object.create(null);
然后有如下方法:
const util = Object.create(null);
const _toString = Object.prototype.toString;
let addMethod = (instance, method, func) => {
instance.prototype[method] = func;
return instance;
};
[“Number”, “String”, “Function”, “Undefined”, “Boolean”].forEach(
(type) => (util[“is” + type] = (value) => typeof value === type.toLowerCase())
);
util.addMethod = addMethod;
[“Object”, “Array”, “RegExp”].forEach(
(type) =>
(util[“isDeep” + type] = (value) =>
_toString.call(value).slice(8, -1).toLowerCase() === type.toLowerCase())
);
util.isShallowObject = (value) =>
typeof value === “object” && !util.isNull(value);
util[“ewObjToArray”] = (value) =>
util.isShallowObject(value) ? Array.prototype.slice.call(value) : value;
util.isNull = (value) => value === null;
util.ewAssign = function (target) {
if (util.isNull(target)) return;
const _ = Object(target);
for (let j = 1, len = arguments.length; j < len; j += 1) {
const source = arguments[j];
if (source) {
for (let key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
_[key] = source[key];
}
}
}
}
return _;
};
util.addClass = (el, className) => el.classList.add(className);
util.removeClass = (el, className) => el.classList.remove(className);
util.hasClass = (el, className) => {
let _hasClass = (value) =>
new RegExp(" " + el.className + " “).test(” " + value + " ");
if (util.isDeepArray(className)) {
return className.some((name) => _hasClass(name));
} else {
return _hasClass(className);
}
};
util[“setCss”] = (el, prop, value) => el.style.setProperty(prop, value);
util.setSomeCss = (el, propValue = []) => {
if (propValue.length) {
propValue.forEach(§ => util.setCss(el, p.prop, p.value));
}
};
util.isDom = (el) =>
util.isShallowObject(HTMLElement)
? el instanceof HTMLElement
: (el &&
util.isShallowObject(el) &&
el.nodeType === 1 &&
util.isString(el.nodeName)) ||
el instanceof HTMLCollection ||
el instanceof NodeList;
util.ewError = (value) =>
console.error(“[ewColorPicker warn]\n” + new Error(value));
util.ewWarn = (value) => console.warn(“[ewColorPicker warn]\n” + value);
util.deepCloneObjByJSON = (obj) => JSON.parse(JSON.stringify(obj));
util.deepCloneObjByRecursion = function f(obj) {
if (!util.isShallowObject(obj)) return;
let cloneObj = util.isDeepArray(obj) ? [] : {};
for (let k in obj) {
cloneObj[k] = util.isShallowObject(obj[k]) ? f(obj[k]) : obj[k];
}
return cloneObj;
};
util.getCss = (el, prop) => window.getComputedStyle(el, null)[prop];
util.$ = (ident) => {
if (!ident) return null;
return document[
ident.indexOf(“#”) > -1 ? “querySelector” : “querySelectorAll”
](ident);
};
util[“on”] = (element, type, handler, useCapture = false) => {
if (element && type && handler) {
element.addEventListener(type, handler, useCapture);
}
};
util[“off”] = (element, type, handler, useCapture = false) => {
if (element && type && handler) {
element.removeEventListener(type, handler, useCapture);
}
};
util[“getRect”] = (el) => el.getBoundingClientRect();
util[“baseClickOutSide”] = (element, isUnbind = true, callback) => {
const mouseHandler = (event) => {
const rect = util.getRect(element);
const target = event.target;
if (!target) return;
const targetRect = util.getRect(target);
if (
targetRect.x >= rect.x &&
targetRect.y >= rect.y &&
targetRect.width <= rect.width &&
targetRect.height <= rect.height
)
return;
if (util.isFunction(callback)) callback();
if (isUnbind) {
// 延迟解除绑定
setTimeout(() => {
util.off(document, util.eventType[0], mouseHandler);
}, 0);
}
};
util.on(document, util.eventType[0], mouseHandler);
};
util[“clickOutSide”] = (context, config, callback) => {
const mouseHandler = (event) => {
const rect = util.getRect(context.$Dom.picker);
let boxRect = null;
if (config.hasBox) {
boxRect = util.getRect(context.$Dom.box);
}
const target = event.target;
if (!target) return;
const targetRect = util.getRect(target);
// 利用rect来判断用户点击的地方是否在颜色选择器面板区域之内
if (config.hasBox) {
if (
targetRect.x >= rect.x &&
targetRect.y >= rect.y &&
targetRect.width <= rect.width
)
return;
// 如果点击的是盒子元素
if (
targetRect.x >= boxRect.x &&
targetRect.y >= boxRect.y &&
targetRect.width <= boxRect.width &&
targetRect.height <= boxRect.height
)
return;
callback();
} else {
if (
targetRect.x >= rect.x &&
targetRect.y >= rect.y &&
targetRect.width <= rect.width &&
targetRect.height <= rect.height
)
return;
callback();
}
setTimeout(() => {
util.off(document, util.eventType[0], mouseHandler);
}, 0);
};
util.on(document, util.eventType[0], mouseHandler);
};
util[“createUUID”] = () =>
(Math.random() * 10000000).toString(16).substr(0, 4) +
“-” +
new Date().getTime() +
“-” +
Math.random().toString().substr(2, 5);
util.removeAllSpace = (value) => value.replace(/\s+/g, “”);
util.isJQDom = (dom) =>
typeof window.jQuery !== “undefined” && dom instanceof jQuery;
//the event
util.eventType = navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i)
? [“touchstart”, “touchmove”, “touchend”]
: [“mousedown”, “mousemove”, “mouseup”];
动画函数的封装
const animation = {};
function TimerManager() {
this.timers = [];
this.args = [];
this.isTimerRun = false;
}
TimerManager.makeTimerManage = function (element) {
const elementTimerManage = element.TimerManage;
if (!elementTimerManage || elementTimerManage.constructor !== TimerManager) {
element.TimerManage = new TimerManager();
}
};
const methods = [
{
method: “add”,
func: function (timer, args) {
this.timers.push(timer);
this.args.push(args);
this.timerRun();
},
},
{
method: “timerRun”,
func: function () {
if (!this.isTimerRun) {
let timer = this.timers.shift(),
args = this.args.shift();
if (timer && args) {
this.isTimerRun = true;
timer(args[0], args[1]);
}
}
},
},
{
method: “next”,
func: function () {
this.isTimerRun = false;
this.timerRun();
},
},
];
methods.forEach((method) =>
util.addMethod(TimerManager, method.method, method.func)
);
function runNext(element) {
const elementTimerManage = element.TimerManage;
if (elementTimerManage && elementTimerManage.constructor === TimerManager) {
elementTimerManage.next();
}
}
function registerMethods(type, element, time) {
let transition = “”;
if (type.indexOf(“slide”) > -1) {
transition = “height” + time + " ms";
util.setCss(element, “overflow”, “hidden”);
upAndDown();
} else {
transition = “opacity” + time + " ms";
inAndOut();
}
util.setCss(element, “transition”, transition);
function upAndDown() {
const isDown = type.toLowerCase().indexOf(“down”) > -1;
if (isDown) util.setCss(element, “display”, “block”);
const getPropValue = function (item, prop) {
let v = util.getCss(item, prop);
return util.removeAllSpace(v).length ? parseInt(v) : Number(v);
};
const elementChildHeight = [].reduce.call(
element.children,
(res, item) => {
res +=
item.offsetHeight +
getPropValue(item, “margin-top”) +
getPropValue(item, “margin-bottom”);
return res;
},
0
);
let totalHeight = Math.max(element.offsetHeight, elementChildHeight + 10);
let currentHeight = isDown ? 0 : totalHeight;
let unit = totalHeight / (time / 10);
if (isDown) util.setCss(element, “height”, “0px”);
let timer = setInterval(() => {
currentHeight = isDown ? currentHeight + unit : currentHeight - unit;
util.setCss(element, “height”, currentHeight + “px”);
if (currentHeight >= totalHeight || currentHeight <= 0) {
clearInterval(timer);
util.setCss(element, “height”, totalHeight + “px”);
runNext(element);
}
if (!isDown && currentHeight <= 0) {
util.setCss(element, “display”, “none”);
util.setCss(element, “height”, “0”);
}
}, 10);
}
function inAndOut() {
const isIn = type.toLowerCase().indexOf(“in”) > -1;
let timer = null;
let unit = (1 * 100) / (time / 10);
let curAlpha = isIn ? 0 : 100;
util.setSomeCss(element, [
{
prop: “display”,
value: isIn ? “none” : “block”,
},
{
prop: “opacity”,
value: isIn ? 0 : 1,
},
]);
let handleFade = function () {
curAlpha = isIn ? curAlpha + unit : curAlpha - unit;
if (element.style.display === “none” && isIn)
util.setCss(element, “display”, “block”);
util.setCss(element, “opacity”, (curAlpha / 100).toFixed(2));
if (curAlpha >= 100 || curAlpha <= 0) {
if (timer) clearTimeout(timer);
runNext(element);
if (curAlpha <= 0) util.setCss(element, “display”, “none”);
util.setCss(element, “opacity”, curAlpha >= 100 ? 1 : 0);
} else {
timer = setTimeout(handleFade, 10);
}
};
handleFade();
}
}
[“slideUp”, “slideDown”, “fadeIn”, “fadeOut”].forEach((method) => {
animation[method] = function (element) {
TimerManager.makeTimerManage(element);
element.TimerManage.add(function (element, time) {
return registerMethods(method, element, time);
}, arguments);
};
});
一些颜色操作的算法
const colorRegExp = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
// RGB color
const colorRegRGB =
/[rR][gG][Bb][Aa]?({2}[\s](2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?),?[\s](0.\d{1,2}|1|0)?[)]{1}/g;
// RGBA color
const colorRegRGBA =
/1[gG][Bb][Aa]({3}[\s](1|1.0|0|0?.[0-9]{1,2})[\s][)]{1}$/;
// hsl color
const colorRegHSL =
/2[Ss][Ll](([\s]((100|[0-9][0-9]?)%|0)[\s],)([\s]((100|[0-9][0-9]?)%|0)[\s])[)]$/;
// HSLA color
const colorRegHSLA =
/3[Ss][Ll][Aa](([\s]((100|[0-9][0-9]?)%|0)[\s],){2}([\s](1|1.0|0|0?.[0-9]{1,2})[\s])[)]$/;
/**
* hex to rgba
* @param {*} hex
* @param {*} alpha
*/
function colorHexToRgba(hex, alpha) {
let a = alpha || 1,
hColor = hex.toLowerCase(),
hLen = hex.length,
rgbaColor = [];
if (hex && colorRegExp.test(hColor)) {
//the hex length may be 4 or 7,contained the symbol of #
if (hLen === 4) {
let hSixColor = “#”;
for (let i = 1; i < hLen; i++) {
let sColor = hColor.slice(i, i + 1);
hSixColor += sColor.concat(sColor);
}
hColor = hSixColor;
}
for (let j = 1, len = hColor.length; j < len; j += 2) {
rgbaColor.push(parseInt(“0X” + hColor.slice(j, j + 2), 16));
}