← back to Dw Theme Boost Fix
assets/dw-color-wheel.js
87 lines
/* DW "Shop by Color" — wheel + hue slider, live LAB re-sort over the whole catalog.
* Injects a button on collection pages → opens a drawer with the wheel + results.
* Data (handle→dominant hex + palette + image) lazy-loaded from window.DW_CW_DATA on first open. $0. */
(function(){
if(!/\/collections\//.test(location.pathname)) return; // collection pages only
if(window.__dwcw) return; window.__dwcw=1;
var DATA=null, loading=false, RESULTS=120;
var CDN='https://cdn.shopify.com', STORE='';
var hx2rgb=function(h){h=(''+h).replace('#','');if(h.length===3)h=h[0]+h[0]+h[1]+h[1]+h[2]+h[2];return[0,2,4].map(function(i){return parseInt(h.slice(i,i+2),16);});};
function rgb2lab(c){var R=c.map(function(v){v/=255;return v>0.04045?Math.pow((v+0.055)/1.055,2.4):v/12.92;});
var X=(R[0]*0.4124+R[1]*0.3576+R[2]*0.1805)/0.95047,Y=R[0]*0.2126+R[1]*0.7152+R[2]*0.0722,Z=(R[0]*0.0193+R[1]*0.1192+R[2]*0.9505)/1.08883;
var f=function(t){return t>0.008856?Math.cbrt(t):7.787*t+16/116;};X=f(X);Y=f(Y);Z=f(Z);return[116*Y-16,500*(X-Y),200*(Y-Z)];}
var dE=function(a,b){return Math.hypot(a[0]-b[0],a[1]-b[1],a[2]-b[2]);};
function hsv2rgb(h,s,v){h/=360;var i=Math.floor(h*6),f=h*6-i,p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);
var m=[[v,t,p],[q,v,p],[p,v,t],[p,q,v],[t,p,v],[v,p,q]][i%6];return[Math.round(m[0]*255),Math.round(m[1]*255),Math.round(m[2]*255)];}
var toHex=function(c){return '#'+c.map(function(x){return x.toString(16).padStart(2,'0');}).join('');};
var css='#dwcw-btn{position:fixed;right:18px;bottom:18px;z-index:9998;background:#1a1a1a;color:#fff;border:none;border-radius:30px;padding:12px 18px;font:600 14px system-ui;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.25);display:flex;align-items:center;gap:8px}'
+'#dwcw-btn .sw{width:18px;height:18px;border-radius:50%;background:conic-gradient(red,#ff0,#0f0,#0ff,#00f,#f0f,red);border:2px solid #fff}'
+'#dwcw-ov{position:fixed;inset:0;z-index:9999;background:rgba(20,18,16,.45);display:none}'
+'#dwcw-ov.on{display:block}'
+'#dwcw-panel{position:absolute;right:0;top:0;height:100%;width:min(560px,100%);background:#faf8f5;box-shadow:-6px 0 30px rgba(0,0,0,.3);display:flex;flex-direction:column;font:14px system-ui;color:#1a1a1a}'
+'#dwcw-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 8px}'
+'#dwcw-head h2{font:600 19px Georgia,serif;margin:0}#dwcw-x{background:none;border:none;font-size:24px;cursor:pointer;color:#8a8278}'
+'#dwcw-pick{display:flex;gap:16px;align-items:center;padding:4px 20px 10px;flex-wrap:wrap}'
+'#dwcw-wheel{width:150px;height:150px;cursor:crosshair;border-radius:50%;box-shadow:0 3px 14px rgba(0,0,0,.15)}'
+'#dwcw-hue{width:150px;height:22px;-webkit-appearance:none;border-radius:11px;outline:none;background:linear-gradient(90deg,hsl(0 90% 50%),hsl(60 90% 50%),hsl(120 90% 45%),hsl(180 80% 45%),hsl(240 85% 55%),hsl(300 80% 55%),hsl(360 90% 50%))}'
+'#dwcw-hue::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:#fff;border:3px solid #1a1a1a;cursor:pointer}'
+'#dwcw-sw{display:flex;align-items:center;gap:8px}#dwcw-sw .chip{width:40px;height:40px;border-radius:9px;border:1px solid #e7e1d8}#dwcw-sw code{font:12px ui-monospace,monospace;color:#8a8278}'
+'#dwcw-grid{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:11px;padding:8px 20px 24px}'
+'.dwcw-card{background:#fff;border:1px solid #e7e1d8;border-radius:9px;overflow:hidden;text-decoration:none;color:inherit}'
+'.dwcw-card .ph{aspect-ratio:1;background:#eee center/cover;position:relative}.dwcw-card .dot{position:absolute;left:7px;bottom:7px;width:14px;height:14px;border-radius:50%;border:2px solid #fff}'
+'.dwcw-card .t{font-size:11px;padding:6px 8px 3px;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:30px}'
+'.dwcw-bar{display:flex;height:11px;margin:0 8px;border-radius:3px;overflow:hidden}.dwcw-row{font:9px ui-monospace,monospace;color:#9a9288;padding:4px 8px;display:flex;flex-wrap:wrap;gap:1px 6px}'
+'#dwcw-load{padding:40px;text-align:center;color:#8a8278;grid-column:1/-1}';
var ov,grid,chip,hexlbl,cv,cx,R=75,curHex='#c8102e';
function build(){
var s=document.createElement('style');s.textContent=css;document.head.appendChild(s);
ov=document.createElement('div');ov.id='dwcw-ov';
ov.innerHTML='<div id=dwcw-panel><div id=dwcw-head><h2>Shop by Color</h2><button id=dwcw-x>×</button></div>'
+'<div id=dwcw-pick><canvas id=dwcw-wheel width=150 height=150></canvas>'
+'<div><input type=range id=dwcw-hue min=0 max=360 value=350>'
+'<div id=dwcw-sw style="margin-top:10px"><span class=chip id=dwcw-chip></span><code id=dwcw-hex>#c8102e</code></div></div></div>'
+'<div id=dwcw-grid><div id=dwcw-load>Loading the color library…</div></div></div>';
document.body.appendChild(ov);
grid=ov.querySelector('#dwcw-grid');chip=ov.querySelector('#dwcw-chip');hexlbl=ov.querySelector('#dwcw-hex');
cv=ov.querySelector('#dwcw-wheel');cx=cv.getContext('2d');
var img=cx.createImageData(150,150),d=img.data;
for(var y=0;y<150;y++)for(var x=0;x<150;x++){var dx=x-R,dy=y-R,r=Math.hypot(dx,dy),idx=(y*150+x)*4;
if(r>R){d[idx+3]=0;continue;}var a=Math.atan2(dy,dx)*180/Math.PI;if(a<0)a+=360;var c=hsv2rgb(a,Math.min(1,r/R),1);d[idx]=c[0];d[idx+1]=c[1];d[idx+2]=c[2];d[idx+3]=255;}
cx.putImageData(img,0,0);
ov.querySelector('#dwcw-x').onclick=function(){ov.classList.remove('on');};
ov.onclick=function(e){if(e.target===ov)ov.classList.remove('on');};
var drag=false;
function pick(e){var b=cv.getBoundingClientRect(),x=(e.clientX-b.left)/b.width*150-R,y=(e.clientY-b.top)/b.height*150-R,r=Math.hypot(x,y),a=Math.atan2(y,x)*180/Math.PI;if(a<0)a+=360;setHex(toHex(hsv2rgb(a,Math.min(1,r/R),1)));ov.querySelector('#dwcw-hue').value=Math.round(a);}
cv.addEventListener('pointerdown',function(e){drag=true;pick(e);});
addEventListener('pointermove',function(e){if(drag)requestAnimationFrame(function(){pick(e);});});
addEventListener('pointerup',function(){drag=false;});
ov.querySelector('#dwcw-hue').addEventListener('input',function(e){setHex(toHex(hsv2rgb(+e.target.value,0.85,0.85)));});
}
function setHex(hex){curHex=hex;if(chip)chip.style.background=hex;if(hexlbl)hexlbl.textContent=hex.toUpperCase();render();}
function render(){
if(!DATA){return;}
var lab=rgb2lab(hx2rgb(curHex)),i;
for(i=0;i<DATA.length;i++)DATA[i]._d=dE(DATA[i].lab,lab);
var top=DATA.slice().sort(function(a,b){return a._d-b._d;}).slice(0,RESULTS);
grid.innerHTML=top.map(function(p){
var pal=(p.p||[]).map(function(x){return '<span style="flex:'+(x[1]||1)+';background:'+x[0]+'"></span>';}).join('');
var palr=(p.p||[]).map(function(x){return '<span>'+x[1]+'% '+x[0]+'</span>';}).join('');
return '<a class=dwcw-card href="/products/'+p.h+'"><div class=ph style="background-image:url(\''+(p.i?CDN+p.i+'?width=220':'')+'\')"><span class=dot style="background:'+p.d+'"></span></div>'
+'<div class=t>'+(p.t||p.h)+'</div><div class=dwcw-bar>'+pal+'</div><div class=dwcw-row>'+palr+'</div></a>';
}).join('');
}
function load(){
if(DATA||loading)return;loading=true;
fetch(window.DW_CW_DATA).then(function(r){return r.json();}).then(function(d){
DATA=d.map(function(p){p.lab=p.d?rgb2lab(hx2rgb(p.d)):[50,0,0];return p;});
setHex('#c8102e');
}).catch(function(){grid.innerHTML='<div id=dwcw-load>Color library unavailable.</div>';});
}
var btn=document.createElement('button');btn.id='dwcw-btn';btn.innerHTML='<span class=sw></span> Shop by Color';
btn.onclick=function(){if(!ov)build();ov.classList.add('on');load();};
(document.body?document.body:document.documentElement).appendChild(btn);
})();