poline
Version:
color palette generator mico-lib
66 lines (65 loc) • 16 kB
JavaScript
"use strict";var w=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var a=Math.pow;var z=(e,t)=>{for(var o in t)w(e,o,{get:t[o],enumerable:!0})},G=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of E(t))!k.call(e,i)&&i!==o&&w(e,i,{get:()=>t[i],enumerable:!(n=y(t,i))||n.enumerable});return e};var X=e=>G(w({},"__esModule",{value:!0}),e);var N={};z(N,{Poline:()=>g,PolinePicker:()=>b,positionFunctions:()=>A});module.exports=X(N);var F=(e,t)=>{let[o,n,i]=e,s=.5,h=.5,c=Math.atan2(n-h,o-s)*(180/Math.PI);c=(360+c)%360;let l=i,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-s,2))/s;return[c,l,t?1-u:u]},_=(e,t)=>{let[o,n,i]=e,s=.5,h=.5,r=o/(180/Math.PI),c=(t?1-i:i)*s,l=s+c*Math.cos(r),P=h+c*Math.sin(r);return[l,P,n]},S=(e=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[e,t[0],o[0]],[(e+60+Math.random()*180)%360,t[1],o[1]]];var x=(e,t,o,n=!1,i=(r,c)=>c?1-r:r,s=(r,c)=>c?1-r:r,h=(r,c)=>c?1-r:r)=>{let r=i(e,n),c=s(e,n),l=h(e,n),P=(1-r)*t[0]+r*o[0],u=(1-c)*t[1]+c*o[1],f=(1-l)*t[2]+l*o[2];return[P,u,f]},Y=(e,t,o=4,n=!1,i=(r,c)=>c?1-r:r,s=(r,c)=>c?1-r:r,h=(r,c)=>c?1-r:r)=>{let r=[];for(let c=0;c<o;c++){let[l,P,u]=x(c/(o-1),e,t,n,i,s,h);r.push([l,P,u])}return r},T=e=>e,I=(e,t=!1)=>t?1-a(1-e,2):a(e,2),$=(e,t=!1)=>t?1-a(1-e,3):a(e,3),Z=(e,t=!1)=>t?1-a(1-e,4):a(e,4),q=(e,t=!1)=>t?1-a(1-e,5):a(e,5),p=(e,t=!1)=>t?1-Math.sin((1-e)*Math.PI/2):Math.sin(e*Math.PI/2),R=(e,t=!1)=>t?1-Math.asin(1-e)/(Math.PI/2):Math.asin(e)/(Math.PI/2),D=(e,t=!1)=>t?1-Math.sqrt(1-a(e,2)):1-Math.sqrt(1-e),H=e=>a(e,2)*(3-2*e),A={linearPosition:T,exponentialPosition:I,quadraticPosition:$,cubicPosition:Z,quarticPosition:q,sinusoidalPosition:p,asinusoidalPosition:R,arcPosition:D,smoothStepPosition:H},L=(e,t,o=!1)=>{let n=e[0],i=t[0],s=0;o&&n!==null&&i!==null?(s=Math.min(Math.abs(n-i),360-Math.abs(n-i)),s=s/360):s=n===null||i===null?0:n-i;let h=s,r=e[1]===null||t[1]===null?0:t[1]-e[1],c=e[2]===null||t[2]===null?0:t[2]-e[2];return Math.sqrt(h*h+r*r+c*c)},v=class{constructor({xyz:t,color:o,invertedLightness:n=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=n,this.positionOrColor({xyz:t,color:o,invertedLightness:n})}positionOrColor({xyz:t,color:o,invertedLightness:n=!1}){if(t&&o||!t&&!o)throw new Error("Point must be initialized with either x,y,z or hsl");t?(this.x=t[0],this.y=t[1],this.z=t[2],this.color=F([this.x,this.y,this.z],n)):o&&(this.color=o,[this.x,this.y,this.z]=_(o,n))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=F([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,n]){this.color=[t,o,n],[this.x,this.y,this.z]=_(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,n]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(n*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,n]=this.color;return`oklch(${(n*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,n]=this.color;return`lch(${(n*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=_(this.color,this._invertedLightness)}},g=class{constructor({anchorColors:t=S(),numPoints:o=4,positionFunction:n=p,positionFunctionX:i,positionFunctionY:s,positionFunctionZ:h,closedLoop:r,invertedLightness:c}={anchorColors:S(),numPoints:4,positionFunction:p,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=p;this._positionFunctionY=p;this._positionFunctionZ=p;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(l=>new v({color:l,invertedLightness:c})),this._numPoints=o+2,this._positionFunctionX=i||n||p,this._positionFunctionY=s||n||p,this._positionFunctionZ=h||n||p,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=c||!1,this.updateAnchorPairs()}get numPoints(){return this._numPoints-2}set numPoints(t){if(t<1)throw new Error("Must have at least one point");this._numPoints=t+2,this.updateAnchorPairs()}set positionFunction(t){if(Array.isArray(t)){if(t.length!==3)throw new Error("Position function array must have 3 elements");if(typeof t[0]!="function"||typeof t[1]!="function"||typeof t[2]!="function")throw new Error("Position function array must have 3 functions");this._positionFunctionX=t[0],this._positionFunctionY=t[1],this._positionFunctionZ=t[2]}else this._positionFunctionX=t,this._positionFunctionY=t,this._positionFunctionZ=t;this.updateAnchorPairs()}get positionFunction(){return this._positionFunctionX===this._positionFunctionY&&this._positionFunctionX===this._positionFunctionZ?this._positionFunctionX:[this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ]}set positionFunctionX(t){this._positionFunctionX=t,this.updateAnchorPairs()}get positionFunctionX(){return this._positionFunctionX}set positionFunctionY(t){this._positionFunctionY=t,this.updateAnchorPairs()}get positionFunctionY(){return this._positionFunctionY}set positionFunctionZ(t){this._positionFunctionZ=t,this.updateAnchorPairs()}get positionFunctionZ(){return this._positionFunctionZ}get anchorPoints(){return this._anchorPoints}set anchorPoints(t){this._anchorPoints=t,this.updateAnchorPairs()}updateAnchorPairs(){this._anchorPairs=[];let t=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1;for(let o=0;o<t;o++){let n=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(n)}this.points=this._anchorPairs.map((o,n)=>{let i=o[0]?o[0].position:[0,0,0],s=o[1]?o[1].position:[0,0,0],h=this.shouldInvertEaseForSegment(n);return Y(i,s,this._numPoints,!!h,this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(r=>new v({xyz:r,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n}){let i=new v({xyz:t,color:o,invertedLightness:this._invertedLightness});return n!==void 0?this.anchorPoints.splice(n,0,i):this.anchorPoints.push(i),this.updateAnchorPairs(),i}removeAnchorPoint({point:t,index:o}){if(!t&&o===void 0)throw new Error("Must provide a point or index");if(this.anchorPoints.length<3)throw new Error("Must have at least two anchor points");let n;if(o!==void 0?n=o:t&&(n=this.anchorPoints.indexOf(t)),n>-1&&n<this.anchorPoints.length)this.anchorPoints.splice(n,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:n,color:i}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!n&&!i)throw new Error("Must provide a new xyz position or color");return n&&(t.position=n),i&&(t.hsl=i),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:n=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let i;t?i=this.anchorPoints.map(r=>L(r.position,t)):o&&(i=this.anchorPoints.map(r=>L(r.hsl,o,!0)));let s=Math.min(...i);if(s>n)return null;let h=i.indexOf(s);return this.anchorPoints[h]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._invertedLightness=t,this.updateAnchorPairs()}get invertedLightness(){return this._invertedLightness}get flattenedPoints(){return this.points.flat().filter((t,o)=>o!=0?o%this._numPoints:!0)}get colors(){let t=this.flattenedPoints.map(o=>o.color);return this.connectLastAndFirstAnchor&&this._anchorPoints.length!==2&&t.pop(),t}cssColors(t="hsl"){let o={hsl:i=>i.hslCSS,oklch:i=>i.oklchCSS,lch:i=>i.lchCSS},n=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&n.pop(),n}get colorsCSS(){return this.cssColors("hsl")}get colorsCSSlch(){return this.cssColors("lch")}get colorsCSSoklch(){return this.cssColors("oklch")}shiftHue(t=20){this.anchorPoints.forEach(o=>o.shiftHue(t)),this.updateAnchorPairs()}getColorAt(t){var C;if(t<0||t>1)throw new Error("Position must be between 0 and 1");if(this.anchorPoints.length===0)throw new Error("No anchor points available");let o=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1,n=this.connectLastAndFirstAnchor&&this.anchorPoints.length===2?2:o,i=t*n,s=Math.floor(i),h=i-s,r=s>=n?n-1:s,c=s>=n?1:h,l=this._anchorPairs[r];if(!l||l.length<2||!l[0]||!l[1])return new v({color:((C=this.anchorPoints[0])==null?void 0:C.color)||[0,0,0],invertedLightness:this._invertedLightness});let P=l[0].position,u=l[1].position,f=this.shouldInvertEaseForSegment(r),V=x(c,P,u,f,this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ);return new v({xyz:V,invertedLightness:this._invertedLightness})}shouldInvertEaseForSegment(t){return!!(t%2||this.connectLastAndFirstAnchor&&this.anchorPoints.length===2&&t===0)}},{p5:m}=globalThis;if(m&&m.VERSION&&m.VERSION.startsWith("1.")){console.info("p5 < 1.x detected, adding poline to p5 prototype");let e=new g;m.prototype.poline=e;let t=()=>e.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);m.prototype.registerMethod("polineColors",t),globalThis.poline=e,globalThis.polineColors=t}var M="http://www.w3.org/2000/svg",d=100,b=class extends HTMLElement{constructor(){super();this.currentPoint=null;this.allowAddPoints=!1;this.boundPointerDown=this.handlePointerDown.bind(this);this.boundPointerMove=this.handlePointerMove.bind(this);this.boundPointerUp=this.handlePointerUp.bind(this);this.attachShadow({mode:"open"}),this.interactive=this.hasAttribute("interactive"),this.allowAddPoints=this.hasAttribute("allow-add-points")}connectedCallback(){this.render(),this.interactive&&this.addEventListeners()}disconnectedCallback(){this.removeEventListeners()}setPoline(o){this.poline=o,this.updateSVG(),this.updateLightnessBackground()}setAllowAddPoints(o){this.allowAddPoints=o}addPointAtPosition(o,n){if(!this.poline)return null;let i=o/this.svg.clientWidth,s=n/this.svg.clientHeight,h=this.poline.addAnchorPoint({xyz:[i,s,s]});return this.updateSVG(),this.dispatchPolineChange(),h}updateLightnessBackground(){var n;let o=(n=this.shadowRoot)==null?void 0:n.querySelector(".picker");o&&this.poline&&(this.poline.invertedLightness?(o.style.setProperty("--maxL","#000"),o.style.setProperty("--minL","#fff")):(o.style.setProperty("--maxL","#fff"),o.style.setProperty("--minL","#000")))}render(){if(!this.shadowRoot)return;this.shadowRoot.innerHTML=`
<style>
:host {
display: block;
width: 100%;
}
.picker {
position: relative;
width: 100%;
aspect-ratio: 1;
--wheelS: var(--poline-picker-wheel-saturation, .4);
--wheelL: var(--poline-picker-wheel-lightness, .5);
--minL: #000;
--maxL: #fff;
--grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg;
}
.picker::before {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)),
conic-gradient(from 90deg, var(--grad));
z-index: 1;
}
svg {
position: relative;
z-index: 2;
overflow: visible;
width: 100%;
}
.wheel__line {
stroke: var(--poline-picker-line-color, #000);
stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2));
fill: none;
}
.wheel__anchor {
cursor: grab;
stroke: var(--poline-picker-line-color, #000);
stroke-width: var(--poline-picker-line-width, 0.2);
fill: var(--poline-picker-bg-color, #fff);
}
.wheel__anchor:hover {
cursor: grabbing;
}
.wheel__point {
stroke: var(--poline-picker-line-color, #000);
stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2));
pointer-events: none;
}
</style>
`,this.svg=this.createSVG();let o=document.createElement("div");o.className="picker",o.appendChild(this.svg),this.shadowRoot.appendChild(o),this.wheel=this.svg.querySelector(".wheel"),this.line=this.svg.querySelector(".wheel__line"),this.anchors=this.svg.querySelector(".wheel__anchors"),this.points=this.svg.querySelector(".wheel__points"),this.poline&&this.updateSVG()}createSVG(){let o=document.createElementNS(M,"svg");return o.setAttribute("viewBox",`0 0 ${d} ${d}`),o.innerHTML=`
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" />
<feBlend in="SourceGraphic" in2="goo" />
</filter>
</defs>
<g class="wheel" filter="url(#goo)">
<polyline class="wheel__line" points="" />
<g class="wheel__anchors"></g>
<g class="wheel__points"></g>
</g>
`,o}updateSVG(){if(!this.poline||!this.svg)return;let o=this.poline.flattenedPoints.map(n=>{let i=this.pointToCartesian(n);if(!i)return"";let[s,h]=i;return`${s},${h}`}).filter(n=>n!=="").join(" ");this.line.setAttribute("points",o),this.anchors.innerHTML="",this.points.innerHTML="",this.poline.anchorPoints.forEach(n=>{let i=this.createCircleElement(n,"wheel__anchor","2");i&&this.anchors.appendChild(i)}),this.poline.flattenedPoints.forEach(n=>{let i=.5+n.color[1],s=this.createCircleElement(n,"wheel__point",i);s&&this.points.appendChild(s)})}pointToCartesian(o){let n=d/2,i=n+(o.x-.5)*d,s=n+(o.y-.5)*d;return[i,s]}addEventListeners(){this.svg&&(this.svg.addEventListener("pointerdown",this.boundPointerDown),this.svg.addEventListener("pointermove",this.boundPointerMove),this.svg.addEventListener("pointerup",this.boundPointerUp))}removeEventListeners(){this.svg&&(this.svg.removeEventListener("pointerdown",this.boundPointerDown),this.svg.removeEventListener("pointermove",this.boundPointerMove),this.svg.removeEventListener("pointerup",this.boundPointerUp))}handlePointerDown(o){o.stopPropagation();let{normalizedX:n,normalizedY:i}=this.pointerToNormalizedCoordinates(o),s=this.poline.getClosestAnchorPoint({xyz:[n,i,null],maxDistance:.1});s?this.currentPoint=s:this.allowAddPoints&&(this.currentPoint=this.poline.addAnchorPoint({xyz:[n,i,i]}),this.updateSVG(),this.dispatchPolineChange())}handlePointerMove(o){if(this.currentPoint){let{normalizedX:n,normalizedY:i}=this.pointerToNormalizedCoordinates(o);this.poline.updateAnchorPoint({point:this.currentPoint,xyz:[n,i,this.currentPoint.z]}),this.updateSVG(),this.dispatchPolineChange()}}handlePointerUp(){this.currentPoint=null}getPointerPosition(o){let n=this.svg.getBoundingClientRect();return{x:o.clientX-n.left,y:o.clientY-n.top}}pointerToNormalizedCoordinates(o){let n=this.svg.getBoundingClientRect(),i=(o.clientX-n.left)/n.width*d,s=(o.clientY-n.top)/n.height*d;return{normalizedX:i/d,normalizedY:s/d}}createCircleElement(o,n,i){let s=this.pointToCartesian(o);if(!s)return null;let[h=0,r=0]=s,c=document.createElementNS(M,"circle");return c.setAttribute("class",n),c.setAttribute("cx",h.toString()),c.setAttribute("cy",r.toString()),c.setAttribute("r",i.toString()),c.setAttribute("fill",o.hslCSS),c}dispatchPolineChange(){this.dispatchEvent(new CustomEvent("poline-change",{detail:{poline:this.poline}}))}};customElements.define("poline-picker",b);