@liiift-studio/threadtext 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +34 -20
- package/dist/index.js +392 -338
- package/dist/threadtext.webflow.min.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Homepage: [threadtext.com](https://threadtext.com) · npm: `@liiift-studio/threa
|
|
|
15
15
|
|
|
16
16
|
## What it is
|
|
17
17
|
|
|
18
|
-
Give it a word set in any loaded (variable) font and it renders it as **raised
|
|
18
|
+
Give it a word set in any loaded (variable) font and it renders it as **raised satin floss on a transparent ground** — threads that run *across* each stroke and fan around the curves, lifted into 3D, and sewn in one satin stitch at a time. Drop it over any background. It's photorealistic **by construction** (real thread geometry + lighting), not a raster filter and not an AI image.
|
|
19
19
|
|
|
20
20
|
## Why it's worth building (the niche is empty)
|
|
21
21
|
|
|
@@ -44,7 +44,7 @@ Type to embroider a new word · Backspace to unpick · Enter to replay the sew-i
|
|
|
44
44
|
|
|
45
45
|
## How it works (one paragraph)
|
|
46
46
|
|
|
47
|
-
Rasterise the glyphs → compute a signed-distance field → derive a **flow field** (smoothed in double-angle orientation space so opposite edge-normals reinforce) so threads run across each stroke → lay thousands of discrete pre-shaded **thread sprites** oriented by the flow → lift to 3D with a dome-shade/normal map
|
|
47
|
+
Rasterise the glyphs → compute a signed-distance field → derive a **flow field** (smoothed in double-angle orientation space so opposite edge-normals reinforce) so threads run across each stroke → lay thousands of discrete pre-shaded **thread sprites** oriented by the flow → lift to 3D with a dome-shade/normal map on a transparent ground → **sew it in** one BFS cross-row at a time (the needle follows the stroke) → a subtle cursor-driven sheen. Full walkthrough with line references in `HANDOFF.md`.
|
|
48
48
|
|
|
49
49
|
## Use it (extracted API)
|
|
50
50
|
|
|
@@ -72,9 +72,11 @@ import { ThreadText } from '@liiift-studio/threadtext'
|
|
|
72
72
|
<ThreadText text="Thread" font='"Your Font", serif' weight={680} />
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
`
|
|
77
|
-
|
|
75
|
+
Thread colour, `fill` (size — the word re-fits to the container width on load/resize),
|
|
76
|
+
`weight`, `font`, `sewRate`, `sheen`, `animate`, and `editable` (type straight on the
|
|
77
|
+
artwork) are all options — see `ThreadTextOptions`. Change any of them live with
|
|
78
|
+
`instance.update(...)` — it redraws instantly, never re-running the sew-in. `react`/`react-dom`
|
|
79
|
+
are optional peers; the core is framework-free.
|
|
78
80
|
|
|
79
81
|
## What's next
|
|
80
82
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=require("react"),le=require("react/jsx-runtime"),Dt={bg:"tt-bg",fx:"tt-fx"};function Z(x,r){let a=Math.imul(x|0,374761393)+Math.imul(r|0,668265263)|0;return a=Math.imul(a^a>>>13,1274126177),((a^a>>>16)>>>0)/4294967295}function Nt(x,r){const a=Math.floor(x),c=Math.floor(r),l=x-a,s=r-c,h=l*l*(3-2*l),T=s*s*(3-2*s),S=Z(a,c),I=Z(a+1,c),f=Z(a,c+1),u=Z(a+1,c+1);return S+(I-S)*h+(f-S)*T+(S-I-f+u)*h*T}const wt=1e20;function Qt(x,r,a,c,l){let s=0;c[0]=0,l[0]=-wt,l[1]=wt;for(let h=1;h<r;h++){let T=(x[h]+h*h-(x[c[s]]+c[s]*c[s]))/(2*h-2*c[s]);for(;T<=l[s];)s--,T=(x[h]+h*h-(x[c[s]]+c[s]*c[s]))/(2*h-2*c[s]);s++,c[s]=h,l[s]=T,l[s+1]=wt}s=0;for(let h=0;h<r;h++){for(;l[s+1]<h;)s++;a[h]=(h-c[s])*(h-c[s])+x[c[s]]}}function fe(x,r,a){const c=new Float64Array(r*a);for(let f=0;f<r*a;f++)c[f]=x[f]?wt:0;const l=Math.max(r,a),s=new Float64Array(l),h=new Float64Array(l),T=new Int32Array(l),S=new Float64Array(l+1);for(let f=0;f<r;f++){for(let u=0;u<a;u++)s[u]=c[u*r+f];Qt(s,a,h,T,S);for(let u=0;u<a;u++)c[u*r+f]=h[u]}for(let f=0;f<a;f++){for(let u=0;u<r;u++)s[u]=c[f*r+u];Qt(s,r,h,T,S);for(let u=0;u<r;u++)c[f*r+u]=h[u]}const I=new Float32Array(r*a);for(let f=0;f<r*a;f++)I[f]=Math.sqrt(c[f]);return I}function Zt(x,r,a,c,l){let s=Float32Array.from(x);const h=new Float32Array(r*a),T=1/(2*c+1),S=(I,f)=>I<0?0:I>f?f:I;for(let I=0;I<l;I++){for(let f=0;f<a;f++){const u=f*r;let R=0;for(let P=-c;P<=c;P++)R+=s[u+S(P,r-1)];for(let P=0;P<r;P++)h[u+P]=R*T,R+=s[u+S(P+c+1,r-1)]-s[u+S(P-c,r-1)]}for(let f=0;f<r;f++){let u=0;for(let R=-c;R<=c;R++)u+=h[S(R,a-1)*r+f];for(let R=0;R<a;R++)s[R*r+f]=u*T,u+=h[S(R+c+1,a-1)*r+f]-h[S(R-c,a-1)*r+f]}}return s}function Ht(x,r){if(!x)return r;const a=x.trim();if(a[0]==="#"){let l=a.slice(1);if(l.length===3&&(l=l[0]+l[0]+l[1]+l[1]+l[2]+l[2]),l.length>=6){const s=parseInt(l.slice(0,2),16),h=parseInt(l.slice(2,4),16),T=parseInt(l.slice(4,6),16);if(!Number.isNaN(s)&&!Number.isNaN(h)&&!Number.isNaN(T))return[s,h,T]}return r}const c=a.match(/rgba?\(([^)]+)\)/i);if(c){const l=c[1].split(",").map(s=>parseFloat(s));if(l.length>=3&&l.slice(0,3).every(s=>!Number.isNaN(s)))return[Math.round(l[0]),Math.round(l[1]),Math.round(l[2])]}return r}function D(x){return x<0?0:x>255?255:Math.round(x)}function Wt(x,r){if(typeof window>"u"||typeof document>"u"||!x)return{setText(){},replay(){},resize(){},destroy(){},get text(){return r.text??""}};let a=r.text??"";const c=r.font??"Georgia, serif",l=r.weight??680,s=r.aspect??.46,h=r.sewRate??110,T=r.sheen??!0,S=r.animate??!0,I=(c.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,""),f=r.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),u=Ht(r.threadColor??"#fffbf3",[255,251,243]),R=[u[0]*.585,u[1]*.585,u[2]*.585],P=[u[0]*.47,u[1]*.47,u[2]*.47],H=Ht(r.fabricColor??"#08080a",[8,8,10]),pt=[D(H[0]+14),D(H[1]+13),D(H[2]+15)],j=t=>`rgb(${D(t[0])},${D(t[1])},${D(t[2])})`,W=[];let L,O;x instanceof HTMLCanvasElement?(L=x,O=x.parentElement??x):(O=x,L=document.createElement("canvas"),W.push(L),O.appendChild(L)),L.classList.add(Dt.bg);const N=document.createElement("canvas");W.push(N),N.classList.add(Dt.fx),O.appendChild(N),getComputedStyle(O).position==="static"&&(O.style.position="relative"),L.style.display="block",L.style.width="100%",Object.assign(N.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let e=0,i=0,G=0,Mt=0,et=4,nt=[],ot=0,rt=0,Ct=new Uint8Array(0),St=new Float32Array(0),ft=new Float32Array(0),dt=new Float32Array(0),k=null,U=null,V=null,at=null,ut=null,X=[],ht=3;const A={rows:[],idx:0,acc:0,rate:0,on:!1},K={x:0,y:0,set:!1};let Y=!0,Tt=0,Ot=!1,mt=0,J=!1,At=!1;function qt(){const t=O.getBoundingClientRect(),n=Math.round(t.width||Math.min(window.innerWidth*.95,1240)),o=Math.round(n*s);let d=Math.min(window.devicePixelRatio||1,1.7);for(;n*d*o*d>9e5&&d>.55;)d-=.1;e=Math.max(1,Math.round(n*d)),i=Math.max(1,Math.round(o*d));for(const m of[L,N])m.width=e,m.height=i,m.style.height=o+"px";O.style.height=o+"px"}function zt(){const t=Math.max(7,et*2.5),n=Math.max(3.2,et*1.3);ot=Math.ceil(t)+2,rt=Math.ceil(n)+2,nt=[];for(let o=0;o<20;o++){const d=.34+o/19*.82,m=document.createElement("canvas");m.width=ot,m.height=rt;const M=m.getContext("2d");if(!M){nt.push(m);continue}const C=ot/2,F=rt/2,v=M.createLinearGradient(0,F-n/2,0,F+n/2),p=($,q)=>`rgba(${D($[0]*d)},${D($[1]*d)},${D($[2]*d)},${q})`;v.addColorStop(0,p(P,0)),v.addColorStop(.16,p(R,1)),v.addColorStop(.5,p(u,1)),v.addColorStop(.84,p(R,1)),v.addColorStop(1,p(P,0)),M.fillStyle=v,M.beginPath();const w=n/2;M.moveTo(C-t/2+w,F-w),M.lineTo(C+t/2-w,F-w),M.arc(C+t/2-w,F,w,-Math.PI/2,Math.PI/2),M.lineTo(C-t/2+w,F+w),M.arc(C-t/2+w,F,w,Math.PI/2,-Math.PI/2),M.closePath(),M.fill(),M.globalCompositeOperation="destination-in";const E=M.createLinearGradient(C-t/2,0,C+t/2,0);E.addColorStop(0,"rgba(0,0,0,0)"),E.addColorStop(.22,"rgba(0,0,0,1)"),E.addColorStop(.78,"rgba(0,0,0,1)"),E.addColorStop(1,"rgba(0,0,0,0)"),M.fillStyle=E,M.fillRect(0,0,ot,rt),nt.push(m)}}function bt(){if(!ut)return;const t=ut;t.clearRect(0,0,e,i),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${l} ${G}px ${c}`,t.fillText(a,Mt,i*.5);const n=t.getImageData(0,0,e,i).data,o=new Uint8Array(e*i);for(let g=0;g<i;g++)for(let b=0;b<e;b++){const y=g*e+b,z=n[y*4+3]/255,_=Nt(b*.5,g*.5)*.22+Nt(b*1.8,g*1.8)*.1;o[y]=z>.5+(_-.16)?1:0}Ct=o;const d=fe(o,e,i),m=new Float32Array(e*i);for(let g=1;g<i-1;g++)for(let b=1;b<e-1;b++){const y=g*e+b;m[y]=(d[y]*4+d[y-1]+d[y+1]+d[y-e]+d[y+e])/8}const M=new Float32Array(e*i),C=new Float32Array(e*i),F=new Float32Array(e*i),v=new Float32Array(e*i);for(let g=1;g<i-1;g++)for(let b=1;b<e-1;b++){const y=g*e+b;if(!o[y])continue;const z=(m[y+1]-m[y-1])*.5,_=(m[y+e]-m[y-e])*.5;F[y]=z,v[y]=_,M[y]=z*z-_*_,C[y]=2*z*_}const p=Math.max(7,Math.round(i*.05)),w=Zt(M,e,i,p,2),E=Zt(C,e,i,p,2);ft=new Float32Array(e*i),dt=new Float32Array(e*i);for(let g=0;g<e*i;g++){if(!o[g])continue;const b=.5*Math.atan2(E[g],w[g]);ft[g]=Math.cos(b),dt[g]=Math.sin(b)}const $=Math.max(9,i*.04),q=new Float32Array(e*i),st=-.45,lt=-.62,xt=.64;for(let g=1;g<i-1;g++)for(let b=1;b<e-1;b++){const y=g*e+b;if(!o[y])continue;let z=F[y],_=v[y];const Vt=Math.hypot(z,_)||1;z/=Vt,_/=Vt;const Kt=m[y],Yt=(1-Math.min(Kt/$,1))*1.25;let vt=-z*Yt,Lt=-_*Yt;const Jt=1.5,$t=Math.hypot(vt,Lt,Jt);vt/=$t,Lt/=$t;let Pt=vt*st+Lt*lt+Jt/$t*xt;Pt<0&&(Pt=0);const se=Math.min(1,.45+.55*Math.min(Kt/($*.7),1));q[y]=Math.min(1.12,(.5+.75*Pt)*se)}St=q,k=document.createElement("canvas"),k.width=e,k.height=i;const B=k.getContext("2d");U=document.createElement("canvas"),U.width=e,U.height=i;const tt=U.getContext("2d");if(B&&tt){const g=B.createImageData(e,i),b=tt.createImageData(e,i);for(let y=0;y<e*i;y++)o[y]&&(g.data[y*4]=255,g.data[y*4+1]=255,g.data[y*4+2]=255,g.data[y*4+3]=255,b.data[y*4+3]=255);B.putImageData(g,0,0),tt.putImageData(b,0,0)}}function _t(){V=document.createElement("canvas"),V.width=e,V.height=i;const t=V.getContext("2d",{willReadFrequently:!0});if(t){const n=t.createRadialGradient(e*.5,i*.4,0,e*.5,i*.5,e*.72);n.addColorStop(0,j(pt)),n.addColorStop(1,j(H)),t.fillStyle=n,t.fillRect(0,0,e,i);const o=t.getImageData(0,0,e,i),d=o.data;for(let m=0;m<i;m++)for(let M=0;M<e;M++){const C=(m*e+M)*4,F=Math.sin(M*1.9)*.5+.5,v=Math.sin(m*1.9)*.5+.5,p=F*v*9-4.5+(Nt(M*.18,m*.18)-.5)*15;d[C]=D(d[C]+p),d[C+1]=D(d[C+1]+p),d[C+2]=D(d[C+2]+p*1.1)}t.putImageData(o,0,0)}at=document.createElement("canvas"),at.width=e,at.height=i,ut=at.getContext("2d",{willReadFrequently:!0})}function Ft(){const t=Math.max(1.6,et*.6);ht=t;const n=[];for(let o=0;o<i;o+=t)for(let d=0;d<e;d+=t){const m=(Z(Math.floor(d),Math.floor(o))-.5)*t*.9,M=(Z(Math.floor(o),Math.floor(d))-.5)*t*.9,C=Math.round(d+m),F=Math.round(o+M);if(C<1||F<1||C>=e-1||F>=i-1)continue;const v=F*e+C;if(!Ct[v])continue;const p=Math.atan2(dt[v],ft[v]),w=.86+Z(C*7,F*13)*.18;let E=St[v]*w;E=E<0?0:E>1.12?1.12:E,n.push({x:C,y:F,ang:p,idx:Math.min(19,Math.max(0,Math.round(E/1.12*19)))})}n.sort((o,d)=>o.x-d.x),X=n}function gt(t,n){t.save(),t.translate(n.x,n.y),t.rotate(n.ang),t.drawImage(nt[n.idx],-ot/2,-rt/2),t.restore()}function yt(){const t=L.getContext("2d");if(t)for(const n of X)gt(t,n)}function jt(){return X.length?X[X.length-1].x:0}function Et(){return X.length?X[0].x:0}function it(){const t=L.getContext("2d");!t||!V||(t.drawImage(V,0,0),U&&(t.save(),t.globalAlpha=.85,t.filter=`blur(${Math.round(i*.018)}px)`,t.drawImage(U,0,Math.round(i*.02)),t.restore()))}function ne(t){if(!t.length)return[];const n=Math.max(4,ht*1.5),o=new Map,d=(p,w)=>p+","+w;t.forEach((p,w)=>{const E=d(Math.floor(p.x/n),Math.floor(p.y/n));let $=o.get(E);$||o.set(E,$=[]),$.push(w)});const m=ht*1.8*(ht*1.8);function M(p){const w=t[p],E=Math.floor(w.x/n),$=Math.floor(w.y/n),q=[];for(let st=-1;st<=1;st++)for(let lt=-1;lt<=1;lt++){const xt=o.get(d(E+st,$+lt));if(xt)for(const B of xt){if(B===p)continue;const tt=t[B].x-w.x,g=t[B].y-w.y;tt*tt+g*g<=m&&q.push(B)}}return q}const C=new Uint8Array(t.length),F=[],v=t.map((p,w)=>w).sort((p,w)=>t[p].x+t[p].y-(t[w].x+t[w].y));for(const p of v){if(C[p])continue;let w=[p];for(C[p]=1;w.length;){F.push(w.map($=>t[$]));const E=[];for(const $ of w)for(const q of M($))C[q]||(C[q]=1,E.push(q));w=E}}return F}function It(t,n){it();const o=L.getContext("2d");if(!o)return;const d=[];for(const m of X)m.x<=t?gt(o,m):d.push(m);if(f){for(const m of d)gt(o,m);A.on=!1;return}A.rows=ne(d),A.idx=0,A.acc=0,A.rate=n||h,A.on=A.rows.length>0}function oe(t){const n=L.getContext("2d");if(n)for(t>A.rows.length&&(t=A.rows.length);A.idx<t;){const o=A.rows[A.idx++];for(const d of o)gt(n,d)}}function re(t,n){const o=N.getContext("2d");if(!o||(o.clearRect(0,0,e,i),!T||!k))return;const d=e*.32,m=o.createRadialGradient(t,n,0,t,n,d);m.addColorStop(0,"rgba(255,250,238,0.18)"),m.addColorStop(.5,"rgba(255,250,238,0.055)"),m.addColorStop(1,"rgba(255,250,238,0)"),o.fillStyle=m,o.fillRect(0,0,e,i),o.globalCompositeOperation="destination-in",o.drawImage(k,0,0),o.globalCompositeOperation="source-over"}function Xt(){const t=L.getContext("2d");if(!t){G=Math.round(i*.6),Mt=Math.round(e*.14);return}const n=a||"Ag";t.font=`${l} 100px ${c}`;const o=t.measureText(n).width||100;G=Math.round(100*(e*.72)/o),t.font=`${l} ${G}px ${c}`,Mt=Math.round(e/2-t.measureText(n).width/2)}function kt(t){if(J)return;mt||(mt=t);const n=Math.min(.25,(t-mt)/1e3);mt=t,A.on&&(A.acc+=A.rate*n,oe(A.acc),A.idx>=A.rows.length&&(A.on=!1)),Y&&k&&(re(K.set?K.x:e*.5,K.set?K.y:i*.34),Y=!1),Tt=requestAnimationFrame(kt)}function Bt(t){const n=N.getBoundingClientRect();K.x=(t.clientX-n.left)/(n.width||1)*e,K.y=(t.clientY-n.top)/(n.height||1)*i,K.set=!0,Y=!0}T&&window.addEventListener("pointermove",Bt,{passive:!0});function ae(t){bt(),Ft(),S&&!f?It(Et()-1,h):(it(),yt(),A.on=!1),Y=!0}function ie(){J||(qt(),_t(),et=Math.max(3.4,i*.0095),zt(),Xt(),ae(),At=!0,Ot||(Ot=!0,Tt=requestAnimationFrame(kt)))}let ct;function ce(t){const n=`${l} 200px ${I}`;let o=!1;try{o=!!document.fonts&&document.fonts.check(n)}catch{o=!0}if(!document.fonts||o){t();return}let d=!1;const m=()=>{d||(d=!0,ct&&clearTimeout(ct),t())};document.fonts.load(n,a).then(()=>document.fonts.ready).then(m,m),ct=setTimeout(m,1500)}ce(ie);let Rt;function Gt(){clearTimeout(Rt),Rt=setTimeout(()=>Ut.resize(),200)}window.addEventListener("resize",Gt);const Ut={get text(){return a},setText(t){if(J||!At){a=t??"";return}t=t??"";const n=t.length>a.length&&a.length>0&&t.startsWith(a),o=n?jt():-1;a=t,bt(),Ft(),f||!S?(it(),yt(),A.on=!1):It(n&&o>0?Math.min(o,jt()):Et()-1,n?320:h),Y=!0},replay(){J||!At||(f||!S?(it(),yt(),A.on=!1):It(Et()-1,h),Y=!0)},resize(){J||(qt(),_t(),et=Math.max(3.4,i*.0095),zt(),Xt(),bt(),Ft(),it(),yt(),A.on=!1,Y=!0)},destroy(){if(!J){J=!0,cancelAnimationFrame(Tt),clearTimeout(Rt),ct&&clearTimeout(ct),window.removeEventListener("resize",Gt),T&&window.removeEventListener("pointermove",Bt);for(const t of W)t.remove();nt=[],X=[],A.rows=[],Ct=new Uint8Array(0),St=new Float32Array(0),ft=new Float32Array(0),dt=new Float32Array(0),k=U=V=at=null,ut=null}}};return Ut}function te(x){const r=Q.useRef(null),a=Q.useRef(null),c=Q.useRef(x);c.current=x;const{text:l,font:s,weight:h,threadColor:T,fabricColor:S,pitch:I,animate:f,sewRate:u,sheen:R,reducedMotion:P,aspect:H}=x,pt=JSON.stringify({font:s,weight:h,threadColor:T,fabricColor:S,pitch:I,animate:f,sewRate:u,sheen:R,reducedMotion:P,aspect:H});return Q.useLayoutEffect(()=>{const j=r.current;if(!j)return;const W=Wt(j,c.current);a.current=W;let L=0,O=0,N;return typeof ResizeObserver<"u"&&(N=new ResizeObserver(e=>{const i=Math.round(e[0].contentRect.width);i!==O&&(O=i,cancelAnimationFrame(L),L=requestAnimationFrame(()=>{var G;return(G=a.current)==null?void 0:G.resize()}))}),N.observe(j)),()=>{N==null||N.disconnect(),cancelAnimationFrame(L),W.destroy(),a.current=null}},[pt]),Q.useEffect(()=>{var j;(j=a.current)==null||j.setText(l)},[l]),r}const ee=Q.forwardRef(function({className:r,style:a,as:c="div","aria-label":l,role:s,"aria-describedby":h,...T},S){const I=te(T),f=Q.useCallback(u=>{I.current=u,typeof S=="function"?S(u):S&&(S.current=u)},[I,S]);return le.jsx(c,{ref:f,className:r,style:a,role:s??"img","aria-label":l??T.text,"aria-describedby":h})});ee.displayName="ThreadText";exports.THREAD_TEXT_CLASSES=Dt;exports.ThreadText=ee;exports.createThreadText=Wt;exports.useThreadText=te;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const X=require("react"),xe=require("react/jsx-runtime"),pt={bg:"tt-bg",fx:"tt-fx",caret:"tt-caret"};function Q(m,e){let o=Math.imul(m|0,374761393)+Math.imul(e|0,668265263)|0;return o=Math.imul(o^o>>>13,1274126177),((o^o>>>16)>>>0)/4294967295}function Qt(m,e){const o=Math.floor(m),r=Math.floor(e),i=m-o,a=e-r,f=i*i*(3-2*i),A=a*a*(3-2*a),E=Q(o,r),C=Q(o+1,r),c=Q(o,r+1),d=Q(o+1,r+1);return E+(C-E)*f+(c-E)*A+(E-C-c+d)*f*A}const Mt=1e20;function Zt(m,e,o,r,i){let a=0;r[0]=0,i[0]=-Mt,i[1]=Mt;for(let f=1;f<e;f++){let A=(m[f]+f*f-(m[r[a]]+r[a]*r[a]))/(2*f-2*r[a]);for(;A<=i[a];)a--,A=(m[f]+f*f-(m[r[a]]+r[a]*r[a]))/(2*f-2*r[a]);a++,r[a]=f,i[a]=A,i[a+1]=Mt}a=0;for(let f=0;f<e;f++){for(;i[a+1]<f;)a++;o[f]=(f-r[a])*(f-r[a])+m[r[a]]}}function ge(m,e,o){const r=new Float64Array(e*o);for(let c=0;c<e*o;c++)r[c]=m[c]?Mt:0;const i=Math.max(e,o),a=new Float64Array(i),f=new Float64Array(i),A=new Int32Array(i),E=new Float64Array(i+1);for(let c=0;c<e;c++){for(let d=0;d<o;d++)a[d]=r[d*e+c];Zt(a,o,f,A,E);for(let d=0;d<o;d++)r[d*e+c]=f[d]}for(let c=0;c<o;c++){for(let d=0;d<e;d++)a[d]=r[c*e+d];Zt(a,e,f,A,E);for(let d=0;d<e;d++)r[c*e+d]=f[d]}const C=new Float32Array(e*o);for(let c=0;c<e*o;c++)C[c]=Math.sqrt(r[c]);return C}function Ht(m,e,o,r,i){let a=Float32Array.from(m);const f=new Float32Array(e*o),A=1/(2*r+1),E=(C,c)=>C<0?0:C>c?c:C;for(let C=0;C<i;C++){for(let c=0;c<o;c++){const d=c*e;let $=0;for(let B=-r;B<=r;B++)$+=a[d+E(B,e-1)];for(let B=0;B<e;B++)f[d+B]=$*A,$+=a[d+E(B+r+1,e-1)]-a[d+E(B-r,e-1)]}for(let c=0;c<e;c++){let d=0;for(let $=-r;$<=r;$++)d+=f[E($,o-1)*e+c];for(let $=0;$<o;$++)a[$*e+c]=d*A,d+=f[E($+r+1,o-1)*e+c]-f[E($-r,o-1)*e+c]}}return a}function Wt(m,e){if(!m)return e;const o=m.trim();if(o[0]==="#"){let i=o.slice(1);if(i.length===3&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]),i.length>=6){const a=parseInt(i.slice(0,2),16),f=parseInt(i.slice(2,4),16),A=parseInt(i.slice(4,6),16);if(!Number.isNaN(a)&&!Number.isNaN(f)&&!Number.isNaN(A))return[a,f,A]}return e}const r=o.match(/rgba?\(([^)]+)\)/i);if(r){const i=r[1].split(",").map(a=>parseFloat(a));if(i.length>=3&&i.slice(0,3).every(a=>!Number.isNaN(a)))return[Math.round(i[0]),Math.round(i[1]),Math.round(i[2])]}return e}function K(m){return m<0?0:m>255?255:Math.round(m)}function te(m){return(m.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,"")}function ee(m,e){if(typeof window>"u"||typeof document>"u"||!m)return{setText(){},replay(){},resize(){},update(){},focus(){},destroy(){},get text(){return e.text??""}};let o=e.text??"",r=e.font??"Georgia, serif",i=e.weight??680,a=e.fill??.9,f=e.sewRate??110,A=e.sheen??!0,E=e.animate??!0,C=e.editable??!1,c=e.pitch,d=e.onTextChange,$=te(r);const B=e.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);let D=Wt(e.threadColor??"#fffbf3",[255,251,243]),tt=[0,0,0],N=[0,0,0];function et(t){D=Wt(t,[255,251,243]),tt=[D[0]*.585,D[1]*.585,D[2]*.585],N=[D[0]*.47,D[1]*.47,D[2]*.47]}et(e.threadColor??"#fffbf3");const G=[];let P,g;m instanceof HTMLCanvasElement?(P=m,g=m.parentElement??m):(g=m,P=document.createElement("canvas"),G.push(P),g.appendChild(P)),P.classList.add(pt.bg);const O=document.createElement("canvas");G.push(O),O.classList.add(pt.fx),g.appendChild(O),getComputedStyle(g).position==="static"&&(g.style.position="relative"),P.style.display="block",P.style.width="100%",Object.assign(O.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let S=null,Z=!1,s=0,h=0,H=0,bt=0,ft=4,nt=[],ot=0,rt=0,Ct=new Uint8Array(0),Tt=new Float32Array(0),ut=new Float32Array(0),dt=new Float32Array(0),U=null,it=null,ht=null,mt=[],yt=3;const L={rows:[],idx:0,acc:0,rate:0,on:!1},Y={x:0,y:0,set:!1};let W=!0,vt=0,Bt=!1,xt=0,q=!1,J=!1;function re(){const t=g.getBoundingClientRect(),n=Math.max(1,Math.round(t.width||Math.min(window.innerWidth*.95,1240))),l=o||"Ag",y=P.getContext("2d");let T=100;y&&(y.font=`${i} 100px ${r}`,T=y.measureText(l).width||100);let b=Math.max(8,100*(n*a)/T),v=b*.7,R=b*.3,I=n*a;if(y){y.font=`${i} ${b}px ${r}`;const p=y.measureText(l);I=p.width||I,p.actualBoundingBoxAscent>0&&(v=p.actualBoundingBoxAscent),p.actualBoundingBoxDescent>0&&(R=p.actualBoundingBoxDescent)}const w=Math.max(1,Math.round(2*Math.max(v,R)*1.12));let u=Math.min(window.devicePixelRatio||1,1.7);for(;n*u*w*u>9e5&&u>.4;)u-=.1;s=Math.max(1,Math.round(n*u)),h=Math.max(1,Math.round(w*u)),H=Math.max(4,Math.round(b*u)),bt=Math.round(s/2-I*u/2);for(const p of[P,O])p.width=s,p.height=h,p.style.height=w+"px";g.style.height=w+"px",ft=c??Math.max(3,h*.0095)}function ie(){it=document.createElement("canvas"),it.width=s,it.height=h,ht=it.getContext("2d",{willReadFrequently:!0})}function Nt(){const t=Math.max(7,ft*2.5),n=Math.max(3.2,ft*1.3);ot=Math.ceil(t)+2,rt=Math.ceil(n)+2,nt=[];for(let l=0;l<20;l++){const y=.34+l/19*.82,T=document.createElement("canvas");T.width=ot,T.height=rt;const b=T.getContext("2d");if(!b){nt.push(T);continue}const v=ot/2,R=rt/2,I=b.createLinearGradient(0,R-n/2,0,R+n/2),w=(k,j)=>`rgba(${K(k[0]*y)},${K(k[1]*y)},${K(k[2]*y)},${j})`;I.addColorStop(0,w(N,0)),I.addColorStop(.16,w(tt,1)),I.addColorStop(.5,w(D,1)),I.addColorStop(.84,w(tt,1)),I.addColorStop(1,w(N,0)),b.fillStyle=I,b.beginPath();const u=n/2;b.moveTo(v-t/2+u,R-u),b.lineTo(v+t/2-u,R-u),b.arc(v+t/2-u,R,u,-Math.PI/2,Math.PI/2),b.lineTo(v-t/2+u,R+u),b.arc(v-t/2+u,R,u,Math.PI/2,-Math.PI/2),b.closePath(),b.fill(),b.globalCompositeOperation="destination-in";const p=b.createLinearGradient(v-t/2,0,v+t/2,0);p.addColorStop(0,"rgba(0,0,0,0)"),p.addColorStop(.22,"rgba(0,0,0,1)"),p.addColorStop(.78,"rgba(0,0,0,1)"),p.addColorStop(1,"rgba(0,0,0,0)"),b.fillStyle=p,b.fillRect(0,0,ot,rt),nt.push(T)}}function ae(){if(!ht)return;const t=ht;t.clearRect(0,0,s,h),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${i} ${H}px ${r}`,t.fillText(o,bt,h*.5);const n=t.getImageData(0,0,s,h).data,l=new Uint8Array(s*h);for(let x=0;x<h;x++)for(let M=0;M<s;M++){const F=x*s+M,_=n[F*4+3]/255,z=Qt(M*.5,x*.5)*.22+Qt(M*1.8,x*1.8)*.1;l[F]=_>.5+(z-.16)?1:0}Ct=l;const y=ge(l,s,h),T=new Float32Array(s*h);for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;T[F]=(y[F]*4+y[F-1]+y[F+1]+y[F-s]+y[F+s])/8}const b=new Float32Array(s*h),v=new Float32Array(s*h),R=new Float32Array(s*h),I=new Float32Array(s*h);for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;if(!l[F])continue;const _=(T[F+1]-T[F-1])*.5,z=(T[F+s]-T[F-s])*.5;R[F]=_,I[F]=z,b[F]=_*_-z*z,v[F]=2*_*z}const w=Math.max(7,Math.round(h*.05)),u=Ht(b,s,h,w,2),p=Ht(v,s,h,w,2);ut=new Float32Array(s*h),dt=new Float32Array(s*h);for(let x=0;x<s*h;x++){if(!l[x])continue;const M=.5*Math.atan2(p[x],u[x]);ut[x]=Math.cos(M),dt[x]=Math.sin(M)}const k=Math.max(9,h*.04),j=new Float32Array(s*h),ct=-.45,st=-.62,wt=.64;for(let x=1;x<h-1;x++)for(let M=1;M<s-1;M++){const F=x*s+M;if(!l[F])continue;let _=R[F],z=I[F];const Ut=Math.hypot(_,z)||1;_/=Ut,z/=Ut;const Vt=T[F],Yt=(1-Math.min(Vt/k,1))*1.25;let It=-_*Yt,Pt=-z*Yt;const Jt=1.5,Dt=Math.hypot(It,Pt,Jt);It/=Dt,Pt/=Dt;let kt=It*ct+Pt*st+Jt/Dt*wt;kt<0&&(kt=0);const ye=Math.min(1,.45+.55*Math.min(Vt/(k*.7),1));j[F]=Math.min(1.12,(.5+.75*kt)*ye)}Tt=j,U=document.createElement("canvas"),U.width=s,U.height=h;const V=U.getContext("2d");if(V){const x=V.createImageData(s,h);for(let M=0;M<s*h;M++)l[M]&&(x.data[M*4]=255,x.data[M*4+1]=255,x.data[M*4+2]=255,x.data[M*4+3]=255);V.putImageData(x,0,0)}}function le(){const t=Math.max(1.6,ft*.6);yt=t;const n=[];for(let l=0;l<h;l+=t)for(let y=0;y<s;y+=t){const T=(Q(Math.floor(y),Math.floor(l))-.5)*t*.9,b=(Q(Math.floor(l),Math.floor(y))-.5)*t*.9,v=Math.round(y+T),R=Math.round(l+b);if(v<1||R<1||v>=s-1||R>=h-1)continue;const I=R*s+v;if(!Ct[I])continue;const w=Math.atan2(dt[I],ut[I]),u=.86+Q(v*7,R*13)*.18;let p=Tt[I]*u;p=p<0?0:p>1.12?1.12:p,n.push({x:v,y:R,ang:w,idx:Math.min(19,Math.max(0,Math.round(p/1.12*19)))})}n.sort((l,y)=>l.x-y.x),mt=n}function Ot(t,n){t.save(),t.translate(n.x,n.y),t.rotate(n.ang),t.drawImage(nt[n.idx],-ot/2,-rt/2),t.restore()}function At(){const t=P.getContext("2d");if(t)for(const n of mt)Ot(t,n)}function Et(){const t=P.getContext("2d");t&&t.clearRect(0,0,s,h)}function ce(t){if(!t.length)return[];const n=Math.max(4,yt*1.5),l=new Map,y=(w,u)=>w+","+u;t.forEach((w,u)=>{const p=y(Math.floor(w.x/n),Math.floor(w.y/n));let k=l.get(p);k||l.set(p,k=[]),k.push(u)});const T=yt*1.8*(yt*1.8);function b(w){const u=t[w],p=Math.floor(u.x/n),k=Math.floor(u.y/n),j=[];for(let ct=-1;ct<=1;ct++)for(let st=-1;st<=1;st++){const wt=l.get(y(p+ct,k+st));if(wt)for(const V of wt){if(V===w)continue;const x=t[V].x-u.x,M=t[V].y-u.y;x*x+M*M<=T&&j.push(V)}}return j}const v=new Uint8Array(t.length),R=[],I=t.map((w,u)=>u).sort((w,u)=>t[w].x+t[w].y-(t[u].x+t[u].y));for(const w of I){if(v[w])continue;let u=[w];for(v[w]=1;u.length;){R.push(u.map(k=>t[k]));const p=[];for(const k of u)for(const j of b(k))v[j]||(v[j]=1,p.push(j));u=p}}return R}function se(){if(Et(),B||!E){At(),L.on=!1;return}L.rows=ce(mt),L.idx=0,L.acc=0,L.rate=f,L.on=L.rows.length>0}function fe(t){const n=P.getContext("2d");if(n)for(t>L.rows.length&&(t=L.rows.length);L.idx<t;){const l=L.rows[L.idx++];for(const y of l)Ot(n,y)}}function ue(t,n){const l=O.getContext("2d");if(!l||(l.clearRect(0,0,s,h),!A||!U))return;const y=s*.32,T=l.createRadialGradient(t,n,0,t,n,y);T.addColorStop(0,"rgba(255,250,238,0.18)"),T.addColorStop(.5,"rgba(255,250,238,0.055)"),T.addColorStop(1,"rgba(255,250,238,0)"),l.fillStyle=T,l.fillRect(0,0,s,h),l.globalCompositeOperation="destination-in",l.drawImage(U,0,0),l.globalCompositeOperation="source-over"}function de(){S||!C||(S=document.createElement("span"),S.className=pt.caret,S.setAttribute("aria-hidden","true"),Object.assign(S.style,{position:"absolute",pointerEvents:"none",borderRadius:"1px",opacity:"0",background:`rgb(${K(D[0])},${K(D[1])},${K(D[2])})`}),g.appendChild(S),G.push(S))}function St(){if(!C||!S)return;const t=P.getContext("2d");if(!t)return;t.font=`${i} ${H}px ${r}`;const n=t.measureText(o).width,l=(P.clientWidth||s)/s,y=H*.7;S.style.left=(bt+n+H*.05)*l+"px",S.style.top=.5*(P.clientHeight||h)-y*l/2+"px",S.style.height=y*l+"px",S.style.width=Math.max(2,Math.round(H*.02*l))+"px",S.style.background=`rgb(${K(D[0])},${K(D[1])},${K(D[2])})`}function jt(t){if(q)return;xt||(xt=t);const n=Math.min(.25,(t-xt)/1e3);xt=t,L.on&&(L.acc+=L.rate*n,fe(L.acc),L.idx>=L.rows.length&&(L.on=!1)),W&&U&&(ue(Y.set?Y.x:s*.5,Y.set?Y.y:h*.34),W=!1),S&&(S.style.opacity=C&&Z&&Math.floor(t/530)%2===0?"1":"0"),vt=requestAnimationFrame(jt)}function gt(t){const n=O.getBoundingClientRect();Y.x=(t.clientX-n.left)/(n.width||1)*s,Y.y=(t.clientY-n.top)/(n.height||1)*h,Y.set=!0,W=!0}function qt(t){if(!(!C||t.metaKey||t.ctrlKey||t.altKey)){if(t.key==="Backspace"){t.preventDefault(),Rt(o.slice(0,-1),!0);return}if(t.key==="Enter"){t.preventDefault(),$t.replay();return}[...t.key].length===1&&(t.preventDefault(),Rt(o+t.key,!0))}}function _t(){Z=!0}function zt(){Z=!1,S&&(S.style.opacity="0")}function Kt(){C&&g.focus()}function Ft(t){C=t,t?(g.tabIndex=0,g.setAttribute("role","textbox"),g.getAttribute("aria-label")||g.setAttribute("aria-label","Editable embroidered text — type to change it"),de(),g.addEventListener("keydown",qt),g.addEventListener("focus",_t),g.addEventListener("blur",zt),g.addEventListener("pointerdown",Kt),St()):(g.removeAttribute("tabindex"),g.removeAttribute("role"),g.removeEventListener("keydown",qt),g.removeEventListener("focus",_t),g.removeEventListener("blur",zt),g.removeEventListener("pointerdown",Kt),S&&(S.style.opacity="0"))}function he(t){if(t!==A)if(A=t,t)window.addEventListener("pointermove",gt,{passive:!0}),W=!0;else{window.removeEventListener("pointermove",gt);const n=O.getContext("2d");n&&n.clearRect(0,0,s,h)}}function at(t){re(),ie(),Nt(),ae(),le(),t?se():(Et(),At(),L.on=!1),St(),W=!0}function Rt(t,n){t=t??"",!(t===o&&J)&&(o=t,J&&(at(!1),n&&d&&d(o)))}let lt;function Xt(t,n,l){const y=`${n} 200px ${t}`;let T=!1;try{T=!!document.fonts&&document.fonts.check(y)}catch{T=!0}if(!document.fonts||T){l();return}let b=!1;const v=()=>{b||(b=!0,lt&&clearTimeout(lt),l())};document.fonts.load(y,o).then(()=>document.fonts.ready).then(v,v),lt=setTimeout(v,1500)}function me(){q||(at(!0),J=!0,Bt||(Bt=!0,vt=requestAnimationFrame(jt)))}C&&Ft(!0),A&&window.addEventListener("pointermove",gt,{passive:!0}),Xt($,i,me);let Lt;function Gt(){clearTimeout(Lt),Lt=setTimeout(()=>$t.resize(),200)}window.addEventListener("resize",Gt);const $t={get text(){return o},setText(t){if(!q){if(!J){o=t??"";return}Rt(t,!1)}},replay(){q||!J||at(!0)},resize(){q||!J||at(!1)},update(t){if(q)return;let n=!1,l=!1;t.font!==void 0&&t.font!==r&&(r=t.font,$=te(r),n=!0),t.weight!==void 0&&t.weight!==i&&(i=t.weight,n=!0),t.fill!==void 0&&t.fill!==a&&(a=t.fill,n=!0),t.pitch!==void 0&&t.pitch!==c&&(c=t.pitch,n=!0),t.threadColor!==void 0&&(et(t.threadColor),l=!0),t.sewRate!==void 0&&(f=t.sewRate),t.animate!==void 0&&(E=t.animate),t.onTextChange!==void 0&&(d=t.onTextChange),t.sheen!==void 0&&he(t.sheen),t.editable!==void 0&&t.editable!==C&&Ft(t.editable),J&&(n?Xt($,i,()=>{q||at(!1)}):l&&(Nt(),Et(),At(),St()),W=!0)},focus(){!q&&C&&g.focus()},destroy(){if(!q){q=!0,cancelAnimationFrame(vt),clearTimeout(Lt),lt&&clearTimeout(lt),window.removeEventListener("resize",Gt),window.removeEventListener("pointermove",gt),C&&Ft(!1);for(const t of G)t.remove();nt=[],mt=[],L.rows=[],Ct=new Uint8Array(0),Tt=new Float32Array(0),ut=new Float32Array(0),dt=new Float32Array(0),U=it=null,ht=null,S=null}}};return $t}function ne(m){const e=X.useRef(null),o=X.useRef(null),r=X.useRef(m);r.current=m;const i=X.useRef(m.onTextChange);i.current=m.onTextChange;const{text:a,font:f,weight:A,threadColor:E,pitch:C,fill:c,animate:d,sewRate:$,sheen:B,editable:D,reducedMotion:tt}=m;return X.useLayoutEffect(()=>{const N=e.current;if(!N)return;const et=ee(N,{...r.current,onTextChange:O=>{var S;return(S=i.current)==null?void 0:S.call(i,O)}});o.current=et;let G=0,P=0,g;return typeof ResizeObserver<"u"&&(g=new ResizeObserver(O=>{const S=Math.round(O[0].contentRect.width);S!==P&&(P=S,cancelAnimationFrame(G),G=requestAnimationFrame(()=>{var Z;return(Z=o.current)==null?void 0:Z.resize()}))}),g.observe(N)),()=>{g==null||g.disconnect(),cancelAnimationFrame(G),et.destroy(),o.current=null}},[tt]),X.useEffect(()=>{var N;(N=o.current)==null||N.update({font:f,weight:A,threadColor:E,pitch:C,fill:c,animate:d,sewRate:$,sheen:B,editable:D})},[f,A,E,C,c,d,$,B,D]),X.useEffect(()=>{var N;(N=o.current)==null||N.setText(a)},[a]),e}const oe=X.forwardRef(function({className:e,style:o,as:r="div","aria-label":i,role:a,"aria-describedby":f,...A},E){const C=ne(A),c=X.useCallback(d=>{C.current=d,typeof E=="function"?E(d):E&&(E.current=d)},[C,E]);return xe.jsx(r,{ref:c,className:e,style:o,role:a??"img","aria-label":i??A.text,"aria-describedby":f})});oe.displayName="ThreadText";exports.THREAD_TEXT_CLASSES=pt;exports.ThreadText=oe;exports.createThreadText=ee;exports.useThreadText=ne;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,10 +12,12 @@ export declare function createThreadText(target: HTMLElement, opts: ThreadTextOp
|
|
|
12
12
|
|
|
13
13
|
/** CSS class names applied to the canvases threadText creates inside a container. */
|
|
14
14
|
export declare const THREAD_TEXT_CLASSES: {
|
|
15
|
-
/** Base canvas:
|
|
15
|
+
/** Base canvas: the baked satin stitches (transparent ground). */
|
|
16
16
|
readonly bg: "tt-bg";
|
|
17
17
|
/** Overlay canvas: cursor sheen (`mix-blend-mode: screen`). */
|
|
18
18
|
readonly fx: "tt-fx";
|
|
19
|
+
/** Blinking text caret shown in editable mode. */
|
|
20
|
+
readonly caret: "tt-caret";
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -26,15 +28,19 @@ export declare const ThreadText: default_2.ForwardRefExoticComponent<ThreadTextP
|
|
|
26
28
|
|
|
27
29
|
/** Live handle to a mounted threadText renderer. */
|
|
28
30
|
export declare interface ThreadTextInstance {
|
|
29
|
-
/**
|
|
30
|
-
* Re-embroider with new text. When `text` extends the current word (an append),
|
|
31
|
-
* only the newly-added letters animate in; otherwise the whole word is re-sewn.
|
|
32
|
-
*/
|
|
31
|
+
/** Re-embroider with new text — re-fits to width and redraws instantly (no sew-in). */
|
|
33
32
|
setText(text: string): void;
|
|
34
|
-
/** Re-run the
|
|
33
|
+
/** Re-run the sew-in animation for the current word (when `animate` is on). */
|
|
35
34
|
replay(): void;
|
|
36
|
-
/** Re-fit the render surface to its container and
|
|
35
|
+
/** Re-fit the render surface to its container and redraw. */
|
|
37
36
|
resize(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Apply option changes live and redraw instantly — never re-runs the sew-in. Use for
|
|
39
|
+
* colour, font, weight, size (`fill`), sew rate, sheen, and editability changes.
|
|
40
|
+
*/
|
|
41
|
+
update(options: Partial<ThreadTextOptions>): void;
|
|
42
|
+
/** Focus the surface for typing (editable mode). */
|
|
43
|
+
focus(): void;
|
|
38
44
|
/** Cancel the animation loop, remove listeners, and free the created canvases. */
|
|
39
45
|
destroy(): void;
|
|
40
46
|
/** The current embroidered text. */
|
|
@@ -58,29 +64,36 @@ export declare interface ThreadTextOptions {
|
|
|
58
64
|
weight?: number;
|
|
59
65
|
/** Floss (thread) colour — the lit crest of each thread. (default: warm white '#fffbf3') */
|
|
60
66
|
threadColor?: string;
|
|
61
|
-
/** Woven-ground base colour — the outer edge of the fabric. (default near-black linen '#08080a') */
|
|
62
|
-
fabricColor?: string;
|
|
63
67
|
/**
|
|
64
68
|
* Thread spacing in internal pixels. Smaller = finer, denser stitching (and more work).
|
|
65
|
-
* (default: auto — derived from the
|
|
69
|
+
* (default: auto — derived from the fitted text height)
|
|
66
70
|
*/
|
|
67
71
|
pitch?: number;
|
|
68
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Fraction of the container width the word spans — the effective text size. The word is
|
|
74
|
+
* re-fitted to `fill × containerWidth` on load and resize, leaving the remainder as
|
|
75
|
+
* horizontal padding. Range ~0.3–1. (default: 0.9)
|
|
76
|
+
*/
|
|
77
|
+
fill?: number;
|
|
78
|
+
/** Play the sew-in animation on mount and on `replay()`. (default: true) */
|
|
69
79
|
animate?: boolean;
|
|
70
80
|
/** Satin cross-rows laid per second during the sew-in. (default: 110) */
|
|
71
81
|
sewRate?: number;
|
|
72
82
|
/** Enable the cursor-following radial sheen on the overlay canvas. (default: true) */
|
|
73
83
|
sheen?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Make the surface focusable and typeable — click (or Tab) to focus, then type to edit
|
|
86
|
+
* the word, Backspace to delete, Enter to replay the sew-in. Shows a blinking caret.
|
|
87
|
+
* (default: false)
|
|
88
|
+
*/
|
|
89
|
+
editable?: boolean;
|
|
90
|
+
/** Called with the new text whenever the user edits it directly (editable mode only). */
|
|
91
|
+
onTextChange?: (text: string) => void;
|
|
74
92
|
/**
|
|
75
93
|
* Force reduced-motion (skip the sew-in, draw instantly). If omitted, the value is
|
|
76
94
|
* auto-detected from `prefers-reduced-motion`.
|
|
77
95
|
*/
|
|
78
96
|
reducedMotion?: boolean;
|
|
79
|
-
/**
|
|
80
|
-
* Aspect ratio (height / width) of the render surface when the target is a plain
|
|
81
|
-
* container and its own canvases are created. (default: 0.46)
|
|
82
|
-
*/
|
|
83
|
-
aspect?: number;
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
declare interface ThreadTextProps extends ThreadTextOptions {
|
|
@@ -100,10 +113,11 @@ declare interface ThreadTextProps extends ThreadTextOptions {
|
|
|
100
113
|
/**
|
|
101
114
|
* React hook that mounts a threadText renderer inside a ref'd container.
|
|
102
115
|
*
|
|
103
|
-
* The instance is
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
116
|
+
* The instance is created once and kept alive; option changes (font, weight, colour,
|
|
117
|
+
* size, sew rate, sheen, editability) are applied live via `instance.update()` — no
|
|
118
|
+
* remount, no re-sew. Text changes route through `setText`. The surface re-fits to the
|
|
119
|
+
* container width via ResizeObserver, and tears down on unmount. Only `reducedMotion`
|
|
120
|
+
* (fixed at creation) forces a fresh instance.
|
|
107
121
|
*
|
|
108
122
|
* @param options - {@link ThreadTextOptions} for the renderer.
|
|
109
123
|
* @returns A ref to attach to the target container element.
|
package/dist/index.js
CHANGED
|
@@ -1,439 +1,493 @@
|
|
|
1
|
-
import { useRef as
|
|
2
|
-
import { jsx as
|
|
3
|
-
const
|
|
4
|
-
/** Base canvas:
|
|
1
|
+
import { useRef as wt, useLayoutEffect as me, useEffect as Qt, forwardRef as ye, useCallback as xe } from "react";
|
|
2
|
+
import { jsx as ge } from "react/jsx-runtime";
|
|
3
|
+
const Bt = {
|
|
4
|
+
/** Base canvas: the baked satin stitches (transparent ground). */
|
|
5
5
|
bg: "tt-bg",
|
|
6
6
|
/** Overlay canvas: cursor sheen (`mix-blend-mode: screen`). */
|
|
7
|
-
fx: "tt-fx"
|
|
7
|
+
fx: "tt-fx",
|
|
8
|
+
/** Blinking text caret shown in editable mode. */
|
|
9
|
+
caret: "tt-caret"
|
|
8
10
|
};
|
|
9
|
-
function
|
|
10
|
-
let
|
|
11
|
-
return
|
|
11
|
+
function J(m, e) {
|
|
12
|
+
let o = Math.imul(m | 0, 374761393) + Math.imul(e | 0, 668265263) | 0;
|
|
13
|
+
return o = Math.imul(o ^ o >>> 13, 1274126177), ((o ^ o >>> 16) >>> 0) / 4294967295;
|
|
12
14
|
}
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
return
|
|
15
|
+
function Zt(m, e) {
|
|
16
|
+
const o = Math.floor(m), r = Math.floor(e), i = m - o, a = e - r, f = i * i * (3 - 2 * i), A = a * a * (3 - 2 * a), E = J(o, r), C = J(o + 1, r), c = J(o, r + 1), u = J(o + 1, r + 1);
|
|
17
|
+
return E + (C - E) * f + (c - E) * A + (E - C - c + u) * f * A;
|
|
16
18
|
}
|
|
17
|
-
const
|
|
18
|
-
function
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
for (let
|
|
22
|
-
let A = (
|
|
23
|
-
for (; A <=
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
for (let
|
|
29
|
-
for (;
|
|
30
|
-
|
|
19
|
+
const pt = 1e20;
|
|
20
|
+
function Ht(m, e, o, r, i) {
|
|
21
|
+
let a = 0;
|
|
22
|
+
r[0] = 0, i[0] = -pt, i[1] = pt;
|
|
23
|
+
for (let f = 1; f < e; f++) {
|
|
24
|
+
let A = (m[f] + f * f - (m[r[a]] + r[a] * r[a])) / (2 * f - 2 * r[a]);
|
|
25
|
+
for (; A <= i[a]; )
|
|
26
|
+
a--, A = (m[f] + f * f - (m[r[a]] + r[a] * r[a])) / (2 * f - 2 * r[a]);
|
|
27
|
+
a++, r[a] = f, i[a] = A, i[a + 1] = pt;
|
|
28
|
+
}
|
|
29
|
+
a = 0;
|
|
30
|
+
for (let f = 0; f < e; f++) {
|
|
31
|
+
for (; i[a + 1] < f; ) a++;
|
|
32
|
+
o[f] = (f - r[a]) * (f - r[a]) + m[r[a]];
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
for (let
|
|
36
|
-
const
|
|
37
|
-
for (let
|
|
38
|
-
for (let u = 0; u <
|
|
39
|
-
|
|
40
|
-
for (let u = 0; u <
|
|
41
|
-
}
|
|
42
|
-
for (let
|
|
43
|
-
for (let u = 0; u <
|
|
44
|
-
|
|
45
|
-
for (let u = 0; u <
|
|
46
|
-
}
|
|
47
|
-
const
|
|
48
|
-
for (let
|
|
49
|
-
return
|
|
35
|
+
function we(m, e, o) {
|
|
36
|
+
const r = new Float64Array(e * o);
|
|
37
|
+
for (let c = 0; c < e * o; c++) r[c] = m[c] ? pt : 0;
|
|
38
|
+
const i = Math.max(e, o), a = new Float64Array(i), f = new Float64Array(i), A = new Int32Array(i), E = new Float64Array(i + 1);
|
|
39
|
+
for (let c = 0; c < e; c++) {
|
|
40
|
+
for (let u = 0; u < o; u++) a[u] = r[u * e + c];
|
|
41
|
+
Ht(a, o, f, A, E);
|
|
42
|
+
for (let u = 0; u < o; u++) r[u * e + c] = f[u];
|
|
43
|
+
}
|
|
44
|
+
for (let c = 0; c < o; c++) {
|
|
45
|
+
for (let u = 0; u < e; u++) a[u] = r[c * e + u];
|
|
46
|
+
Ht(a, e, f, A, E);
|
|
47
|
+
for (let u = 0; u < e; u++) r[c * e + u] = f[u];
|
|
48
|
+
}
|
|
49
|
+
const C = new Float32Array(e * o);
|
|
50
|
+
for (let c = 0; c < e * o; c++) C[c] = Math.sqrt(r[c]);
|
|
51
|
+
return C;
|
|
50
52
|
}
|
|
51
|
-
function
|
|
52
|
-
let
|
|
53
|
-
const
|
|
54
|
-
for (let
|
|
55
|
-
for (let
|
|
56
|
-
const u =
|
|
57
|
-
let
|
|
58
|
-
for (let
|
|
59
|
-
for (let
|
|
60
|
-
|
|
53
|
+
function Wt(m, e, o, r, i) {
|
|
54
|
+
let a = Float32Array.from(m);
|
|
55
|
+
const f = new Float32Array(e * o), A = 1 / (2 * r + 1), E = (C, c) => C < 0 ? 0 : C > c ? c : C;
|
|
56
|
+
for (let C = 0; C < i; C++) {
|
|
57
|
+
for (let c = 0; c < o; c++) {
|
|
58
|
+
const u = c * e;
|
|
59
|
+
let $ = 0;
|
|
60
|
+
for (let D = -r; D <= r; D++) $ += a[u + E(D, e - 1)];
|
|
61
|
+
for (let D = 0; D < e; D++)
|
|
62
|
+
f[u + D] = $ * A, $ += a[u + E(D + r + 1, e - 1)] - a[u + E(D - r, e - 1)];
|
|
61
63
|
}
|
|
62
|
-
for (let
|
|
64
|
+
for (let c = 0; c < e; c++) {
|
|
63
65
|
let u = 0;
|
|
64
|
-
for (let
|
|
65
|
-
for (let
|
|
66
|
-
|
|
66
|
+
for (let $ = -r; $ <= r; $++) u += f[E($, o - 1) * e + c];
|
|
67
|
+
for (let $ = 0; $ < o; $++)
|
|
68
|
+
a[$ * e + c] = u * A, u += f[E($ + r + 1, o - 1) * e + c] - f[E($ - r, o - 1) * e + c];
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
return
|
|
71
|
+
return a;
|
|
70
72
|
}
|
|
71
|
-
function
|
|
72
|
-
if (!
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
let
|
|
76
|
-
if (
|
|
77
|
-
const
|
|
78
|
-
if (!Number.isNaN(
|
|
73
|
+
function te(m, e) {
|
|
74
|
+
if (!m) return e;
|
|
75
|
+
const o = m.trim();
|
|
76
|
+
if (o[0] === "#") {
|
|
77
|
+
let i = o.slice(1);
|
|
78
|
+
if (i.length === 3 && (i = i[0] + i[0] + i[1] + i[1] + i[2] + i[2]), i.length >= 6) {
|
|
79
|
+
const a = parseInt(i.slice(0, 2), 16), f = parseInt(i.slice(2, 4), 16), A = parseInt(i.slice(4, 6), 16);
|
|
80
|
+
if (!Number.isNaN(a) && !Number.isNaN(f) && !Number.isNaN(A)) return [a, f, A];
|
|
79
81
|
}
|
|
80
|
-
return
|
|
82
|
+
return e;
|
|
81
83
|
}
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
86
|
-
return [Math.round(
|
|
84
|
+
const r = o.match(/rgba?\(([^)]+)\)/i);
|
|
85
|
+
if (r) {
|
|
86
|
+
const i = r[1].split(",").map((a) => parseFloat(a));
|
|
87
|
+
if (i.length >= 3 && i.slice(0, 3).every((a) => !Number.isNaN(a)))
|
|
88
|
+
return [Math.round(i[0]), Math.round(i[1]), Math.round(i[2])];
|
|
87
89
|
}
|
|
88
|
-
return
|
|
90
|
+
return e;
|
|
89
91
|
}
|
|
90
|
-
function
|
|
91
|
-
return
|
|
92
|
+
function K(m) {
|
|
93
|
+
return m < 0 ? 0 : m > 255 ? 255 : Math.round(m);
|
|
92
94
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
+
function ee(m) {
|
|
96
|
+
return (m.split(",")[0] || "serif").trim().replace(/^["']|["']$/g, "");
|
|
97
|
+
}
|
|
98
|
+
function pe(m, e) {
|
|
99
|
+
if (typeof window > "u" || typeof document > "u" || !m)
|
|
95
100
|
return { setText() {
|
|
96
101
|
}, replay() {
|
|
97
102
|
}, resize() {
|
|
103
|
+
}, update() {
|
|
104
|
+
}, focus() {
|
|
98
105
|
}, destroy() {
|
|
99
106
|
}, get text() {
|
|
100
|
-
return
|
|
107
|
+
return e.text ?? "";
|
|
101
108
|
} };
|
|
102
|
-
let
|
|
103
|
-
const
|
|
104
|
-
let
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
let o = e.text ?? "", r = e.font ?? "Georgia, serif", i = e.weight ?? 680, a = e.fill ?? 0.9, f = e.sewRate ?? 110, A = e.sheen ?? !0, E = e.animate ?? !0, C = e.editable ?? !1, c = e.pitch, u = e.onTextChange, $ = ee(r);
|
|
110
|
+
const D = e.reducedMotion ?? !!(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
111
|
+
let k = te(e.threadColor ?? "#fffbf3", [255, 251, 243]), W = [0, 0, 0], N = [0, 0, 0];
|
|
112
|
+
function tt(t) {
|
|
113
|
+
k = te(t, [255, 251, 243]), W = [k[0] * 0.585, k[1] * 0.585, k[2] * 0.585], N = [k[0] * 0.47, k[1] * 0.47, k[2] * 0.47];
|
|
114
|
+
}
|
|
115
|
+
tt(e.threadColor ?? "#fffbf3");
|
|
116
|
+
const G = [];
|
|
117
|
+
let P, g;
|
|
118
|
+
m instanceof HTMLCanvasElement ? (P = m, g = m.parentElement ?? m) : (g = m, P = document.createElement("canvas"), G.push(P), g.appendChild(P)), P.classList.add(Bt.bg);
|
|
119
|
+
const O = document.createElement("canvas");
|
|
120
|
+
G.push(O), O.classList.add(Bt.fx), g.appendChild(O), getComputedStyle(g).position === "static" && (g.style.position = "relative"), P.style.display = "block", P.style.width = "100%", Object.assign(O.style, { position: "absolute", inset: "0", width: "100%", pointerEvents: "none", mixBlendMode: "screen" });
|
|
121
|
+
let S = null, Q = !1, s = 0, h = 0, Z = 0, Mt = 0, st = 4, et = [], nt = 0, ot = 0, bt = new Uint8Array(0), Ct = new Float32Array(0), ft = new Float32Array(0), dt = new Float32Array(0), U = null, rt = null, ut = null, ht = [], mt = 3;
|
|
122
|
+
const R = { rows: [], idx: 0, acc: 0, rate: 0, on: !1 }, V = { x: 0, y: 0, set: !1 };
|
|
123
|
+
let H = !0, vt = 0, Dt = !1, yt = 0, z = !1, Y = !1;
|
|
124
|
+
function ne() {
|
|
125
|
+
const t = g.getBoundingClientRect(), n = Math.max(1, Math.round(t.width || Math.min(window.innerWidth * 0.95, 1240))), l = o || "Ag", y = P.getContext("2d");
|
|
126
|
+
let v = 100;
|
|
127
|
+
y && (y.font = `${i} 100px ${r}`, v = y.measureText(l).width || 100);
|
|
128
|
+
let b = Math.max(8, 100 * (n * a) / v), T = b * 0.7, L = b * 0.3, I = n * a;
|
|
129
|
+
if (y) {
|
|
130
|
+
y.font = `${i} ${b}px ${r}`;
|
|
131
|
+
const p = y.measureText(l);
|
|
132
|
+
I = p.width || I, p.actualBoundingBoxAscent > 0 && (T = p.actualBoundingBoxAscent), p.actualBoundingBoxDescent > 0 && (L = p.actualBoundingBoxDescent);
|
|
133
|
+
}
|
|
134
|
+
const w = Math.max(1, Math.round(2 * Math.max(T, L) * 1.12));
|
|
113
135
|
let d = Math.min(window.devicePixelRatio || 1, 1.7);
|
|
114
|
-
for (; n * d *
|
|
115
|
-
|
|
116
|
-
for (const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
function
|
|
121
|
-
|
|
136
|
+
for (; n * d * w * d > 9e5 && d > 0.4; ) d -= 0.1;
|
|
137
|
+
s = Math.max(1, Math.round(n * d)), h = Math.max(1, Math.round(w * d)), Z = Math.max(4, Math.round(b * d)), Mt = Math.round(s / 2 - I * d / 2);
|
|
138
|
+
for (const p of [P, O])
|
|
139
|
+
p.width = s, p.height = h, p.style.height = w + "px";
|
|
140
|
+
g.style.height = w + "px", st = c ?? Math.max(3, h * 95e-4);
|
|
141
|
+
}
|
|
142
|
+
function oe() {
|
|
143
|
+
rt = document.createElement("canvas"), rt.width = s, rt.height = h, ut = rt.getContext("2d", { willReadFrequently: !0 });
|
|
144
|
+
}
|
|
145
|
+
function Nt() {
|
|
146
|
+
const t = Math.max(7, st * 2.5), n = Math.max(3.2, st * 1.3);
|
|
122
147
|
nt = Math.ceil(t) + 2, ot = Math.ceil(n) + 2, et = [];
|
|
123
|
-
for (let
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
if (!
|
|
128
|
-
et.push(
|
|
148
|
+
for (let l = 0; l < 20; l++) {
|
|
149
|
+
const y = 0.34 + l / 19 * 0.82, v = document.createElement("canvas");
|
|
150
|
+
v.width = nt, v.height = ot;
|
|
151
|
+
const b = v.getContext("2d");
|
|
152
|
+
if (!b) {
|
|
153
|
+
et.push(v);
|
|
129
154
|
continue;
|
|
130
155
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
156
|
+
const T = nt / 2, L = ot / 2, I = b.createLinearGradient(0, L - n / 2, 0, L + n / 2), w = (B, j) => `rgba(${K(B[0] * y)},${K(B[1] * y)},${K(B[2] * y)},${j})`;
|
|
157
|
+
I.addColorStop(0, w(N, 0)), I.addColorStop(0.16, w(W, 1)), I.addColorStop(0.5, w(k, 1)), I.addColorStop(0.84, w(W, 1)), I.addColorStop(1, w(N, 0)), b.fillStyle = I, b.beginPath();
|
|
158
|
+
const d = n / 2;
|
|
159
|
+
b.moveTo(T - t / 2 + d, L - d), b.lineTo(T + t / 2 - d, L - d), b.arc(T + t / 2 - d, L, d, -Math.PI / 2, Math.PI / 2), b.lineTo(T - t / 2 + d, L + d), b.arc(T - t / 2 + d, L, d, Math.PI / 2, -Math.PI / 2), b.closePath(), b.fill(), b.globalCompositeOperation = "destination-in";
|
|
160
|
+
const p = b.createLinearGradient(T - t / 2, 0, T + t / 2, 0);
|
|
161
|
+
p.addColorStop(0, "rgba(0,0,0,0)"), p.addColorStop(0.22, "rgba(0,0,0,1)"), p.addColorStop(0.78, "rgba(0,0,0,1)"), p.addColorStop(1, "rgba(0,0,0,0)"), b.fillStyle = p, b.fillRect(0, 0, nt, ot), et.push(v);
|
|
137
162
|
}
|
|
138
163
|
}
|
|
139
|
-
function
|
|
140
|
-
if (!
|
|
141
|
-
const t =
|
|
142
|
-
t.clearRect(0, 0,
|
|
143
|
-
const n = t.getImageData(0, 0,
|
|
144
|
-
for (let
|
|
145
|
-
const
|
|
146
|
-
|
|
164
|
+
function re() {
|
|
165
|
+
if (!ut) return;
|
|
166
|
+
const t = ut;
|
|
167
|
+
t.clearRect(0, 0, s, h), t.fillStyle = "#fff", t.textAlign = "left", t.textBaseline = "middle", t.font = `${i} ${Z}px ${r}`, t.fillText(o, Mt, h * 0.5);
|
|
168
|
+
const n = t.getImageData(0, 0, s, h).data, l = new Uint8Array(s * h);
|
|
169
|
+
for (let x = 0; x < h; x++) for (let M = 0; M < s; M++) {
|
|
170
|
+
const F = x * s + M, q = n[F * 4 + 3] / 255, _ = Zt(M * 0.5, x * 0.5) * 0.22 + Zt(M * 1.8, x * 1.8) * 0.1;
|
|
171
|
+
l[F] = q > 0.5 + (_ - 0.16) ? 1 : 0;
|
|
147
172
|
}
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
for (let
|
|
151
|
-
const
|
|
152
|
-
|
|
173
|
+
bt = l;
|
|
174
|
+
const y = we(l, s, h), v = new Float32Array(s * h);
|
|
175
|
+
for (let x = 1; x < h - 1; x++) for (let M = 1; M < s - 1; M++) {
|
|
176
|
+
const F = x * s + M;
|
|
177
|
+
v[F] = (y[F] * 4 + y[F - 1] + y[F + 1] + y[F - s] + y[F + s]) / 8;
|
|
153
178
|
}
|
|
154
|
-
const
|
|
155
|
-
for (let
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
158
|
-
const q = (
|
|
159
|
-
|
|
179
|
+
const b = new Float32Array(s * h), T = new Float32Array(s * h), L = new Float32Array(s * h), I = new Float32Array(s * h);
|
|
180
|
+
for (let x = 1; x < h - 1; x++) for (let M = 1; M < s - 1; M++) {
|
|
181
|
+
const F = x * s + M;
|
|
182
|
+
if (!l[F]) continue;
|
|
183
|
+
const q = (v[F + 1] - v[F - 1]) * 0.5, _ = (v[F + s] - v[F - s]) * 0.5;
|
|
184
|
+
L[F] = q, I[F] = _, b[F] = q * q - _ * _, T[F] = 2 * q * _;
|
|
160
185
|
}
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
for (let
|
|
164
|
-
if (!
|
|
165
|
-
const
|
|
166
|
-
|
|
186
|
+
const w = Math.max(7, Math.round(h * 0.05)), d = Wt(b, s, h, w, 2), p = Wt(T, s, h, w, 2);
|
|
187
|
+
ft = new Float32Array(s * h), dt = new Float32Array(s * h);
|
|
188
|
+
for (let x = 0; x < s * h; x++) {
|
|
189
|
+
if (!l[x]) continue;
|
|
190
|
+
const M = 0.5 * Math.atan2(p[x], d[x]);
|
|
191
|
+
ft[x] = Math.cos(M), dt[x] = Math.sin(M);
|
|
167
192
|
}
|
|
168
|
-
const
|
|
169
|
-
for (let
|
|
170
|
-
const
|
|
171
|
-
if (!
|
|
172
|
-
let q =
|
|
173
|
-
const
|
|
174
|
-
q /=
|
|
175
|
-
const Vt =
|
|
176
|
-
let
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
let
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
193
|
+
const B = Math.max(9, h * 0.04), j = new Float32Array(s * h), lt = -0.45, ct = -0.62, gt = 0.64;
|
|
194
|
+
for (let x = 1; x < h - 1; x++) for (let M = 1; M < s - 1; M++) {
|
|
195
|
+
const F = x * s + M;
|
|
196
|
+
if (!l[F]) continue;
|
|
197
|
+
let q = L[F], _ = I[F];
|
|
198
|
+
const Xt = Math.hypot(q, _) || 1;
|
|
199
|
+
q /= Xt, _ /= Xt;
|
|
200
|
+
const Vt = v[F], Yt = (1 - Math.min(Vt / B, 1)) * 1.25;
|
|
201
|
+
let $t = -q * Yt, It = -_ * Yt;
|
|
202
|
+
const Jt = 1.5, Pt = Math.hypot($t, It, Jt);
|
|
203
|
+
$t /= Pt, It /= Pt;
|
|
204
|
+
let kt = $t * lt + It * ct + Jt / Pt * gt;
|
|
205
|
+
kt < 0 && (kt = 0);
|
|
206
|
+
const he = Math.min(1, 0.45 + 0.55 * Math.min(Vt / (B * 0.7), 1));
|
|
207
|
+
j[F] = Math.min(1.12, (0.5 + 0.75 * kt) * he);
|
|
183
208
|
}
|
|
184
|
-
Ct =
|
|
185
|
-
const X =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
for (let y = 0; y < e * i; y++) o[y] && (g.data[y * 4] = 255, g.data[y * 4 + 1] = 255, g.data[y * 4 + 2] = 255, g.data[y * 4 + 3] = 255, F.data[y * 4 + 3] = 255);
|
|
191
|
-
X.putImageData(g, 0, 0), W.putImageData(F, 0, 0);
|
|
209
|
+
Ct = j, U = document.createElement("canvas"), U.width = s, U.height = h;
|
|
210
|
+
const X = U.getContext("2d");
|
|
211
|
+
if (X) {
|
|
212
|
+
const x = X.createImageData(s, h);
|
|
213
|
+
for (let M = 0; M < s * h; M++) l[M] && (x.data[M * 4] = 255, x.data[M * 4 + 1] = 255, x.data[M * 4 + 2] = 255, x.data[M * 4 + 3] = 255);
|
|
214
|
+
X.putImageData(x, 0, 0);
|
|
192
215
|
}
|
|
193
216
|
}
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (t) {
|
|
198
|
-
const n = t.createRadialGradient(e * 0.5, i * 0.4, 0, e * 0.5, i * 0.5, e * 0.72);
|
|
199
|
-
n.addColorStop(0, j(wt)), n.addColorStop(1, j(Z)), t.fillStyle = n, t.fillRect(0, 0, e, i);
|
|
200
|
-
const o = t.getImageData(0, 0, e, i), d = o.data;
|
|
201
|
-
for (let m = 0; m < i; m++) for (let M = 0; M < e; M++) {
|
|
202
|
-
const C = (m * e + M) * 4, T = Math.sin(M * 1.9) * 0.5 + 0.5, R = Math.sin(m * 1.9) * 0.5 + 0.5, p = T * R * 9 - 4.5 + (Nt(M * 0.18, m * 0.18) - 0.5) * 15;
|
|
203
|
-
d[C] = D(d[C] + p), d[C + 1] = D(d[C + 1] + p), d[C + 2] = D(d[C + 2] + p * 1.1);
|
|
204
|
-
}
|
|
205
|
-
t.putImageData(o, 0, 0);
|
|
206
|
-
}
|
|
207
|
-
rt = document.createElement("canvas"), rt.width = e, rt.height = i, dt = rt.getContext("2d", { willReadFrequently: !0 });
|
|
208
|
-
}
|
|
209
|
-
function Ft() {
|
|
210
|
-
const t = Math.max(1.6, tt * 0.6);
|
|
211
|
-
ut = t;
|
|
217
|
+
function ie() {
|
|
218
|
+
const t = Math.max(1.6, st * 0.6);
|
|
219
|
+
mt = t;
|
|
212
220
|
const n = [];
|
|
213
|
-
for (let
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
const
|
|
217
|
-
if (!
|
|
218
|
-
const
|
|
219
|
-
let
|
|
220
|
-
|
|
221
|
+
for (let l = 0; l < h; l += t) for (let y = 0; y < s; y += t) {
|
|
222
|
+
const v = (J(Math.floor(y), Math.floor(l)) - 0.5) * t * 0.9, b = (J(Math.floor(l), Math.floor(y)) - 0.5) * t * 0.9, T = Math.round(y + v), L = Math.round(l + b);
|
|
223
|
+
if (T < 1 || L < 1 || T >= s - 1 || L >= h - 1) continue;
|
|
224
|
+
const I = L * s + T;
|
|
225
|
+
if (!bt[I]) continue;
|
|
226
|
+
const w = Math.atan2(dt[I], ft[I]), d = 0.86 + J(T * 7, L * 13) * 0.18;
|
|
227
|
+
let p = Ct[I] * d;
|
|
228
|
+
p = p < 0 ? 0 : p > 1.12 ? 1.12 : p, n.push({ x: T, y: L, ang: w, idx: Math.min(19, Math.max(0, Math.round(p / 1.12 * 19))) });
|
|
221
229
|
}
|
|
222
|
-
n.sort((
|
|
230
|
+
n.sort((l, y) => l.x - y.x), ht = n;
|
|
223
231
|
}
|
|
224
|
-
function
|
|
232
|
+
function Ot(t, n) {
|
|
225
233
|
t.save(), t.translate(n.x, n.y), t.rotate(n.ang), t.drawImage(et[n.idx], -nt / 2, -ot / 2), t.restore();
|
|
226
234
|
}
|
|
227
|
-
function gt() {
|
|
228
|
-
const t = $.getContext("2d");
|
|
229
|
-
if (t)
|
|
230
|
-
for (const n of k) mt(t, n);
|
|
231
|
-
}
|
|
232
|
-
function _t() {
|
|
233
|
-
return k.length ? k[k.length - 1].x : 0;
|
|
234
|
-
}
|
|
235
235
|
function Tt() {
|
|
236
|
-
|
|
236
|
+
const t = P.getContext("2d");
|
|
237
|
+
if (t)
|
|
238
|
+
for (const n of ht) Ot(t, n);
|
|
237
239
|
}
|
|
238
|
-
function
|
|
239
|
-
const t =
|
|
240
|
-
|
|
240
|
+
function At() {
|
|
241
|
+
const t = P.getContext("2d");
|
|
242
|
+
t && t.clearRect(0, 0, s, h);
|
|
241
243
|
}
|
|
242
|
-
function
|
|
244
|
+
function ae(t) {
|
|
243
245
|
if (!t.length) return [];
|
|
244
|
-
const n = Math.max(4,
|
|
245
|
-
t.forEach((
|
|
246
|
-
const
|
|
247
|
-
let
|
|
248
|
-
|
|
246
|
+
const n = Math.max(4, mt * 1.5), l = /* @__PURE__ */ new Map(), y = (w, d) => w + "," + d;
|
|
247
|
+
t.forEach((w, d) => {
|
|
248
|
+
const p = y(Math.floor(w.x / n), Math.floor(w.y / n));
|
|
249
|
+
let B = l.get(p);
|
|
250
|
+
B || l.set(p, B = []), B.push(d);
|
|
249
251
|
});
|
|
250
|
-
const
|
|
251
|
-
function
|
|
252
|
-
const
|
|
253
|
-
for (let
|
|
254
|
-
const
|
|
255
|
-
if (
|
|
256
|
-
for (const X of
|
|
257
|
-
if (X ===
|
|
258
|
-
const
|
|
259
|
-
|
|
252
|
+
const v = mt * 1.8 * (mt * 1.8);
|
|
253
|
+
function b(w) {
|
|
254
|
+
const d = t[w], p = Math.floor(d.x / n), B = Math.floor(d.y / n), j = [];
|
|
255
|
+
for (let lt = -1; lt <= 1; lt++) for (let ct = -1; ct <= 1; ct++) {
|
|
256
|
+
const gt = l.get(y(p + lt, B + ct));
|
|
257
|
+
if (gt)
|
|
258
|
+
for (const X of gt) {
|
|
259
|
+
if (X === w) continue;
|
|
260
|
+
const x = t[X].x - d.x, M = t[X].y - d.y;
|
|
261
|
+
x * x + M * M <= v && j.push(X);
|
|
260
262
|
}
|
|
261
263
|
}
|
|
262
|
-
return
|
|
264
|
+
return j;
|
|
263
265
|
}
|
|
264
|
-
const
|
|
265
|
-
for (const
|
|
266
|
-
if (
|
|
267
|
-
let
|
|
268
|
-
for (
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
for (const
|
|
272
|
-
|
|
266
|
+
const T = new Uint8Array(t.length), L = [], I = t.map((w, d) => d).sort((w, d) => t[w].x + t[w].y - (t[d].x + t[d].y));
|
|
267
|
+
for (const w of I) {
|
|
268
|
+
if (T[w]) continue;
|
|
269
|
+
let d = [w];
|
|
270
|
+
for (T[w] = 1; d.length; ) {
|
|
271
|
+
L.push(d.map((B) => t[B]));
|
|
272
|
+
const p = [];
|
|
273
|
+
for (const B of d) for (const j of b(B)) T[j] || (T[j] = 1, p.push(j));
|
|
274
|
+
d = p;
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
|
-
return
|
|
276
|
-
}
|
|
277
|
-
function
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (!o) return;
|
|
281
|
-
const d = [];
|
|
282
|
-
for (const m of k)
|
|
283
|
-
m.x <= t ? mt(o, m) : d.push(m);
|
|
284
|
-
if (f) {
|
|
285
|
-
for (const m of d) mt(o, m);
|
|
286
|
-
b.on = !1;
|
|
277
|
+
return L;
|
|
278
|
+
}
|
|
279
|
+
function le() {
|
|
280
|
+
if (At(), D || !E) {
|
|
281
|
+
Tt(), R.on = !1;
|
|
287
282
|
return;
|
|
288
283
|
}
|
|
289
|
-
|
|
284
|
+
R.rows = ae(ht), R.idx = 0, R.acc = 0, R.rate = f, R.on = R.rows.length > 0;
|
|
290
285
|
}
|
|
291
|
-
function
|
|
292
|
-
const n =
|
|
286
|
+
function ce(t) {
|
|
287
|
+
const n = P.getContext("2d");
|
|
293
288
|
if (n)
|
|
294
|
-
for (t >
|
|
295
|
-
const
|
|
296
|
-
for (const
|
|
289
|
+
for (t > R.rows.length && (t = R.rows.length); R.idx < t; ) {
|
|
290
|
+
const l = R.rows[R.idx++];
|
|
291
|
+
for (const y of l) Ot(n, y);
|
|
297
292
|
}
|
|
298
293
|
}
|
|
299
|
-
function
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
302
|
-
const
|
|
303
|
-
|
|
294
|
+
function se(t, n) {
|
|
295
|
+
const l = O.getContext("2d");
|
|
296
|
+
if (!l || (l.clearRect(0, 0, s, h), !A || !U)) return;
|
|
297
|
+
const y = s * 0.32, v = l.createRadialGradient(t, n, 0, t, n, y);
|
|
298
|
+
v.addColorStop(0, "rgba(255,250,238,0.18)"), v.addColorStop(0.5, "rgba(255,250,238,0.055)"), v.addColorStop(1, "rgba(255,250,238,0)"), l.fillStyle = v, l.fillRect(0, 0, s, h), l.globalCompositeOperation = "destination-in", l.drawImage(U, 0, 0), l.globalCompositeOperation = "source-over";
|
|
304
299
|
}
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
300
|
+
function fe() {
|
|
301
|
+
S || !C || (S = document.createElement("span"), S.className = Bt.caret, S.setAttribute("aria-hidden", "true"), Object.assign(S.style, { position: "absolute", pointerEvents: "none", borderRadius: "1px", opacity: "0", background: `rgb(${K(k[0])},${K(k[1])},${K(k[2])})` }), g.appendChild(S), G.push(S));
|
|
302
|
+
}
|
|
303
|
+
function Et() {
|
|
304
|
+
if (!C || !S) return;
|
|
305
|
+
const t = P.getContext("2d");
|
|
306
|
+
if (!t) return;
|
|
307
|
+
t.font = `${i} ${Z}px ${r}`;
|
|
308
|
+
const n = t.measureText(o).width, l = (P.clientWidth || s) / s, y = Z * 0.7;
|
|
309
|
+
S.style.left = (Mt + n + Z * 0.05) * l + "px", S.style.top = 0.5 * (P.clientHeight || h) - y * l / 2 + "px", S.style.height = y * l + "px", S.style.width = Math.max(2, Math.round(Z * 0.02 * l)) + "px", S.style.background = `rgb(${K(k[0])},${K(k[1])},${K(k[2])})`;
|
|
310
|
+
}
|
|
311
|
+
function jt(t) {
|
|
312
|
+
if (z) return;
|
|
313
|
+
yt || (yt = t);
|
|
314
|
+
const n = Math.min(0.25, (t - yt) / 1e3);
|
|
315
|
+
yt = t, R.on && (R.acc += R.rate * n, ce(R.acc), R.idx >= R.rows.length && (R.on = !1)), H && U && (se(V.set ? V.x : s * 0.5, V.set ? V.y : h * 0.34), H = !1), S && (S.style.opacity = C && Q && Math.floor(t / 530) % 2 === 0 ? "1" : "0"), vt = requestAnimationFrame(jt);
|
|
316
|
+
}
|
|
317
|
+
function xt(t) {
|
|
318
|
+
const n = O.getBoundingClientRect();
|
|
319
|
+
V.x = (t.clientX - n.left) / (n.width || 1) * s, V.y = (t.clientY - n.top) / (n.height || 1) * h, V.set = !0, H = !0;
|
|
320
|
+
}
|
|
321
|
+
function zt(t) {
|
|
322
|
+
if (!(!C || t.metaKey || t.ctrlKey || t.altKey)) {
|
|
323
|
+
if (t.key === "Backspace") {
|
|
324
|
+
t.preventDefault(), Ft(o.slice(0, -1), !0);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (t.key === "Enter") {
|
|
328
|
+
t.preventDefault(), Rt.replay();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
[...t.key].length === 1 && (t.preventDefault(), Ft(o + t.key, !0));
|
|
310
332
|
}
|
|
311
|
-
const n = a || "Ag";
|
|
312
|
-
t.font = `${l} 100px ${c}`;
|
|
313
|
-
const o = t.measureText(n).width || 100;
|
|
314
|
-
G = Math.round(100 * (e * 0.72) / o), t.font = `${l} ${G}px ${c}`, pt = Math.round(e / 2 - t.measureText(n).width / 2);
|
|
315
333
|
}
|
|
316
|
-
function
|
|
317
|
-
|
|
318
|
-
ht || (ht = t);
|
|
319
|
-
const n = Math.min(0.25, (t - ht) / 1e3);
|
|
320
|
-
ht = t, b.on && (b.acc += b.rate * n, te(b.acc), b.idx >= b.rows.length && (b.on = !1)), Y && B && (ee(K.set ? K.x : e * 0.5, K.set ? K.y : i * 0.34), Y = !1), St = requestAnimationFrame(kt);
|
|
334
|
+
function qt() {
|
|
335
|
+
Q = !0;
|
|
321
336
|
}
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
K.x = (t.clientX - n.left) / (n.width || 1) * e, K.y = (t.clientY - n.top) / (n.height || 1) * i, K.set = !0, Y = !0;
|
|
337
|
+
function _t() {
|
|
338
|
+
Q = !1, S && (S.style.opacity = "0");
|
|
325
339
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
bt(), Ft(), S && !f ? Et(Tt() - 1, h) : (at(), gt(), b.on = !1), Y = !0;
|
|
340
|
+
function Kt() {
|
|
341
|
+
C && g.focus();
|
|
329
342
|
}
|
|
330
|
-
function
|
|
331
|
-
|
|
343
|
+
function St(t) {
|
|
344
|
+
C = t, t ? (g.tabIndex = 0, g.setAttribute("role", "textbox"), g.getAttribute("aria-label") || g.setAttribute("aria-label", "Editable embroidered text — type to change it"), fe(), g.addEventListener("keydown", zt), g.addEventListener("focus", qt), g.addEventListener("blur", _t), g.addEventListener("pointerdown", Kt), Et()) : (g.removeAttribute("tabindex"), g.removeAttribute("role"), g.removeEventListener("keydown", zt), g.removeEventListener("focus", qt), g.removeEventListener("blur", _t), g.removeEventListener("pointerdown", Kt), S && (S.style.opacity = "0"));
|
|
332
345
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
346
|
+
function de(t) {
|
|
347
|
+
if (t !== A)
|
|
348
|
+
if (A = t, t)
|
|
349
|
+
window.addEventListener("pointermove", xt, { passive: !0 }), H = !0;
|
|
350
|
+
else {
|
|
351
|
+
window.removeEventListener("pointermove", xt);
|
|
352
|
+
const n = O.getContext("2d");
|
|
353
|
+
n && n.clearRect(0, 0, s, h);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function it(t) {
|
|
357
|
+
ne(), oe(), Nt(), re(), ie(), t ? le() : (At(), Tt(), R.on = !1), Et(), H = !0;
|
|
358
|
+
}
|
|
359
|
+
function Ft(t, n) {
|
|
360
|
+
t = t ?? "", !(t === o && Y) && (o = t, Y && (it(!1), n && u && u(o)));
|
|
361
|
+
}
|
|
362
|
+
let at;
|
|
363
|
+
function Gt(t, n, l) {
|
|
364
|
+
const y = `${n} 200px ${t}`;
|
|
365
|
+
let v = !1;
|
|
337
366
|
try {
|
|
338
|
-
|
|
367
|
+
v = !!document.fonts && document.fonts.check(y);
|
|
339
368
|
} catch {
|
|
340
|
-
|
|
369
|
+
v = !0;
|
|
341
370
|
}
|
|
342
|
-
if (!document.fonts ||
|
|
343
|
-
|
|
371
|
+
if (!document.fonts || v) {
|
|
372
|
+
l();
|
|
344
373
|
return;
|
|
345
374
|
}
|
|
346
|
-
let
|
|
347
|
-
const
|
|
348
|
-
|
|
375
|
+
let b = !1;
|
|
376
|
+
const T = () => {
|
|
377
|
+
b || (b = !0, at && clearTimeout(at), l());
|
|
349
378
|
};
|
|
350
|
-
document.fonts.load(
|
|
379
|
+
document.fonts.load(y, o).then(() => document.fonts.ready).then(T, T), at = setTimeout(T, 1500);
|
|
380
|
+
}
|
|
381
|
+
function ue() {
|
|
382
|
+
z || (it(!0), Y = !0, Dt || (Dt = !0, vt = requestAnimationFrame(jt)));
|
|
351
383
|
}
|
|
352
|
-
|
|
353
|
-
let
|
|
354
|
-
function
|
|
355
|
-
clearTimeout(
|
|
384
|
+
C && St(!0), A && window.addEventListener("pointermove", xt, { passive: !0 }), Gt($, i, ue);
|
|
385
|
+
let Lt;
|
|
386
|
+
function Ut() {
|
|
387
|
+
clearTimeout(Lt), Lt = setTimeout(() => Rt.resize(), 200);
|
|
356
388
|
}
|
|
357
|
-
window.addEventListener("resize",
|
|
358
|
-
const
|
|
389
|
+
window.addEventListener("resize", Ut);
|
|
390
|
+
const Rt = {
|
|
359
391
|
get text() {
|
|
360
|
-
return
|
|
392
|
+
return o;
|
|
361
393
|
},
|
|
362
394
|
setText(t) {
|
|
363
|
-
if (
|
|
364
|
-
|
|
365
|
-
|
|
395
|
+
if (!z) {
|
|
396
|
+
if (!Y) {
|
|
397
|
+
o = t ?? "";
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
Ft(t, !1);
|
|
366
401
|
}
|
|
367
|
-
t = t ?? "";
|
|
368
|
-
const n = t.length > a.length && a.length > 0 && t.startsWith(a), o = n ? _t() : -1;
|
|
369
|
-
a = t, bt(), Ft(), f || !S ? (at(), gt(), b.on = !1) : Et(n && o > 0 ? Math.min(o, _t()) : Tt() - 1, n ? 320 : h), Y = !0;
|
|
370
402
|
},
|
|
371
403
|
replay() {
|
|
372
|
-
|
|
404
|
+
z || !Y || it(!0);
|
|
373
405
|
},
|
|
374
406
|
resize() {
|
|
375
|
-
|
|
407
|
+
z || !Y || it(!1);
|
|
408
|
+
},
|
|
409
|
+
update(t) {
|
|
410
|
+
if (z) return;
|
|
411
|
+
let n = !1, l = !1;
|
|
412
|
+
t.font !== void 0 && t.font !== r && (r = t.font, $ = ee(r), n = !0), t.weight !== void 0 && t.weight !== i && (i = t.weight, n = !0), t.fill !== void 0 && t.fill !== a && (a = t.fill, n = !0), t.pitch !== void 0 && t.pitch !== c && (c = t.pitch, n = !0), t.threadColor !== void 0 && (tt(t.threadColor), l = !0), t.sewRate !== void 0 && (f = t.sewRate), t.animate !== void 0 && (E = t.animate), t.onTextChange !== void 0 && (u = t.onTextChange), t.sheen !== void 0 && de(t.sheen), t.editable !== void 0 && t.editable !== C && St(t.editable), Y && (n ? Gt($, i, () => {
|
|
413
|
+
z || it(!1);
|
|
414
|
+
}) : l && (Nt(), At(), Tt(), Et()), H = !0);
|
|
415
|
+
},
|
|
416
|
+
focus() {
|
|
417
|
+
!z && C && g.focus();
|
|
376
418
|
},
|
|
377
419
|
destroy() {
|
|
378
|
-
if (!
|
|
379
|
-
|
|
380
|
-
for (const t of
|
|
381
|
-
et = [],
|
|
420
|
+
if (!z) {
|
|
421
|
+
z = !0, cancelAnimationFrame(vt), clearTimeout(Lt), at && clearTimeout(at), window.removeEventListener("resize", Ut), window.removeEventListener("pointermove", xt), C && St(!1);
|
|
422
|
+
for (const t of G) t.remove();
|
|
423
|
+
et = [], ht = [], R.rows = [], bt = new Uint8Array(0), Ct = new Float32Array(0), ft = new Float32Array(0), dt = new Float32Array(0), U = rt = null, ut = null, S = null;
|
|
382
424
|
}
|
|
383
425
|
}
|
|
384
426
|
};
|
|
385
|
-
return
|
|
427
|
+
return Rt;
|
|
386
428
|
}
|
|
387
|
-
function
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
var
|
|
401
|
-
return (
|
|
429
|
+
function Me(m) {
|
|
430
|
+
const e = wt(null), o = wt(null), r = wt(m);
|
|
431
|
+
r.current = m;
|
|
432
|
+
const i = wt(m.onTextChange);
|
|
433
|
+
i.current = m.onTextChange;
|
|
434
|
+
const { text: a, font: f, weight: A, threadColor: E, pitch: C, fill: c, animate: u, sewRate: $, sheen: D, editable: k, reducedMotion: W } = m;
|
|
435
|
+
return me(() => {
|
|
436
|
+
const N = e.current;
|
|
437
|
+
if (!N) return;
|
|
438
|
+
const tt = pe(N, {
|
|
439
|
+
...r.current,
|
|
440
|
+
// Stable callback wrapper so typing edits always reach the latest handler.
|
|
441
|
+
onTextChange: (O) => {
|
|
442
|
+
var S;
|
|
443
|
+
return (S = i.current) == null ? void 0 : S.call(i, O);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
o.current = tt;
|
|
447
|
+
let G = 0, P = 0, g;
|
|
448
|
+
return typeof ResizeObserver < "u" && (g = new ResizeObserver((O) => {
|
|
449
|
+
const S = Math.round(O[0].contentRect.width);
|
|
450
|
+
S !== P && (P = S, cancelAnimationFrame(G), G = requestAnimationFrame(() => {
|
|
451
|
+
var Q;
|
|
452
|
+
return (Q = o.current) == null ? void 0 : Q.resize();
|
|
402
453
|
}));
|
|
403
|
-
}),
|
|
404
|
-
|
|
454
|
+
}), g.observe(N)), () => {
|
|
455
|
+
g == null || g.disconnect(), cancelAnimationFrame(G), tt.destroy(), o.current = null;
|
|
405
456
|
};
|
|
406
|
-
}, [
|
|
407
|
-
var
|
|
408
|
-
(
|
|
409
|
-
}, [
|
|
457
|
+
}, [W]), Qt(() => {
|
|
458
|
+
var N;
|
|
459
|
+
(N = o.current) == null || N.update({ font: f, weight: A, threadColor: E, pitch: C, fill: c, animate: u, sewRate: $, sheen: D, editable: k });
|
|
460
|
+
}, [f, A, E, C, c, u, $, D, k]), Qt(() => {
|
|
461
|
+
var N;
|
|
462
|
+
(N = o.current) == null || N.setText(a);
|
|
463
|
+
}, [a]), e;
|
|
410
464
|
}
|
|
411
|
-
const
|
|
412
|
-
function({ className:
|
|
413
|
-
const
|
|
465
|
+
const be = ye(
|
|
466
|
+
function({ className: e, style: o, as: r = "div", "aria-label": i, role: a, "aria-describedby": f, ...A }, E) {
|
|
467
|
+
const C = Me(A), c = xe(
|
|
414
468
|
(u) => {
|
|
415
|
-
|
|
469
|
+
C.current = u, typeof E == "function" ? E(u) : E && (E.current = u);
|
|
416
470
|
},
|
|
417
471
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
418
|
-
[
|
|
472
|
+
[C, E]
|
|
419
473
|
);
|
|
420
|
-
return /* @__PURE__ */
|
|
421
|
-
|
|
474
|
+
return /* @__PURE__ */ ge(
|
|
475
|
+
r,
|
|
422
476
|
{
|
|
423
|
-
ref:
|
|
424
|
-
className:
|
|
425
|
-
style:
|
|
426
|
-
role:
|
|
427
|
-
"aria-label":
|
|
428
|
-
"aria-describedby":
|
|
477
|
+
ref: c,
|
|
478
|
+
className: e,
|
|
479
|
+
style: o,
|
|
480
|
+
role: a ?? "img",
|
|
481
|
+
"aria-label": i ?? A.text,
|
|
482
|
+
"aria-describedby": f
|
|
429
483
|
}
|
|
430
484
|
);
|
|
431
485
|
}
|
|
432
486
|
);
|
|
433
|
-
|
|
487
|
+
be.displayName = "ThreadText";
|
|
434
488
|
export {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
489
|
+
Bt as THREAD_TEXT_CLASSES,
|
|
490
|
+
be as ThreadText,
|
|
491
|
+
pe as createThreadText,
|
|
492
|
+
Me as useThreadText
|
|
439
493
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var ThreadText=(function(at){"use strict";const Dt={bg:"tt-bg",fx:"tt-fx"};function B(s,n){let e=Math.imul(s|0,374761393)+Math.imul(n|0,668265263)|0;return e=Math.imul(e^e>>>13,1274126177),((e^e>>>16)>>>0)/4294967295}function xt(s,n){const e=Math.floor(s),o=Math.floor(n),r=s-e,d=n-o,m=r*r*(3-2*r),b=d*d*(3-2*d),E=B(e,o),L=B(e+1,o),u=B(e,o+1),g=B(e+1,o+1);return E+(L-E)*m+(u-E)*b+(E-L-u+g)*m*b}const it=1e20;function Ot(s,n,e,o,r){let d=0;o[0]=0,r[0]=-it,r[1]=it;for(let m=1;m<n;m++){let b=(s[m]+m*m-(s[o[d]]+o[d]*o[d]))/(2*m-2*o[d]);for(;b<=r[d];)d--,b=(s[m]+m*m-(s[o[d]]+o[d]*o[d]))/(2*m-2*o[d]);d++,o[d]=m,r[d]=b,r[d+1]=it}d=0;for(let m=0;m<n;m++){for(;r[d+1]<m;)d++;e[m]=(m-o[d])*(m-o[d])+s[o[d]]}}function ee(s,n,e){const o=new Float64Array(n*e);for(let u=0;u<n*e;u++)o[u]=s[u]?it:0;const r=Math.max(n,e),d=new Float64Array(r),m=new Float64Array(r),b=new Int32Array(r),E=new Float64Array(r+1);for(let u=0;u<n;u++){for(let g=0;g<e;g++)d[g]=o[g*n+u];Ot(d,e,m,b,E);for(let g=0;g<e;g++)o[g*n+u]=m[g]}for(let u=0;u<e;u++){for(let g=0;g<n;g++)d[g]=o[u*n+g];Ot(d,n,m,b,E);for(let g=0;g<n;g++)o[u*n+g]=m[g]}const L=new Float32Array(n*e);for(let u=0;u<n*e;u++)L[u]=Math.sqrt(o[u]);return L}function _t(s,n,e,o,r){let d=Float32Array.from(s);const m=new Float32Array(n*e),b=1/(2*o+1),E=(L,u)=>L<0?0:L>u?u:L;for(let L=0;L<r;L++){for(let u=0;u<e;u++){const g=u*n;let v=0;for(let $=-o;$<=o;$++)v+=d[g+E($,n-1)];for(let $=0;$<n;$++)m[g+$]=v*b,v+=d[g+E($+o+1,n-1)]-d[g+E($-o,n-1)]}for(let u=0;u<n;u++){let g=0;for(let v=-o;v<=o;v++)g+=m[E(v,e-1)*n+u];for(let v=0;v<e;v++)d[v*n+u]=g*b,g+=m[E(v+o+1,e-1)*n+u]-m[E(v-o,e-1)*n+u]}}return d}function qt(s,n){if(!s)return n;const e=s.trim();if(e[0]==="#"){let r=e.slice(1);if(r.length===3&&(r=r[0]+r[0]+r[1]+r[1]+r[2]+r[2]),r.length>=6){const d=parseInt(r.slice(0,2),16),m=parseInt(r.slice(2,4),16),b=parseInt(r.slice(4,6),16);if(!Number.isNaN(d)&&!Number.isNaN(m)&&!Number.isNaN(b))return[d,m,b]}return n}const o=e.match(/rgba?\(([^)]+)\)/i);if(o){const r=o[1].split(",").map(d=>parseFloat(d));if(r.length>=3&&r.slice(0,3).every(d=>!Number.isNaN(d)))return[Math.round(r[0]),Math.round(r[1]),Math.round(r[2])]}return n}function P(s){return s<0?0:s>255?255:Math.round(s)}function ne(s,n){if(typeof window>"u"||typeof document>"u"||!s)return{setText(){},replay(){},resize(){},destroy(){},get text(){return n.text??""}};let e=n.text??"";const o=n.font??"Georgia, serif",r=n.weight??680,d=n.aspect??.46,m=n.sewRate??110,b=n.sheen??!0,E=n.animate??!0,L=(o.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,""),u=n.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),g=qt(n.threadColor??"#fffbf3",[255,251,243]),v=[g[0]*.585,g[1]*.585,g[2]*.585],$=[g[0]*.47,g[1]*.47,g[2]*.47],st=qt(n.fabricColor??"#08080a",[8,8,10]),ce=[P(st[0]+14),P(st[1]+13),P(st[2]+15)],kt=t=>`rgb(${P(t[0])},${P(t[1])},${P(t[2])})`,Mt=[];let R,j;s instanceof HTMLCanvasElement?(R=s,j=s.parentElement??s):(j=s,R=document.createElement("canvas"),Mt.push(R),j.appendChild(R)),R.classList.add(Dt.bg);const X=document.createElement("canvas");Mt.push(X),X.classList.add(Dt.fx),j.appendChild(X),getComputedStyle(j).position==="static"&&(j.style.position="relative"),R.style.display="block",R.style.width="100%",Object.assign(X.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let c=0,l=0,lt=0,Ct=0,J=4,Q=[],Z=0,W=0,St=new Uint8Array(0),At=new Float32Array(0),ft=new Float32Array(0),dt=new Float32Array(0),k=null,G=null,U=null,tt=null,ut=null,q=[],ht=3;const S={rows:[],idx:0,acc:0,rate:0,on:!1},V={x:0,y:0,set:!1};let H=!0,Tt=0,zt=!1,mt=0,K=!1,Ft=!1;function Bt(){const t=j.getBoundingClientRect(),a=Math.round(t.width||Math.min(window.innerWidth*.95,1240)),i=Math.round(a*d);let f=Math.min(window.devicePixelRatio||1,1.7);for(;a*f*i*f>9e5&&f>.55;)f-=.1;c=Math.max(1,Math.round(a*f)),l=Math.max(1,Math.round(i*f));for(const h of[R,X])h.width=c,h.height=l,h.style.height=i+"px";j.style.height=i+"px"}function Xt(){const t=Math.max(7,J*2.5),a=Math.max(3.2,J*1.3);Z=Math.ceil(t)+2,W=Math.ceil(a)+2,Q=[];for(let i=0;i<20;i++){const f=.34+i/19*.82,h=document.createElement("canvas");h.width=Z,h.height=W;const M=h.getContext("2d");if(!M){Q.push(h);continue}const C=Z/2,T=W/2,I=M.createLinearGradient(0,T-a/2,0,T+a/2),p=(N,D)=>`rgba(${P(N[0]*f)},${P(N[1]*f)},${P(N[2]*f)},${D})`;I.addColorStop(0,p($,0)),I.addColorStop(.16,p(v,1)),I.addColorStop(.5,p(g,1)),I.addColorStop(.84,p(v,1)),I.addColorStop(1,p($,0)),M.fillStyle=I,M.beginPath();const x=a/2;M.moveTo(C-t/2+x,T-x),M.lineTo(C+t/2-x,T-x),M.arc(C+t/2-x,T,x,-Math.PI/2,Math.PI/2),M.lineTo(C-t/2+x,T+x),M.arc(C-t/2+x,T,x,Math.PI/2,-Math.PI/2),M.closePath(),M.fill(),M.globalCompositeOperation="destination-in";const F=M.createLinearGradient(C-t/2,0,C+t/2,0);F.addColorStop(0,"rgba(0,0,0,0)"),F.addColorStop(.22,"rgba(0,0,0,1)"),F.addColorStop(.78,"rgba(0,0,0,1)"),F.addColorStop(1,"rgba(0,0,0,0)"),M.fillStyle=F,M.fillRect(0,0,Z,W),Q.push(h)}}function bt(){if(!ut)return;const t=ut;t.clearRect(0,0,c,l),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${r} ${lt}px ${o}`,t.fillText(e,Ct,l*.5);const a=t.getImageData(0,0,c,l).data,i=new Uint8Array(c*l);for(let y=0;y<l;y++)for(let A=0;A<c;A++){const w=y*c+A,O=a[w*4+3]/255,_=xt(A*.5,y*.5)*.22+xt(A*1.8,y*1.8)*.1;i[w]=O>.5+(_-.16)?1:0}St=i;const f=ee(i,c,l),h=new Float32Array(c*l);for(let y=1;y<l-1;y++)for(let A=1;A<c-1;A++){const w=y*c+A;h[w]=(f[w]*4+f[w-1]+f[w+1]+f[w-c]+f[w+c])/8}const M=new Float32Array(c*l),C=new Float32Array(c*l),T=new Float32Array(c*l),I=new Float32Array(c*l);for(let y=1;y<l-1;y++)for(let A=1;A<c-1;A++){const w=y*c+A;if(!i[w])continue;const O=(h[w+1]-h[w-1])*.5,_=(h[w+c]-h[w-c])*.5;T[w]=O,I[w]=_,M[w]=O*O-_*_,C[w]=2*O*_}const p=Math.max(7,Math.round(l*.05)),x=_t(M,c,l,p,2),F=_t(C,c,l,p,2);ft=new Float32Array(c*l),dt=new Float32Array(c*l);for(let y=0;y<c*l;y++){if(!i[y])continue;const A=.5*Math.atan2(F[y],x[y]);ft[y]=Math.cos(A),dt[y]=Math.sin(A)}const N=Math.max(9,l*.04),D=new Float32Array(c*l),ot=-.45,rt=-.62,wt=.64;for(let y=1;y<l-1;y++)for(let A=1;A<c-1;A++){const w=y*c+A;if(!i[w])continue;let O=T[w],_=I[w];const Qt=Math.hypot(O,_)||1;O/=Qt,_/=Qt;const Zt=h[w],Wt=(1-Math.min(Zt/N,1))*1.25;let Lt=-O*Wt,Rt=-_*Wt;const te=1.5,Pt=Math.hypot(Lt,Rt,te);Lt/=Pt,Rt/=Pt;let $t=Lt*ot+Rt*rt+te/Pt*wt;$t<0&&($t=0);const me=Math.min(1,.45+.55*Math.min(Zt/(N*.7),1));D[w]=Math.min(1.12,(.5+.75*$t)*me)}At=D,k=document.createElement("canvas"),k.width=c,k.height=l;const z=k.getContext("2d");G=document.createElement("canvas"),G.width=c,G.height=l;const Y=G.getContext("2d");if(z&&Y){const y=z.createImageData(c,l),A=Y.createImageData(c,l);for(let w=0;w<c*l;w++)i[w]&&(y.data[w*4]=255,y.data[w*4+1]=255,y.data[w*4+2]=255,y.data[w*4+3]=255,A.data[w*4+3]=255);z.putImageData(y,0,0),Y.putImageData(A,0,0)}}function Gt(){U=document.createElement("canvas"),U.width=c,U.height=l;const t=U.getContext("2d",{willReadFrequently:!0});if(t){const a=t.createRadialGradient(c*.5,l*.4,0,c*.5,l*.5,c*.72);a.addColorStop(0,kt(ce)),a.addColorStop(1,kt(st)),t.fillStyle=a,t.fillRect(0,0,c,l);const i=t.getImageData(0,0,c,l),f=i.data;for(let h=0;h<l;h++)for(let M=0;M<c;M++){const C=(h*c+M)*4,T=Math.sin(M*1.9)*.5+.5,I=Math.sin(h*1.9)*.5+.5,p=T*I*9-4.5+(xt(M*.18,h*.18)-.5)*15;f[C]=P(f[C]+p),f[C+1]=P(f[C+1]+p),f[C+2]=P(f[C+2]+p*1.1)}t.putImageData(i,0,0)}tt=document.createElement("canvas"),tt.width=c,tt.height=l,ut=tt.getContext("2d",{willReadFrequently:!0})}function Et(){const t=Math.max(1.6,J*.6);ht=t;const a=[];for(let i=0;i<l;i+=t)for(let f=0;f<c;f+=t){const h=(B(Math.floor(f),Math.floor(i))-.5)*t*.9,M=(B(Math.floor(i),Math.floor(f))-.5)*t*.9,C=Math.round(f+h),T=Math.round(i+M);if(C<1||T<1||C>=c-1||T>=l-1)continue;const I=T*c+C;if(!St[I])continue;const p=Math.atan2(dt[I],ft[I]),x=.86+B(C*7,T*13)*.18;let F=At[I]*x;F=F<0?0:F>1.12?1.12:F,a.push({x:C,y:T,ang:p,idx:Math.min(19,Math.max(0,Math.round(F/1.12*19)))})}a.sort((i,f)=>i.x-f.x),q=a}function gt(t,a){t.save(),t.translate(a.x,a.y),t.rotate(a.ang),t.drawImage(Q[a.idx],-Z/2,-W/2),t.restore()}function yt(){const t=R.getContext("2d");if(t)for(const a of q)gt(t,a)}function Ut(){return q.length?q[q.length-1].x:0}function It(){return q.length?q[0].x:0}function et(){const t=R.getContext("2d");!t||!U||(t.drawImage(U,0,0),G&&(t.save(),t.globalAlpha=.85,t.filter=`blur(${Math.round(l*.018)}px)`,t.drawImage(G,0,Math.round(l*.02)),t.restore()))}function se(t){if(!t.length)return[];const a=Math.max(4,ht*1.5),i=new Map,f=(p,x)=>p+","+x;t.forEach((p,x)=>{const F=f(Math.floor(p.x/a),Math.floor(p.y/a));let N=i.get(F);N||i.set(F,N=[]),N.push(x)});const h=ht*1.8*(ht*1.8);function M(p){const x=t[p],F=Math.floor(x.x/a),N=Math.floor(x.y/a),D=[];for(let ot=-1;ot<=1;ot++)for(let rt=-1;rt<=1;rt++){const wt=i.get(f(F+ot,N+rt));if(wt)for(const z of wt){if(z===p)continue;const Y=t[z].x-x.x,y=t[z].y-x.y;Y*Y+y*y<=h&&D.push(z)}}return D}const C=new Uint8Array(t.length),T=[],I=t.map((p,x)=>x).sort((p,x)=>t[p].x+t[p].y-(t[x].x+t[x].y));for(const p of I){if(C[p])continue;let x=[p];for(C[p]=1;x.length;){T.push(x.map(N=>t[N]));const F=[];for(const N of x)for(const D of M(N))C[D]||(C[D]=1,F.push(D));x=F}}return T}function vt(t,a){et();const i=R.getContext("2d");if(!i)return;const f=[];for(const h of q)h.x<=t?gt(i,h):f.push(h);if(u){for(const h of f)gt(i,h);S.on=!1;return}S.rows=se(f),S.idx=0,S.acc=0,S.rate=a||m,S.on=S.rows.length>0}function le(t){const a=R.getContext("2d");if(a)for(t>S.rows.length&&(t=S.rows.length);S.idx<t;){const i=S.rows[S.idx++];for(const f of i)gt(a,f)}}function fe(t,a){const i=X.getContext("2d");if(!i||(i.clearRect(0,0,c,l),!b||!k))return;const f=c*.32,h=i.createRadialGradient(t,a,0,t,a,f);h.addColorStop(0,"rgba(255,250,238,0.18)"),h.addColorStop(.5,"rgba(255,250,238,0.055)"),h.addColorStop(1,"rgba(255,250,238,0)"),i.fillStyle=h,i.fillRect(0,0,c,l),i.globalCompositeOperation="destination-in",i.drawImage(k,0,0),i.globalCompositeOperation="source-over"}function Vt(){const t=R.getContext("2d");if(!t){lt=Math.round(l*.6),Ct=Math.round(c*.14);return}const a=e||"Ag";t.font=`${r} 100px ${o}`;const i=t.measureText(a).width||100;lt=Math.round(100*(c*.72)/i),t.font=`${r} ${lt}px ${o}`,Ct=Math.round(c/2-t.measureText(a).width/2)}function Ht(t){if(K)return;mt||(mt=t);const a=Math.min(.25,(t-mt)/1e3);mt=t,S.on&&(S.acc+=S.rate*a,le(S.acc),S.idx>=S.rows.length&&(S.on=!1)),H&&k&&(fe(V.set?V.x:c*.5,V.set?V.y:l*.34),H=!1),Tt=requestAnimationFrame(Ht)}function Kt(t){const a=X.getBoundingClientRect();V.x=(t.clientX-a.left)/(a.width||1)*c,V.y=(t.clientY-a.top)/(a.height||1)*l,V.set=!0,H=!0}b&&window.addEventListener("pointermove",Kt,{passive:!0});function de(t){bt(),Et(),E&&!u?vt(It()-1,m):(et(),yt(),S.on=!1),H=!0}function ue(){K||(Bt(),Gt(),J=Math.max(3.4,l*.0095),Xt(),Vt(),de(),Ft=!0,zt||(zt=!0,Tt=requestAnimationFrame(Ht)))}let nt;function he(t){const a=`${r} 200px ${L}`;let i=!1;try{i=!!document.fonts&&document.fonts.check(a)}catch{i=!0}if(!document.fonts||i){t();return}let f=!1;const h=()=>{f||(f=!0,nt&&clearTimeout(nt),t())};document.fonts.load(a,e).then(()=>document.fonts.ready).then(h,h),nt=setTimeout(h,1500)}he(ue);let Nt;function Yt(){clearTimeout(Nt),Nt=setTimeout(()=>Jt.resize(),200)}window.addEventListener("resize",Yt);const Jt={get text(){return e},setText(t){if(K||!Ft){e=t??"";return}t=t??"";const a=t.length>e.length&&e.length>0&&t.startsWith(e),i=a?Ut():-1;e=t,bt(),Et(),u||!E?(et(),yt(),S.on=!1):vt(a&&i>0?Math.min(i,Ut()):It()-1,a?320:m),H=!0},replay(){K||!Ft||(u||!E?(et(),yt(),S.on=!1):vt(It()-1,m),H=!0)},resize(){K||(Bt(),Gt(),J=Math.max(3.4,l*.0095),Xt(),Vt(),bt(),Et(),et(),yt(),S.on=!1,H=!0)},destroy(){if(!K){K=!0,cancelAnimationFrame(Tt),clearTimeout(Nt),nt&&clearTimeout(nt),window.removeEventListener("resize",Yt),b&&window.removeEventListener("pointermove",Kt);for(const t of Mt)t.remove();Q=[],q=[],S.rows=[],St=new Uint8Array(0),At=new Float32Array(0),ft=new Float32Array(0),dt=new Float32Array(0),k=G=U=tt=null,ut=null}}};return Jt}const oe="data-threadtext",pt=new WeakMap;function re(s,n){const e=s.dataset,o={text:e.ttText??n};if(e.ttFont&&(o.font=e.ttFont),e.ttWeight!==void 0){const r=parseFloat(e.ttWeight);isNaN(r)||(o.weight=r)}if(e.ttThreadColor&&(o.threadColor=e.ttThreadColor),e.ttFabricColor&&(o.fabricColor=e.ttFabricColor),e.ttPitch!==void 0){const r=parseFloat(e.ttPitch);isNaN(r)||(o.pitch=r)}if(e.ttSewRate!==void 0){const r=parseFloat(e.ttSewRate);isNaN(r)||(o.sewRate=r)}if(e.ttAspect!==void 0){const r=parseFloat(e.ttAspect);isNaN(r)||(o.aspect=r)}return e.ttSheen==="false"&&(o.sheen=!1),e.ttAnimate==="false"&&(o.animate=!1),o}function ae(s){jt(s);const n=s.innerHTML,e=(s.dataset.ttText??s.textContent??"").trim();if(!e)return;const o=re(s,e);s.setAttribute("role","img"),s.setAttribute("aria-label",e),s.innerHTML="";const r=ne(s,o);pt.set(s,{instance:r,originalHTML:n})}function jt(s){const n=pt.get(s);n&&(n.instance.destroy(),s.removeAttribute("role"),s.removeAttribute("aria-label"),s.innerHTML=n.originalHTML,pt.delete(s))}function ct(s=document){s.querySelectorAll(`[${oe}]`).forEach(ae)}function ie(){const s=()=>{var n;typeof document<"u"&&((n=document.fonts)!=null&&n.ready)?document.fonts.ready.then(()=>ct()).catch(()=>ct()):ct()};typeof document<"u"&&document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:!0}):s()}return ie(),at.destroy=jt,at.init=ct,Object.defineProperty(at,Symbol.toStringTag,{value:"Module"}),at})({});
|
|
1
|
+
var ThreadText=(function(rt){"use strict";const gt={bg:"tt-bg",fx:"tt-fx",caret:"tt-caret"};function U(a,n){let e=Math.imul(a|0,374761393)+Math.imul(n|0,668265263)|0;return e=Math.imul(e^e>>>13,1274126177),((e^e>>>16)>>>0)/4294967295}function Bt(a,n){const e=Math.floor(a),o=Math.floor(n),r=a-e,s=n-o,h=r*r*(3-2*r),F=s*s*(3-2*s),R=U(e,o),A=U(e+1,o),d=U(e,o+1),y=U(e+1,o+1);return R+(A-R)*h+(d-R)*F+(R-A-d+y)*h*F}const it=1e20;function Ot(a,n,e,o,r){let s=0;o[0]=0,r[0]=-it,r[1]=it;for(let h=1;h<n;h++){let F=(a[h]+h*h-(a[o[s]]+o[s]*o[s]))/(2*h-2*o[s]);for(;F<=r[s];)s--,F=(a[h]+h*h-(a[o[s]]+o[s]*o[s]))/(2*h-2*o[s]);s++,o[s]=h,r[s]=F,r[s+1]=it}s=0;for(let h=0;h<n;h++){for(;r[s+1]<h;)s++;e[h]=(h-o[s])*(h-o[s])+a[o[s]]}}function re(a,n,e){const o=new Float64Array(n*e);for(let d=0;d<n*e;d++)o[d]=a[d]?it:0;const r=Math.max(n,e),s=new Float64Array(r),h=new Float64Array(r),F=new Int32Array(r),R=new Float64Array(r+1);for(let d=0;d<n;d++){for(let y=0;y<e;y++)s[y]=o[y*n+d];Ot(s,e,h,F,R);for(let y=0;y<e;y++)o[y*n+d]=h[y]}for(let d=0;d<e;d++){for(let y=0;y<n;y++)s[y]=o[d*n+y];Ot(s,n,h,F,R);for(let y=0;y<n;y++)o[d*n+y]=h[y]}const A=new Float32Array(n*e);for(let d=0;d<n*e;d++)A[d]=Math.sqrt(o[d]);return A}function _t(a,n,e,o,r){let s=Float32Array.from(a);const h=new Float32Array(n*e),F=1/(2*o+1),R=(A,d)=>A<0?0:A>d?d:A;for(let A=0;A<r;A++){for(let d=0;d<e;d++){const y=d*n;let I=0;for(let k=-o;k<=o;k++)I+=s[y+R(k,n-1)];for(let k=0;k<n;k++)h[y+k]=I*F,I+=s[y+R(k+o+1,n-1)]-s[y+R(k-o,n-1)]}for(let d=0;d<n;d++){let y=0;for(let I=-o;I<=o;I++)y+=h[R(I,e-1)*n+d];for(let I=0;I<e;I++)s[I*n+d]=y*F,y+=h[R(I+o+1,e-1)*n+d]-h[R(I-o,e-1)*n+d]}}return s}function jt(a,n){if(!a)return n;const e=a.trim();if(e[0]==="#"){let r=e.slice(1);if(r.length===3&&(r=r[0]+r[0]+r[1]+r[1]+r[2]+r[2]),r.length>=6){const s=parseInt(r.slice(0,2),16),h=parseInt(r.slice(2,4),16),F=parseInt(r.slice(4,6),16);if(!Number.isNaN(s)&&!Number.isNaN(h)&&!Number.isNaN(F))return[s,h,F]}return n}const o=e.match(/rgba?\(([^)]+)\)/i);if(o){const r=o[1].split(",").map(s=>parseFloat(s));if(r.length>=3&&r.slice(0,3).every(s=>!Number.isNaN(s)))return[Math.round(r[0]),Math.round(r[1]),Math.round(r[2])]}return n}function q(a){return a<0?0:a>255?255:Math.round(a)}function qt(a){return(a.split(",")[0]||"serif").trim().replace(/^["']|["']$/g,"")}function ie(a,n){if(typeof window>"u"||typeof document>"u"||!a)return{setText(){},replay(){},resize(){},update(){},focus(){},destroy(){},get text(){return n.text??""}};let e=n.text??"",o=n.font??"Georgia, serif",r=n.weight??680,s=n.fill??.9,h=n.sewRate??110,F=n.sheen??!0,R=n.animate??!0,A=n.editable??!1,d=n.pitch,y=n.onTextChange,I=qt(o);const k=n.reducedMotion??!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);let D=jt(n.threadColor??"#fffbf3",[255,251,243]),pt=[0,0,0],Mt=[0,0,0];function zt(t){D=jt(t,[255,251,243]),pt=[D[0]*.585,D[1]*.585,D[2]*.585],Mt=[D[0]*.47,D[1]*.47,D[2]*.47]}zt(n.threadColor??"#fffbf3");const lt=[];let P,v;a instanceof HTMLCanvasElement?(P=a,v=a.parentElement??a):(v=a,P=document.createElement("canvas"),lt.push(P),v.appendChild(P)),P.classList.add(gt.bg);const K=document.createElement("canvas");lt.push(K),K.classList.add(gt.fx),v.appendChild(K),getComputedStyle(v).position==="static"&&(v.style.position="relative"),P.style.display="block",P.style.width="100%",Object.assign(K.style,{position:"absolute",inset:"0",width:"100%",pointerEvents:"none",mixBlendMode:"screen"});let L=null,Ct=!1,c=0,u=0,V=0,bt=0,st=4,J=[],Q=0,Z=0,vt=new Uint8Array(0),Tt=new Float32Array(0),ct=new Float32Array(0),ft=new Float32Array(0),z=null,W=null,dt=null,ut=[],ht=3;const E={rows:[],idx:0,acc:0,rate:0,on:!1},X={x:0,y:0,set:!1};let Y=!0,At=0,Gt=!1,mt=0,O=!1,H=!1;function fe(){const t=v.getBoundingClientRect(),i=Math.max(1,Math.round(t.width||Math.min(window.innerWidth*.95,1240))),l=e||"Ag",m=P.getContext("2d");let C=100;m&&(m.font=`${r} 100px ${o}`,C=m.measureText(l).width||100);let M=Math.max(8,100*(i*s)/C),b=M*.7,S=M*.3,N=i*s;if(m){m.font=`${r} ${M}px ${o}`;const w=m.measureText(l);N=w.width||N,w.actualBoundingBoxAscent>0&&(b=w.actualBoundingBoxAscent),w.actualBoundingBoxDescent>0&&(S=w.actualBoundingBoxDescent)}const g=Math.max(1,Math.round(2*Math.max(b,S)*1.12));let f=Math.min(window.devicePixelRatio||1,1.7);for(;i*f*g*f>9e5&&f>.4;)f-=.1;c=Math.max(1,Math.round(i*f)),u=Math.max(1,Math.round(g*f)),V=Math.max(4,Math.round(M*f)),bt=Math.round(c/2-N*f/2);for(const w of[P,K])w.width=c,w.height=u,w.style.height=g+"px";v.style.height=g+"px",st=d??Math.max(3,u*.0095)}function de(){W=document.createElement("canvas"),W.width=c,W.height=u,dt=W.getContext("2d",{willReadFrequently:!0})}function Ut(){const t=Math.max(7,st*2.5),i=Math.max(3.2,st*1.3);Q=Math.ceil(t)+2,Z=Math.ceil(i)+2,J=[];for(let l=0;l<20;l++){const m=.34+l/19*.82,C=document.createElement("canvas");C.width=Q,C.height=Z;const M=C.getContext("2d");if(!M){J.push(C);continue}const b=Q/2,S=Z/2,N=M.createLinearGradient(0,S-i/2,0,S+i/2),g=($,B)=>`rgba(${q($[0]*m)},${q($[1]*m)},${q($[2]*m)},${B})`;N.addColorStop(0,g(Mt,0)),N.addColorStop(.16,g(pt,1)),N.addColorStop(.5,g(D,1)),N.addColorStop(.84,g(pt,1)),N.addColorStop(1,g(Mt,0)),M.fillStyle=N,M.beginPath();const f=i/2;M.moveTo(b-t/2+f,S-f),M.lineTo(b+t/2-f,S-f),M.arc(b+t/2-f,S,f,-Math.PI/2,Math.PI/2),M.lineTo(b-t/2+f,S+f),M.arc(b-t/2+f,S,f,Math.PI/2,-Math.PI/2),M.closePath(),M.fill(),M.globalCompositeOperation="destination-in";const w=M.createLinearGradient(b-t/2,0,b+t/2,0);w.addColorStop(0,"rgba(0,0,0,0)"),w.addColorStop(.22,"rgba(0,0,0,1)"),w.addColorStop(.78,"rgba(0,0,0,1)"),w.addColorStop(1,"rgba(0,0,0,0)"),M.fillStyle=w,M.fillRect(0,0,Q,Z),J.push(C)}}function ue(){if(!dt)return;const t=dt;t.clearRect(0,0,c,u),t.fillStyle="#fff",t.textAlign="left",t.textBaseline="middle",t.font=`${r} ${V}px ${o}`,t.fillText(e,bt,u*.5);const i=t.getImageData(0,0,c,u).data,l=new Uint8Array(c*u);for(let x=0;x<u;x++)for(let p=0;p<c;p++){const T=x*c+p,_=i[T*4+3]/255,j=Bt(p*.5,x*.5)*.22+Bt(p*1.8,x*1.8)*.1;l[T]=_>.5+(j-.16)?1:0}vt=l;const m=re(l,c,u),C=new Float32Array(c*u);for(let x=1;x<u-1;x++)for(let p=1;p<c-1;p++){const T=x*c+p;C[T]=(m[T]*4+m[T-1]+m[T+1]+m[T-c]+m[T+c])/8}const M=new Float32Array(c*u),b=new Float32Array(c*u),S=new Float32Array(c*u),N=new Float32Array(c*u);for(let x=1;x<u-1;x++)for(let p=1;p<c-1;p++){const T=x*c+p;if(!l[T])continue;const _=(C[T+1]-C[T-1])*.5,j=(C[T+c]-C[T-c])*.5;S[T]=_,N[T]=j,M[T]=_*_-j*j,b[T]=2*_*j}const g=Math.max(7,Math.round(u*.05)),f=_t(M,c,u,g,2),w=_t(b,c,u,g,2);ct=new Float32Array(c*u),ft=new Float32Array(c*u);for(let x=0;x<c*u;x++){if(!l[x])continue;const p=.5*Math.atan2(w[x],f[x]);ct[x]=Math.cos(p),ft[x]=Math.sin(p)}const $=Math.max(9,u*.04),B=new Float32Array(c*u),nt=-.45,ot=-.62,xt=.64;for(let x=1;x<u-1;x++)for(let p=1;p<c-1;p++){const T=x*c+p;if(!l[T])continue;let _=S[T],j=N[T];const te=Math.hypot(_,j)||1;_/=te,j/=te;const ee=C[T],ne=(1-Math.min(ee/$,1))*1.25;let $t=-_*ne,Pt=-j*ne;const oe=1.5,Dt=Math.hypot($t,Pt,oe);$t/=Dt,Pt/=Dt;let kt=$t*nt+Pt*ot+oe/Dt*xt;kt<0&&(kt=0);const Ce=Math.min(1,.45+.55*Math.min(ee/($*.7),1));B[T]=Math.min(1.12,(.5+.75*kt)*Ce)}Tt=B,z=document.createElement("canvas"),z.width=c,z.height=u;const G=z.getContext("2d");if(G){const x=G.createImageData(c,u);for(let p=0;p<c*u;p++)l[p]&&(x.data[p*4]=255,x.data[p*4+1]=255,x.data[p*4+2]=255,x.data[p*4+3]=255);G.putImageData(x,0,0)}}function he(){const t=Math.max(1.6,st*.6);ht=t;const i=[];for(let l=0;l<u;l+=t)for(let m=0;m<c;m+=t){const C=(U(Math.floor(m),Math.floor(l))-.5)*t*.9,M=(U(Math.floor(l),Math.floor(m))-.5)*t*.9,b=Math.round(m+C),S=Math.round(l+M);if(b<1||S<1||b>=c-1||S>=u-1)continue;const N=S*c+b;if(!vt[N])continue;const g=Math.atan2(ft[N],ct[N]),f=.86+U(b*7,S*13)*.18;let w=Tt[N]*f;w=w<0?0:w>1.12?1.12:w,i.push({x:b,y:S,ang:g,idx:Math.min(19,Math.max(0,Math.round(w/1.12*19)))})}i.sort((l,m)=>l.x-m.x),ut=i}function Xt(t,i){t.save(),t.translate(i.x,i.y),t.rotate(i.ang),t.drawImage(J[i.idx],-Q/2,-Z/2),t.restore()}function St(){const t=P.getContext("2d");if(t)for(const i of ut)Xt(t,i)}function Et(){const t=P.getContext("2d");t&&t.clearRect(0,0,c,u)}function me(t){if(!t.length)return[];const i=Math.max(4,ht*1.5),l=new Map,m=(g,f)=>g+","+f;t.forEach((g,f)=>{const w=m(Math.floor(g.x/i),Math.floor(g.y/i));let $=l.get(w);$||l.set(w,$=[]),$.push(f)});const C=ht*1.8*(ht*1.8);function M(g){const f=t[g],w=Math.floor(f.x/i),$=Math.floor(f.y/i),B=[];for(let nt=-1;nt<=1;nt++)for(let ot=-1;ot<=1;ot++){const xt=l.get(m(w+nt,$+ot));if(xt)for(const G of xt){if(G===g)continue;const x=t[G].x-f.x,p=t[G].y-f.y;x*x+p*p<=C&&B.push(G)}}return B}const b=new Uint8Array(t.length),S=[],N=t.map((g,f)=>f).sort((g,f)=>t[g].x+t[g].y-(t[f].x+t[f].y));for(const g of N){if(b[g])continue;let f=[g];for(b[g]=1;f.length;){S.push(f.map($=>t[$]));const w=[];for(const $ of f)for(const B of M($))b[B]||(b[B]=1,w.push(B));f=w}}return S}function ye(){if(Et(),k||!R){St(),E.on=!1;return}E.rows=me(ut),E.idx=0,E.acc=0,E.rate=h,E.on=E.rows.length>0}function xe(t){const i=P.getContext("2d");if(i)for(t>E.rows.length&&(t=E.rows.length);E.idx<t;){const l=E.rows[E.idx++];for(const m of l)Xt(i,m)}}function ge(t,i){const l=K.getContext("2d");if(!l||(l.clearRect(0,0,c,u),!F||!z))return;const m=c*.32,C=l.createRadialGradient(t,i,0,t,i,m);C.addColorStop(0,"rgba(255,250,238,0.18)"),C.addColorStop(.5,"rgba(255,250,238,0.055)"),C.addColorStop(1,"rgba(255,250,238,0)"),l.fillStyle=C,l.fillRect(0,0,c,u),l.globalCompositeOperation="destination-in",l.drawImage(z,0,0),l.globalCompositeOperation="source-over"}function we(){L||!A||(L=document.createElement("span"),L.className=gt.caret,L.setAttribute("aria-hidden","true"),Object.assign(L.style,{position:"absolute",pointerEvents:"none",borderRadius:"1px",opacity:"0",background:`rgb(${q(D[0])},${q(D[1])},${q(D[2])})`}),v.appendChild(L),lt.push(L))}function Ft(){if(!A||!L)return;const t=P.getContext("2d");if(!t)return;t.font=`${r} ${V}px ${o}`;const i=t.measureText(e).width,l=(P.clientWidth||c)/c,m=V*.7;L.style.left=(bt+i+V*.05)*l+"px",L.style.top=.5*(P.clientHeight||u)-m*l/2+"px",L.style.height=m*l+"px",L.style.width=Math.max(2,Math.round(V*.02*l))+"px",L.style.background=`rgb(${q(D[0])},${q(D[1])},${q(D[2])})`}function Ht(t){if(O)return;mt||(mt=t);const i=Math.min(.25,(t-mt)/1e3);mt=t,E.on&&(E.acc+=E.rate*i,xe(E.acc),E.idx>=E.rows.length&&(E.on=!1)),Y&&z&&(ge(X.set?X.x:c*.5,X.set?X.y:u*.34),Y=!1),L&&(L.style.opacity=A&&Ct&&Math.floor(t/530)%2===0?"1":"0"),At=requestAnimationFrame(Ht)}function yt(t){const i=K.getBoundingClientRect();X.x=(t.clientX-i.left)/(i.width||1)*c,X.y=(t.clientY-i.top)/(i.height||1)*u,X.set=!0,Y=!0}function Vt(t){if(!(!A||t.metaKey||t.ctrlKey||t.altKey)){if(t.key==="Backspace"){t.preventDefault(),Nt(e.slice(0,-1),!0);return}if(t.key==="Enter"){t.preventDefault(),It.replay();return}[...t.key].length===1&&(t.preventDefault(),Nt(e+t.key,!0))}}function Yt(){Ct=!0}function Jt(){Ct=!1,L&&(L.style.opacity="0")}function Qt(){A&&v.focus()}function Lt(t){A=t,t?(v.tabIndex=0,v.setAttribute("role","textbox"),v.getAttribute("aria-label")||v.setAttribute("aria-label","Editable embroidered text — type to change it"),we(),v.addEventListener("keydown",Vt),v.addEventListener("focus",Yt),v.addEventListener("blur",Jt),v.addEventListener("pointerdown",Qt),Ft()):(v.removeAttribute("tabindex"),v.removeAttribute("role"),v.removeEventListener("keydown",Vt),v.removeEventListener("focus",Yt),v.removeEventListener("blur",Jt),v.removeEventListener("pointerdown",Qt),L&&(L.style.opacity="0"))}function pe(t){if(t!==F)if(F=t,t)window.addEventListener("pointermove",yt,{passive:!0}),Y=!0;else{window.removeEventListener("pointermove",yt);const i=K.getContext("2d");i&&i.clearRect(0,0,c,u)}}function tt(t){fe(),de(),Ut(),ue(),he(),t?ye():(Et(),St(),E.on=!1),Ft(),Y=!0}function Nt(t,i){t=t??"",!(t===e&&H)&&(e=t,H&&(tt(!1),i&&y&&y(e)))}let et;function Zt(t,i,l){const m=`${i} 200px ${t}`;let C=!1;try{C=!!document.fonts&&document.fonts.check(m)}catch{C=!0}if(!document.fonts||C){l();return}let M=!1;const b=()=>{M||(M=!0,et&&clearTimeout(et),l())};document.fonts.load(m,e).then(()=>document.fonts.ready).then(b,b),et=setTimeout(b,1500)}function Me(){O||(tt(!0),H=!0,Gt||(Gt=!0,At=requestAnimationFrame(Ht)))}A&&Lt(!0),F&&window.addEventListener("pointermove",yt,{passive:!0}),Zt(I,r,Me);let Rt;function Wt(){clearTimeout(Rt),Rt=setTimeout(()=>It.resize(),200)}window.addEventListener("resize",Wt);const It={get text(){return e},setText(t){if(!O){if(!H){e=t??"";return}Nt(t,!1)}},replay(){O||!H||tt(!0)},resize(){O||!H||tt(!1)},update(t){if(O)return;let i=!1,l=!1;t.font!==void 0&&t.font!==o&&(o=t.font,I=qt(o),i=!0),t.weight!==void 0&&t.weight!==r&&(r=t.weight,i=!0),t.fill!==void 0&&t.fill!==s&&(s=t.fill,i=!0),t.pitch!==void 0&&t.pitch!==d&&(d=t.pitch,i=!0),t.threadColor!==void 0&&(zt(t.threadColor),l=!0),t.sewRate!==void 0&&(h=t.sewRate),t.animate!==void 0&&(R=t.animate),t.onTextChange!==void 0&&(y=t.onTextChange),t.sheen!==void 0&&pe(t.sheen),t.editable!==void 0&&t.editable!==A&&Lt(t.editable),H&&(i?Zt(I,r,()=>{O||tt(!1)}):l&&(Ut(),Et(),St(),Ft()),Y=!0)},focus(){!O&&A&&v.focus()},destroy(){if(!O){O=!0,cancelAnimationFrame(At),clearTimeout(Rt),et&&clearTimeout(et),window.removeEventListener("resize",Wt),window.removeEventListener("pointermove",yt),A&&Lt(!1);for(const t of lt)t.remove();J=[],ut=[],E.rows=[],vt=new Uint8Array(0),Tt=new Float32Array(0),ct=new Float32Array(0),ft=new Float32Array(0),z=W=null,dt=null,L=null}}};return It}const ae="data-threadtext",wt=new WeakMap;function le(a,n){const e=a.dataset,o={text:e.ttText??n};if(e.ttFont&&(o.font=e.ttFont),e.ttWeight!==void 0){const r=parseFloat(e.ttWeight);isNaN(r)||(o.weight=r)}if(e.ttThreadColor&&(o.threadColor=e.ttThreadColor),e.ttPitch!==void 0){const r=parseFloat(e.ttPitch);isNaN(r)||(o.pitch=r)}if(e.ttFill!==void 0){const r=parseFloat(e.ttFill);isNaN(r)||(o.fill=r)}if(e.ttSewRate!==void 0){const r=parseFloat(e.ttSewRate);isNaN(r)||(o.sewRate=r)}return e.ttSheen==="false"&&(o.sheen=!1),e.ttAnimate==="false"&&(o.animate=!1),e.ttEditable==="true"&&(o.editable=!0),o}function se(a){Kt(a);const n=a.innerHTML,e=(a.dataset.ttText??a.textContent??"").trim();if(!e)return;const o=le(a,e);a.setAttribute("role","img"),a.setAttribute("aria-label",e),a.innerHTML="";const r=ie(a,o);wt.set(a,{instance:r,originalHTML:n})}function Kt(a){const n=wt.get(a);n&&(n.instance.destroy(),a.removeAttribute("role"),a.removeAttribute("aria-label"),a.innerHTML=n.originalHTML,wt.delete(a))}function at(a=document){a.querySelectorAll(`[${ae}]`).forEach(se)}function ce(){const a=()=>{var n;typeof document<"u"&&((n=document.fonts)!=null&&n.ready)?document.fonts.ready.then(()=>at()).catch(()=>at()):at()};typeof document<"u"&&document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a,{once:!0}):a()}return ce(),rt.destroy=Kt,rt.init=at,Object.defineProperty(rt,Symbol.toStringTag,{value:"Module"}),rt})({});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liiift-studio/threadtext",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Render any text as photorealistic procedural satin-stitch embroidery, in real time in the browser, from the font's glyph geometry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@types/react": "^19.0.0",
|
|
43
43
|
"@vitejs/plugin-react": "^4.0.0",
|
|
44
44
|
"happy-dom": "^20.10.6",
|
|
45
|
+
"next": "16.2.2",
|
|
45
46
|
"react": "^19.0.0",
|
|
46
47
|
"react-dom": "^19.0.0",
|
|
47
48
|
"typescript": "^5.0.0",
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
"homepage": "https://threadtext.com",
|
|
74
75
|
"repository": {
|
|
75
76
|
"type": "git",
|
|
76
|
-
"url": "https://github.com/Liiift-Studio/ThreadText.git"
|
|
77
|
+
"url": "git+https://github.com/Liiift-Studio/ThreadText.git"
|
|
77
78
|
},
|
|
78
79
|
"bugs": {
|
|
79
80
|
"url": "https://github.com/Liiift-Studio/ThreadText/issues"
|