0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSSの色関数相互変換

0
Posted at

#RGBrgb()hsl()oklab()等といった連中を相互変換する的な何かです。やや中途半端な性能。oninput eventに反応する設計です

<!DOCTYPE html><html><head><meta charset=sjis><meta name=viewport content="width=device-width,initial-scale=1">
<title>CSS Color Sync</title><style>
:root{--bg:#111;--panel:#1d1d1d;--text:#f2f2f2;--border:#3a3a3a;--accent:#5ad1ff}
*{box-sizing:border-box;font-family:MS Gothic,monospace}
body{margin:0;background:linear-gradient(135deg,#0d0d0d,#1a1a1a);color:var(--text);min-height:100vh}
.wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:20px}
.panel{background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:18px;padding:18px;backdrop-filter:blur(8px)}
.preview{height:220px;border-radius:20px;border:2px solid rgba(255,255,255,.15);box-shadow:0 0 40px rgba(0,0,0,.4) inset}
.preview:empty:before{content:"preview";display:block;text-align:center;position:relative;top:45%}
.row{margin-top:14px}
label{display:block;margin-bottom:6px;font-weight:700;color:var(--accent)}
input[type=text]{width:100%;background:#101010;border:1px solid var(--border);color:#fff;padding:10px 12px;border-radius:10px;font-family:ui-monospace,monospace;font-size:14px}
input[type=color]{width:100%;height:60px;border:none;background:0 0;cursor:pointer}
.status{margin-top:12px;min-height:24px;font-size:14px;color:#ffb3b3}
.hint{opacity:.8;line-height:1.6}
</style></head><body>
<p class=hint>color(), hsl(), hwb(), lab(), lch(), oklab(), oklch(), rgb(), rgba(), #RGB/#RGBA を相互変換して同期します
<div class=wrap>
	<div class=panel>
		<div id=preview class=preview></div>
		<div class=row>
			<label>Color Picker</label>
			<input id=picker type=color value="#0af">
		</div>
		<div class=status id=status></div>
	</div>
	<div class=panel id=inputs></div>
</div><script>
const formats=[
	['hex','#RGB / #RGBA / #RRGGBB / #RRGGBBAA'],
	['rgb','rgb()'],
	['rgba','rgba()'],
	['hsl','hsl()'],
	['hwb','hwb()'],
	['lab','lab()'],
	['lch','lch()'],
	['oklab','oklab()'],
	['oklch','oklch()'],
	['color','color(display-p3 ...)']
],gE=a=>document.getElementById(a),
	container=gE('inputs'),
	preview=gE('preview'),
	picker=gE('picker'),
	status=gE('status'),
	inputs={};

for(let[k,l]of formats){
	const row=document.createElement('div');
	row.className='row';
	row.innerHTML=`<label>${l}</label><input type=text id="${k}">`;
	container.appendChild(row);
	inputs[k]=row.querySelector('input')
}
function canvasColor(str){
	const ctx=document.createElement('canvas').getContext('2d');
	ctx.fillStyle='#000';
	ctx.fillStyle=str;
	return ctx.fillStyle
}
function validColor(str){
	return canvasColor(str)!=='#000000'||str==='#000'||str==='#000000'||(str=str.toLowerCase())==='black'||str==='rgb(0 0 0)'
}
function parseRGB(css){
	let d=document.createElement('div');
	d.style.color=css;
	document.body.appendChild(d);
	d.remove(d=getComputedStyle(d).color.match(/\d+(?:\.\d+)?/g).map(Number));
	return{r:d[0],g:d[1],b:d[2],a:d[3]??1}
}
function toHex({r,g,b,a=1}){
	const hex=[r,g,b].map(v=>Math.round(v).toString(16).padStart(2,'0')).join('');
	const aa=Math.round(a*255).toString(16).padStart(2,'0');
	return a<1?'#'+hex+aa:'#'+hex;
}
function rgbToHsl(r,g,b){
	r/=255;g/=255;b/=255;
	const max=Math.max(r,g,b),min=Math.min(r,g,b);
	let h,s,l=(max+min)/2;
	if(max===min)return {h:0,s:0,l:l*100};
	const d=max-min;
	s=l>.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*=60;
	return {h,s:s*100,l:l*100};
}
function hslToHwb(h,s,l){
	s/=100;l/=100;
	const v=l+s*Math.min(l,1-l);
	const w=(2*l-v)*100;
	const b=(1-v)*100;
	return {h,w,b};
}
function pivotRgb(n){
	n/=255;
	return n<=0.04045?n/12.92:((n+0.055)/1.055)**2.4
}
function rgbToXyz(r,g,b){
	r=pivotRgb(r);g=pivotRgb(g);b=pivotRgb(b);
	return{
		x:r*0.4124564+g*0.3575761+b*0.1804375,
		y:r*0.2126729+g*0.7151522+b*0.0721750,
		z:r*0.0193339+g*0.1191920+b*0.9503041
	};
}
function pivotLab(n){
	return n>0.008856?Math.cbrt(n):(903.3*n+16)/116;
}
function rgbToLab(r,g,b){
	let xyz=rgbToXyz(r,g,b),x=pivotLab(xyz.x/0.95047),y=pivotLab(xyz.y),z=pivotLab(xyz.z/1.08883);
	return{l:116*y-16,a:500*(x-y),b:200*(y-z)}
}
function labToLch(l,a,b){
	return{l,c:Math.sqrt(a*a+b*b),h:(Math.atan2(b,a)*180/Math.PI+360)%360}
}
function rgbToOklab(r,g,b){
	r=pivotRgb(r);g=pivotRgb(g);b=pivotRgb(b);
	let l=Math.cbrt(0.4122214708*r+0.5363325363*g+0.0514459929*b),
		m=Math.cbrt(0.2119034982*r+0.6806995451*g+0.1073969566*b),
		s=Math.cbrt(0.0883024619*r+0.2817188376*g+0.6299787005*b);
	return{
		l:0.2104542553*l+0.7936177850*m-0.0040720468*s,
		a:1.9779984951*l-2.4285922050*m+0.4505937099*s,
		b:0.0259040371*l+0.7827717662*m-0.8086757660*s
	}
}
function toFns(rgb){
	const hex=toHex(rgb),probe=document.createElement('div'),
	read=(prop,val)=>{
		probe.style.color=val;
		return getComputedStyle(probe).color;
	};
	document.body.appendChild(probe);
	probe.style.color=hex;
	const hsl=rgbToHsl(rgb.r,rgb.g,rgb.b),
		hwb=hslToHwb(hsl.h,hsl.s,hsl.l),
		lab=rgbToLab(rgb.r,rgb.g,rgb.b),
		lch=labToLch(lab.l,lab.a,lab.b),
		oklab=rgbToOklab(rgb.r,rgb.g,rgb.b),
		oklch=labToLch(oklab.l,oklab.a,oklab.b),

	out={
		hex,
		rgb:`rgb(${rgb.r} ${rgb.g} ${rgb.b})`,
		rgba:`rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${(+rgb.a.toFixed(4))})`,
		hsl:`hsl(${hsl.h.toFixed(1)} ${hsl.s.toFixed(1)}% ${hsl.l.toFixed(1)}%)`,
		hwb:`hwb(${hwb.h.toFixed(1)} ${hwb.w.toFixed(1)}% ${hwb.b.toFixed(1)}%)`,
		lab:`lab(${lab.l.toFixed(3)}% ${lab.a.toFixed(3)} ${lab.b.toFixed(3)})`,
		lch:`lch(${lch.l.toFixed(3)}% ${lch.c.toFixed(3)} ${lch.h.toFixed(3)})`,
		oklab:`oklab(${oklab.l.toFixed(5)} ${oklab.a.toFixed(5)} ${oklab.b.toFixed(5)})`,
		oklch:`oklch(${oklch.l.toFixed(5)} ${oklch.c.toFixed(5)} ${oklch.h.toFixed(3)})`,
		color:`color(display-p3 ${(rgb.r/255).toFixed(4)} ${(rgb.g/255).toFixed(4)} ${(rgb.b/255).toFixed(4)})`
	};
	probe.remove();
	return out
}
let lock;
function sync(source,value){
	if(lock)return;
	lock=1;
	try{
		if(!validColor(value)){
			status.textContent='無効な色です';
			return lock=0
		}
		status.textContent='';

		const rgb=parseRGB(value),vals=toFns(rgb);
		for(const k in inputs)
			if(k!==source)inputs[k].value=vals[k];
		if(source in vals)inputs[source].value=value;
		preview.style.background=value;
		picker.value=vals.hex
	}catch(e){status.textContent=e+''}
	lock=0
}
for(const k in inputs)inputs[k].oninput=e=>sync(k,e.target.value);
picker.oninput=e=>sync('hex',e.target.value);
sync('hex','#00aaff')
</script>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?