input要素で使用可能なカラーピッカーです。
グラデーションはcanvasは使わず、CSSのconic-gradient()とlinear-gradient()のみで表示しています。
####各部機能####
1 彩度/明度ピッカー
2 色相ピッカー
3 リサイズアイコン
4 RGBスライダー
5 履歴パレット
ピッカーやスライダーの操作を行うごとに自動更新。クリックすることでカレントカラー(対象要素へのフィードバック色)へ反映されます。
6 グラデーションパレット
左右はグラデーションの開始色、終了色をセットするボタンになっていて、クリックすることでボタンにカレントカラーがセットされます。
中間の3段あるグラデーションは開始色~終了色のグラデーションで、上からカラーコード、色相(右回り)、色相(左回り)で補間しています。グラデーションの任意の場所を選択することでその位置の色がカレントカラーへ反映されます。
####script####
'use strict';
{
const
obj = {
color: {h: 0, s: 0, v: 1},
},
eventListenerManage = (function() {
const events = {};
let key = 1;
return {
add: function(target, type, listener, capture) {
target.addEventListener(type, listener, capture);
events[key] = {target, type, listener, capture};
return key++;
},
remove: function(key) {
if(key in events) {
const e = events[key];
e.target.removeEventListener(e.type, e.listener, e.capture);
delete(events[key]);
}
}
};
}()),
touchFlg = window.ontouchstart !== undefined ? true : false,
pre = 'colorPicker-';
window.addEventListener('DOMContentLoaded', function() {
// insert picker elements
document.querySelector('body').insertAdjacentHTML('beforeend', `
<div class='${pre}container'>
<div class='${pre}back'></div>
<div class='${pre}frame'></div>
<div class='${pre}move'></div>
<div class='${pre}circle'></div>
<div class='${pre}innerCircle'>
<div class='${pre}pointer'></div>
</div>
<div class='${pre}circleInput'></div>
<div class='${pre}rect'>
<div class='${pre}pointer'>
<div class='${pre}circle1'></div>
<div class='${pre}circle2'></div>
</div>
</div>
<div class='${pre}rectInput'></div>
<div class='${pre}resize'></div>
<input type='${touchFlg ? 'checkbox' : 'text'}' class='${pre}d'>
<div class='${pre}rgbUnit'>
<div class='${pre}rSlider'></div>
<div class='${pre}gSlider'></div>
<div class='${pre}bSlider'></div>
</div>
<div class='${pre}historyPalette'></div>
<div class='${pre}interpolationGradation'>
<div class='${pre}input'></div>
<div class='${pre}left'></div>
<div class='${pre}center'>
<div class='${pre}rgb'></div>
<div class='${pre}hsv0'></div>
<div class='${pre}hsv1'></div>
<div class='${pre}pointer'>
<div class='${pre}shape'></div>
</div>
</div>
<div class='${pre}right'></div>
</div>
</div>
`);
// elements style set
let s = (obj.picker = document.querySelector(`div.${pre}container`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '200px';
s.zIndex = '10000';
s.userSelect = s.WebkitUserSelect = 'none';
// hue ring
s = (obj.circle = obj.picker.querySelector(`.${pre}circle`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '100%';
s.borderRadius = '50%';
const g = [];
for(let i = 0; i <= 360; i += 60) g.push(`hsl(${i}deg 100% 50%)`);
s.maskImage = s.WebkitMaskImage = `radial-gradient(transparent 56%, #000 56.5%)`;
s.backgroundImage = `conic-gradient(${g.join(',')})`;
s = (obj.circleInput = obj.picker.querySelector(`.${pre}circleInput`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '104%';
s.top = s.left= '-2%';
s.borderRadius = '50%';
s = (obj.innerCircle = obj.picker.querySelector(`.${pre}innerCircle`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '80%';
s.top = s.left = '10%';
s.backgroundColor = '#fff6';
s.borderRadius = '50%';
s = (obj.circlePointer = obj.innerCircle.querySelector(`.${pre}pointer`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = '6%';
s.height = '10%';
s.top = '-11.4%';
s.left = '47%';
s.border = '2px #000 solid';
s.borderRadius = '20%';
// color rectangle
s = (obj.rect = obj.picker.querySelector(`.${pre}rect`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = (75 / Math.sqrt(2)) + '%';
s.top = s.left = (50 - (75 / Math.sqrt(2) / 2)) + '%';
s.backgroundColor = '#f00';
s.backgroundImage = 'linear-gradient(to top, #000, #0000), linear-gradient(to left, #fff0, #fff)';
s = (obj.rectInput = obj.picker.querySelector(`.${pre}rectInput`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = (80 / Math.sqrt(2)) + '%';
s.top = s.left = (50 - (80 / Math.sqrt(2) / 2)) + '%';
s = (obj.rectPointer = obj.rect.querySelector(`.${pre}pointer`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '12%';
s.top = s.left = '0';
s = (obj.rectPointerCircle1 = obj.rectPointer.querySelector(`.${pre}circle1`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.top = s.left = '-50%';
s.width = s.height = '100%';
s.border = '1px #fff solid';
s.borderRadius = '50%';
s = (obj.rectPointerCircle2 = obj.rectPointer.querySelector(`.${pre}circle2`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.top = s.left = (-50 + (100 - 80) / 2) + '%';
s.width = s.height = '80%';
s.backgroundColor = '#fff';
s.borderRadius = '50%';
s.border = '1px #000 solid';
// resize icon
s = (obj.resize = obj.picker.querySelector(`.${pre}resize`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.borderRight = s.borderBottom = '1px #888 solid';
s.width = s.height = '12%';
s.right = s.bottom = '0';
s.cursor = 'nwse-resize';
// move icon
s = (obj.move = obj.picker.querySelector(`.${pre}move`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = s.height = '100%';
s.cursor = 'move';
// dummy object
s = (obj.d = obj.picker.querySelector(`.${pre}d`)).style;
s.position = 'fixed';
s.left = s.top = '-500px';
s = (obj.back = obj.picker.querySelector(`.${pre}back`)).style;
s.position = 'fixed';
s.width = s.height = '100%';
s.left = s.top = '0';
s = (obj.frame = obj.picker.querySelector(`.${pre}frame`)).style;
s.position = 'absolute';
s.width = s.height = '110%';
s.left = s.top = '-5%';
// RGB slider
s = (obj.rgbUnit = obj.picker.querySelector(`.${pre}rgbUnit`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = '100%';
s.height = '30%';
s.left = '0';
s.top = '100%';
for(let i = 0; i < 3; i++) {
const c = 'rgb'[i];
s = (obj[`${c}Slider`] = obj.picker.querySelector(`.${pre}${c}Slider`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = '90%';
s.left = '5%';
s.height = '22%';
s.zIndex = '-1';
s.userSelect = 'none';
s.paddingLeft = '2%';
s.top = `${33.33 * i + 33.33 - 22}%`;
s.borderBottom = `1px solid #${['f00', '0f0', '00f'][i]}`;
obj[`${c}Slider`].appendChild(document.createElement('div'));
s = (obj[`${c}Slider`].child = obj[`${c}Slider`].lastChild).style;
s.color = '#fff';
s.mixBlendMode = 'difference';
}
// history palette
s = (obj.historyPalette = obj.picker.querySelector(`.${pre}historyPalette`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.display = 'flex';
s.justifyContent = 'space-between';
s.width = '100%';
s.height = '16%';
s.left = '0';
obj.historyCell = [];
for(let i = 0; i < 8; i++) {
obj.historyPalette.appendChild(document.createElement('div'));
s = (obj.historyCell[i] = obj.historyPalette.lastChild).style;
s.position = 'relative';
s.boxSizing = 'border-box';
s.border = '1px #eee solid';
s.width = '100%';
s.height = '75%';
s.top = '25%';
s.backgroundColor = '#fff';
}
// gradation unit
s = (obj.gradation = obj.picker.querySelector(`.${pre}interpolationGradation`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.display = 'flex';
s.justifyContent = 'space-between';
s.width = '100%';
s.height = '35%';
s.left = '0';
s = (obj.gradationInput = obj.gradation.querySelector(`.${pre}input`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.width = '100%';
s.height = '90%';
s.top = '10%';
s = (obj.gradationLeft = obj.gradation.querySelector(`.${pre}left`)).style;
s.position = 'relative';
s.boxSizing = 'border-box';
s.width = '10%';
s.height = '90%';
s.top = '10%';
s.border = '1px #ddd solid';
s.cursor = 'pointer';
obj.gradationColorLeft = {...obj.color};
s.backgroundColor = hsv2rgb(obj.color.h, obj.color.s, obj.color.v).cc;
s = (obj.gradationCenter = obj.gradation.querySelector(`.${pre}center`)).style;
s.position = 'relative';
s.boxSizing = 'border-box';
s.width = '80%';
s.height = '90%';
s.top = '10%';
s.zIndex = '-1';
s = (obj.gradationRight = obj.gradation.querySelector(`.${pre}right`)).style;
s.position = 'relative';
s.boxSizing = 'border-box';
s.width = '10%';
s.height = '90%';
s.top = '10%';
s.border = '1px #ddd solid';
s.cursor = 'pointer';
obj.gradationColorRight = {...obj.color};
s.backgroundColor = hsv2rgb(obj.color.h, obj.color.s, obj.color.v).cc;
for(let i = 0; i < 3; i++) {
s = (obj[`gradation${['Rgb', 'Hsv0', 'Hsv1'][i]}`] =
obj.gradationCenter.querySelector(`.${pre}${['rgb', 'hsv0', 'hsv1'][i]}`)).style;
s.position = 'relative';
s.boxSizing = 'border-box';
s.width = '100%';
s.height = '33.33%';
s.border = '1px #ddd solid';
}
s = (obj.gradationPointer = obj.gradationCenter.querySelector(`.${pre}pointer`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.top = s.left = '0';
s.width = '5px';
s.height = '33.33%';
s.opacity = '0';
s = (obj.gradationPointerShape = obj.gradationPointer.querySelector(`.${pre}shape`)).style;
s.position = 'absolute';
s.boxSizing = 'border-box';
s.top = '0';
s.left = '-1px';
s.width = '3px';
s.height = '100%';
s.backgroundColor = '#fff';
s.borderLeft = s.borderRight = '1px #000 solid';
setGradation();
obj.picker.style.display = 'none';
// picker eventListener set
const ev = {
mousedown: touchFlg ? 'touchstart' : 'mousedown',
mousemove: touchFlg ? 'touchmove' : 'mousemove',
mouseup: touchFlg ? 'touchend' : 'mouseup',
};
obj.circleInput.addEventListener(ev.mousedown, function(e) {
obj.d.focus();
obj.circleInput.style.zIndex = '99';
obj.t = 'circle';
circleInput(e);
obj.rect.style.backgroundColor = `hsl(${obj.color.h}deg 100% 50%)`;
const prevColor = obj.historyCell[0].style.backgroundColor;
feedbackValue(obj.p);
historyPush(prevColor);
enabledPreventDefault();
});
obj.rectInput.addEventListener(ev.mousedown, function(e) {
obj.d.focus();
obj.t = 'rect';
rectInput(e);
const prevColor = obj.historyCell[0].style.backgroundColor;
feedbackValue(obj.p);
historyPush(prevColor);
enabledPreventDefault();
});
obj.resize.addEventListener(ev.mousedown, function() {
obj.d.focus();
obj.t = 'resize';
enabledPreventDefault();
});
obj.move.addEventListener(ev.mousedown, function(e) {
obj.d.focus();
obj.t = 'move';
obj.offsetX = touchFlg ? e.touches[0].pageX - obj.picker.offsetLeft : e.layerX;
obj.offsetY = touchFlg ? e.touches[0].pageY - obj.picker.offsetTop : e.layerY;
enabledPreventDefault();
});
obj.rgbUnit.addEventListener(ev.mousedown, function(e) {
obj.d.focus();
const
y = touchFlg ? e.touches[0].pageY - (obj.picker.offsetTop + this.offsetTop) : e.layerY,
n = Math.floor(y / this.clientHeight * 3),
prevColor = obj.historyCell[0].style.backgroundColor;
obj.t = `slider${n}`;
sliderInput(obj[`${'rgb'[n]}Slider`], e, n);
historyPush(prevColor);
enabledPreventDefault();
});
obj.rgbUnit.addEventListener(ev.mousemove, function(e) {
const m = obj.t.match(/slider(\d)$/);
if(m === null || m[1] === undefined) return;
sliderInput(obj[`${'rgb'[+m[1]]}Slider`], e, +m[1]);
});
for(let i = 0; i < 8; i++) {
obj.historyCell[i].addEventListener(ev.mousedown, function() {
obj.t = 'history';
const rgb = this.style.backgroundColor.match(/\d+/g);
if(rgb !== null) {
const hsv = rgb2hsv(+rgb[0], +rgb[1], +rgb[2]);
obj.color = {...hsv};
obj.rectPointer.style.left = Math.floor(obj.rect.clientWidth * hsv.s) + 'px';
obj.rectPointer.style.top = Math.floor(obj.rect.clientHeight * (1 - hsv.v)) + 'px';
obj.rect.style.backgroundColor = `hsl(${hsv.h}deg 100% 50%)`;
obj.innerCircle.style.transform = `rotate(${hsv.h}deg)`;
obj.circlePointer.style.borderColor = `hsl(${180 + hsv.h}, 100%, 50%)`;
feedbackValue(obj.p, true);
}
enabledPreventDefault();
});
}
obj.circleInput.addEventListener(ev.mousemove, enabledPreventDefault);
obj.rgbUnit.addEventListener(ev.mousemove, enabledPreventDefault);
obj.historyPalette.addEventListener(ev.mousemove, enabledPreventDefault);
obj.gradation.addEventListener(ev.mousemove, enabledPreventDefault);
window.addEventListener(ev.mousemove, function(e) {
if(obj.t === 'resize') {
const
e_ = touchFlg ? e.touches[0] : e,
h = e_.pageX - obj.picker.offsetLeft,
v = e_.pageY - obj.picker.offsetTop,
size = Math.max(100, Math.max(h, v));
obj.picker.style.width = obj.picker.style.height = size + 'px';
obj.rectPointer.style.left = Math.floor(obj.rect.clientWidth * obj.color.s) + 'px';
obj.rectPointer.style.top = Math.floor(obj.rect.clientHeight * (1 - obj.color.v)) + 'px';
setRgbSliderValue(true);
}
else if(obj.t === 'move') {
const
e_ = touchFlg ? e.touches[0] : e,
x = e_.pageX - obj.offsetX,
y = e_.pageY - obj.offsetY;
obj.picker.style.left = x + 'px';
obj.picker.style.top = y + 'px';
}
else if(obj.t === 'circle') {
circleInput(e);
obj.rect.style.backgroundColor = `hsl(${obj.color.h}deg 100% 50%)`;
feedbackValue(obj.p);
}
else if(obj.t === 'rect') {
rectInput(e);
feedbackValue(obj.p);
}
});
window.addEventListener(ev.mouseup, function() {
setTimeout(function() {
obj.t = '';
obj.circleInput.style.zIndex = '';
obj.gradationInput.style.zIndex = '';
obj.gradationPointer.style.opacity = '0';
if(obj.touchmoveId) {
eventListenerManage.remove(obj.touchmoveId);
obj.touchmoveId = 0;
}
});
if(obj.t !== undefined && obj.t !== '' || obj.windowScroll === 1) return;
obj.picker.style.display = 'none';
if(obj.p !== undefined) {
delete obj.p;
for(const e of elements) e.dataset.colorpickerDisplay = 0;
}
});
window.addEventListener(ev.mousedown, function() {
obj.windowScroll = 0;
});
window.addEventListener('touchmove', function() {
obj.windowScroll = 1;
});
obj.historyPalette.addEventListener(ev.mousedown, function() {
obj.t = 'historyPalette';
});
obj.gradation.addEventListener(ev.mousedown, function() {
obj.t = 'gradation';
});
obj.gradationLeft.addEventListener(ev.mousedown, function() {
obj.d.focus();
obj.t = 'gradationLeft';
enabledPreventDefault();
const c = obj.color;
this.style.backgroundColor = hsv2rgb(c.h, c.s, c.v).cc;
obj.gradationColorLeft = {...c};
setGradation();
});
obj.gradationRight.addEventListener(ev.mousedown, function() {
obj.d.focus();
obj.t = 'gradationRight';
enabledPreventDefault();
const c = obj.color;
this.style.backgroundColor = hsv2rgb(c.h, c.s, c.v).cc;
obj.gradationColorRight = {...c};
setGradation();
});
obj.gradationInput.addEventListener(ev.mousedown, function(e) {
obj.d.focus();
obj.t = 'gradation';
enabledPreventDefault();
obj.gradationInput.style.zIndex = '99';
const y = touchFlg ?
e.touches[0].pageY - (obj.picker.offsetTop + obj.gradation.offsetTop + this.offsetTop) : e.layerY;
obj.gradationSelectNumber = Math.min(Math.floor(y / this.clientHeight * 3), 2);
obj.gradationPointer.style.opacity = '1';
obj.gradationPointer.style.top = (obj.gradationSelectNumber * this.clientHeight / 3) + 'px';
const centor = obj.gradationCenter;
const prevColor = obj.historyCell[0].style.backgroundColor;
obj.historyCell[0].style.backgroundColor = '';
historyPush(prevColor);
pickupGradationColor(e);
});
obj.gradationInput.addEventListener(ev.mousemove, function(e) {
if(obj.t === '') return;
pickupGradationColor(e);
});
// target elements eventListener set
const elements = document.querySelectorAll('input[data-colorpicker]');
let i = 1;
for(const e of elements) {
e.dataset.colorpickerId = i++;
e.addEventListener('click', function() {
if(e.dataset.colorpickerDisplay & 1) {
obj.picker.style.display = 'none';
e.dataset.colorpickerDisplay = 0;
}
else {
const d = e.dataset.colorpicker.toLowerCase();
const size = d.match(/size[^:]*:\s*([.\d]+)/);
obj.picker.style.width = obj.picker.style.height =
(size !== null ? Math.max(100, size[1]) : 200) + 'px';
displayPicker(e);
let t = [];
t = d.match(/rgb-slider-disabled[^:]*:\s*([\w]+)/);
obj.rgbUnit.style.display =
(t !== null && (+t[1] || t[1] === 'true')) ? 'none' : 'block';
t = d.match(/history-palette-disabled[^:]*:\s*([\w]+)/);
obj.historyPalette.style.display =
(t !== null && (+t[1] || t[1] === 'true')) ? 'none' : 'flex';
t = d.match(/gradation-palette-disabled[^:]*:\s*([\w]+)/);
obj.gradation.style.display =
(t !== null && (+t[1] || t[1] === 'true')) ? 'none' : 'flex';
obj.historyPalette.style.top =
(100 + obj.rgbUnit.clientHeight / obj.picker.clientHeight * 100) + '%';
obj.gradation.style.top =
(100 + (obj.rgbUnit.clientHeight + obj.historyPalette.clientHeight) / obj.picker.clientHeight * 100) + '%';
obj.frame.style.backgroundColor = '#fffc';
obj.frame.style.zIndex = '-1';
obj.frame.style.borderRadius = '5px';
obj.frame.style.boxShadow = '0 0 4px 4px #0001';
obj.frame.style.height = (10 + (obj.picker.clientHeight + obj.rgbUnit.clientHeight +
obj.historyPalette.clientHeight + obj.gradation.clientHeight) / obj.picker.clientHeight * 100) + '%';
obj.rectPointer.style.left = obj.rectPointer.style.top = '0';
obj.rect.style.backgroundColor = '#f00';
obj.innerCircle.style.transform = 'rotate(0deg)';
obj.rectPointerCircle2.style.backgroundColor = '#fff';
obj.d.style.color = e.value;
const rgb = obj.d.style.color.match(/\d+/g);
if(rgb !== null) {
rgb.splice(3);
const hsv = rgb2hsv(+rgb[0], +rgb[1], +rgb[2]);
obj.innerCircle.style.transform = `rotate(${hsv.h}deg)`;
obj.rectPointer.style.left = Math.floor(obj.rect.clientWidth * hsv.s) + 'px';
obj.rectPointer.style.top = Math.floor(obj.rect.clientHeight * (1 - hsv.v)) + 'px';
obj.rect.style.backgroundColor = `hsl(${hsv.h}deg 100% 50%)`;
obj.color = {...hsv};
obj.circlePointer.style.borderColor = `hsl(${180 + hsv.h}, 100%, 50%)`;
obj.rectPointerCircle2.style.backgroundColor = `rgb(${rgb.join(',')})`;
}
e.dataset.colorpickerDisplay = 1;
if(obj.p !== undefined) {
if(e.dataset.colorpickerId !== obj.p.dataset.colorpickerId)
obj.p.dataset.colorpickerDisplay = 0;
}
setRgbSliderValue(true);
historyPush();
obj.p = e;
disabledDefaultPicker(this);
}
});
}
});
// functions
function hsv2rgb(h, s, v) {
const
a = h / 60,
[r, g, b] = [5, 3, 1].map(
i => Math.round(
(v - Math.max(0, Math.min(1, 2 - Math.abs(2 - (a + i) % 6))) * s * v) * 255
));
return {cc: '#' + (r << 16 | g << 8 | b).toString(16).padStart(6, '0'), rgb: [r, g, b], r, g, b};
}
function rgb2hsv(r, g, b) {
const
v = Math.max(r, g, b), d = v - Math.min(r, g, b),
s = v ? d / v : 0, a = [r, g, b, r, g], i = a.indexOf(v),
h = s ? (((a[i + 1] - a[i + 2]) / d + i * 2 + 6) % 6) * 60 : 0;
return {h, s, v: v / 255};
}
function displayPicker(e) {
obj.picker.style.display = 'block';
const x = e.offsetLeft + obj.picker.clientWidth <= window.innerWidth ||
window.innerWidth < obj.picker.clientWidth ? e.offsetLeft : window.innerWidth - obj.picker.clientWidth;
obj.picker.style.left = 8 + x + 'px';
obj.picker.style.top = (16 + e.offsetTop + e.clientHeight) + 'px';
obj.color = {h: 0, s: 0, v: 1};
obj.circlePointer.style.borderColor = `hsl(${180}, 100%, 50%)`;
setTimeout(function() { obj.d.focus();});
}
function circleInput(e) {
const
h = obj.circleInput.clientWidth,
v = obj.circleInput.clientHeight,
e_ = touchFlg ? e.touches[0] : e,
x = e_.pageX - obj.picker.offsetLeft - obj.circleInput.offsetLeft,
y = e_.pageY - obj.picker.offsetTop - obj.circleInput.offsetTop,
at = Math.atan2(v / 2 - y , h / 2 - x),
k = (at * 180 / Math.PI + 270) % 360;
obj.innerCircle.style.transform = `rotate(${k}deg)`;
obj.color.h = k;
obj.circlePointer.style.borderColor = `hsl(${180 + k}, 100%, 50%)`;
}
function rectInput(e) {
const
h = obj.rect.clientWidth,
v = obj.rect.clientHeight,
e_ = touchFlg ? e.touches[0] : e,
x_ = (e_.pageX - obj.rectInput.offsetLeft - obj.picker.offsetLeft) - (obj.rectInput.clientWidth - h) / 2,
y_ = (e_.pageY - obj.rectInput.offsetTop - obj.picker.offsetTop) - (obj.rectInput.clientHeight - v) / 2,
x = x_ < 0 ? 0 : x_ > h ? h : x_,
y = y_ < 0 ? 0 : y_ > v ? v : y_;
obj.rectPointer.style.left = x + 'px';
obj.rectPointer.style.top = y + 'px';
obj.color.s = x / h;
obj.color.v = 1 - y / v;
}
function sliderInput(o, e, ch) {
const
h = o.clientWidth,
x = (touchFlg ? e.touches[0].pageX - obj.picker.offsetLeft : e.layerX) - o.offsetLeft,
c = obj.color,
rgb = hsv2rgb(c.h, c.s, c.v).rgb;
rgb[ch] = (x < 0 ? 0 : x > h ? h : x) / h * 255;
const hsv = rgb2hsv(rgb[0], rgb[1], rgb[2]);
obj.color = {...hsv};
obj.rectPointer.style.left = Math.floor(obj.rect.clientWidth * hsv.s) + 'px';
obj.rectPointer.style.top = Math.floor(obj.rect.clientHeight * (1 - hsv.v)) + 'px';
obj.rect.style.backgroundColor = `hsl(${hsv.h}deg 100% 50%)`;
obj.innerCircle.style.transform = `rotate(${hsv.h}deg)`;
obj.circlePointer.style.borderColor = `hsl(${180 + hsv.h}, 100%, 50%)`;
setRgbSliderValue();
feedbackValue(obj.p);
}
function feedbackValue(e, notUpdateHistory) {
const c = obj.color;
e.value = hsv2rgb(c.h, c.s, c.v).cc;
if(e.type !== 'color') {
e.style.backgroundColor = e.value;
const rgb = e.style.backgroundColor.match(/\d+/g);
if(rgb !== null) {
const l = (rgb[0] * 2 + rgb[1] * 4 + +rgb[2]) / 3;
e.style.color = l > 300 ? '#000' : '#fff';
}
}
obj.rectPointerCircle2.style.backgroundColor = e.value;
setRgbSliderValue();
if(notUpdateHistory === undefined && notUpdateHistory !== true)
obj.historyCell[0].style.backgroundColor = obj.historyCell[0].dataset.colorCode = e.value;
}
function setRgbSliderValue(f) {
const
c = obj.color,
rgb = hsv2rgb(c.h, c.s, c.v).rgb,
fontsize = f === true ? (obj.rgbUnit.clientWidth / 20) + 'px' : '';
for(let i = 0; i < 3; i++) {
const ch = [0, 0, 0];
ch[i] = rgb[i];
obj[`${'rgb'[i]}Slider`].style.backgroundImage =
`linear-gradient(to right, #000, rgb(${ch.join(',')}) ${rgb[i] / 2.55}%, #fff8 0%)`;
obj[`${'rgb'[i]}Slider`].child.textContent = rgb[i];
if(fontsize) obj[`${'rgb'[i]}Slider`].child.style.fontSize = fontsize;
}
}
function historyPush(prevColor) {
const
c = obj.color,
currentColorcode = hsv2rgb(c.h, c.s, c.v).cc;
if(obj.historyCell[0].dataset.colorCode !== undefined &&
obj.historyCell[0].dataset.colorCode === currentColorcode && prevColor === undefined) return;
if(prevColor !== undefined && prevColor === obj.historyCell[0].style.backgroundColor) return;
for(let i = 0; i < 7; i++)
obj.historyCell[7 - i].style.backgroundColor = obj.historyCell[6 - i].style.backgroundColor;
if(prevColor !== undefined) obj.historyCell[1].style.backgroundColor = prevColor;
obj.historyCell[0].style.backgroundColor = obj.historyCell[0].dataset.colorCode = currentColorcode;
}
function enabledPreventDefault() {
if(obj.touchmoveId) return;
obj.touchmoveId = eventListenerManage.add(obj.picker, 'touchmove', function(e) {
e.preventDefault();
});
}
function setGradation() {
const
c0 = obj.gradationColorLeft,
c1 = obj.gradationColorRight,
hR = getHuePosition(c0.h, c1.h),
hL = getHuePosition(c0.h, c1.h, 1),
rgb0 = hsv2rgb(c0.h, c0.s, c0.v).rgb,
rgb1 = hsv2rgb(c1.h, c1.s, c1.v).rgb,
n = 12, g0 = [], g1 = [], g2 = [];
for(let i = 0; i < n; i++) {
const
is = c0.s + (c1.s - c0.s) / n * i,
iv = c0.v + (c1.v - c0.v) / n * i;
g1.push(hsv2rgb(hR[0] + (hR[1] - hR[0]) / n * i, is, iv).cc);
g2.push(hsv2rgb(hL[0] + (hL[1] - hL[0]) / n * i, is, iv).cc);
}
g0.push(`rgb(${rgb0.join(',')})`);
const rightColor = `rgb(${rgb1.join(',')})`;
g0.push(rightColor);
g1.push(rightColor);
g2.push(rightColor);
obj.gradationRgb.style.backgroundImage = `linear-gradient(to right, ${g0.join(',')})`;
obj.gradationHsv0.style.backgroundImage = `linear-gradient(to right, ${g1.join(',')})`;
obj.gradationHsv1.style.backgroundImage = `linear-gradient(to right, ${g2.join(',')})`;
}
function pickupGradationColor(e) {
const
centor = obj.gradationCenter,
n = obj.gradationSelectNumber,
x = touchFlg ?
e.touches[0].pageX - (obj.picker.offsetLeft + obj.gradationCenter.offsetLeft) :
e.layerX - centor.offsetLeft,
value = Math.max(0, Math.min(1, x / centor.clientWidth)),
c0 = obj.gradationColorLeft,
c1 = obj.gradationColorRight,
rgb0 = hsv2rgb(c0.h, c0.s, c0.v).rgb,
rgb1 = hsv2rgb(c1.h, c1.s, c1.v).rgb;
obj.gradationPointer.style.left = (value * centor.clientWidth) + 'px';
const hsv = {h: 0, s: 0, v: 1};
if(n === 0) {
const c = rgb2hsv(
rgb0[0] + (rgb1[0] - rgb0[0]) * value,
rgb0[1] + (rgb1[1] - rgb0[1]) * value,
rgb0[2] + (rgb1[2] - rgb0[2]) * value);
[hsv.h, hsv.s, hsv.v] = [c.h, c.s, c.v];
}
else {
const hP = getHuePosition(c0.h, c1.h, n === 1 ? undefined : 1);
hsv.h = hP[0] + (hP[1] - hP[0]) * value;
hsv.s = c0.s + (c1.s - c0.s) * value;
hsv.v = c0.v + (c1.v - c0.v) * value;
}
obj.color = {...hsv};
obj.rectPointer.style.left = Math.floor(obj.rect.clientWidth * hsv.s) + 'px';
obj.rectPointer.style.top = Math.floor(obj.rect.clientHeight * (1 - hsv.v)) + 'px';
obj.rect.style.backgroundColor = `hsl(${hsv.h}deg 100% 50%)`;
obj.innerCircle.style.transform = `rotate(${hsv.h}deg)`;
obj.circlePointer.style.borderColor = `hsl(${180 + hsv.h}, 100%, 50%)`;
obj.historyCell[0].style.backgroundColor = hsv2rgb(hsv.h, hsv.s, hsv.v).cc;
feedbackValue(obj.p, true);
}
function disabledDefaultPicker(e) {
const cType = e.type;
const cn = e.cloneNode();
e.before(cn);
e.type = 'hidden';
setTimeout(function() {
e.value = cn.value;
e.type = cType;
cn.remove();
});
}
function getHuePosition(start, end, direction) {
start %= 360;
end %= 360;
if(direction !== undefined && direction) {
if(start < end) start += 360;
}
else {
if(start > end) end += 360;
}
return [start, end];
}
}
####使用方法####
scriptタグでcolor_picker.jsを読み込み、対象input要素にdata-colorpicker
属性を付加することで適用できます。
<script src='color_picker.js'></script>
<input type='text' data-colorpicker>
<input type='text' data-colorpicker='size:300'>
以下のオプションが使用できます。
size: サイズ
ピッカーの初期サイズ
単位はpixel、最小値は100、無指定時のデフォルトは200です。
rgb-slider-disabled: true
RGBスライダー非表示
history-palette-disabled: true
履歴パレット非表示
gradation-palette-disabled: true
グラデーションパレット非表示
適用input要素のtypeはとくに制限していませんが、value値としてカラーコードを指定できない要素では期待する動作にはならないと思います。
####サンプル####
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<script src='color_picker.js'></script>
</head>
<body>
type=text<br>
<input type='text' data-colorpicker value='#ffff00'>
<input type='text' data-colorpicker>
<input type='text' data-colorpicker='size:300; rgb-slider-disabled:true; gradation-palette-disabled:true;'>
<input type='text' data-colorpicker='size:250; rgb-slider-disabled:true; history-palette-disabled:true; gradation-palette-disabled:true;'>
<hr>
type=button<br>
<input type='button' style='width:70px' data-colorpicker><hr>
type=color<br>
<input type='color' value='#ff0000' data-colorpicker><br>
<input type='color' value='#ff0000'> 本UIなし(各ブラウザのデフォルトピッカー)
</body>
</html>
設置デモ
スマホ、PC両対応(IE除く)