从零开始实现一个颜色选择器(原生JavaScript实现)

本文详细介绍了如何从零开始使用原生JavaScript实现一个颜色选择器,包括样式布局、工具方法、动画封装、事件处理等关键步骤。通过创建和管理工具方法,如addClass、removeClass等,实现颜色选择器的功能。此外,还涉及到了透明度和颜色值的处理,以及点击事件和拖拽事件的逻辑。文章最后提到了禁用逻辑和输入框的处理,确保了颜色选择器的完整性和交互性。
摘要由CSDN通过智能技术生成

.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));

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值