cascading-reel 0.0.1 → 0.0.2
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/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.es.js +223 -177
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const D=6,a=3,u=3,U=34,B=.05,A=1200,F=1800,k=.075,z=34,$=[255,235,110],X=[255,255,255,.78],Q=190,M=650,Z=220;function w(t,i,e){return t<i?i:t>e?e:t}function m(t){return 1-(1-t)**3}function j(t){return Math.floor(Math.random()*t)}function E(t,i){return(t%i+i)%i}function p(t){return w(Math.round(t),0,255)}function V(t){return w(t,0,1)}function G(t){const i=[];for(let e=0;e<a;e+=1){const n=[];for(let r=0;r<u;r+=1)n.push(j(t));i.push(n)}return i}function v(t){const i=new Map;for(let l=0;l<a;l+=1)for(let s=0;s<u;s+=1){const o=t[l][s];i.set(o,(i.get(o)??0)+1)}let e=t[0][0],n=-1;for(const[l,s]of i.entries())s>n&&(n=s,e=l);const r=[];for(let l=0;l<a;l+=1)for(let s=0;s<u;s+=1)t[l][s]===e&&r.push({col:l,row:s});return r}function H(){return Array.from({length:a},()=>Array.from({length:u},()=>0))}function R(t,i){for(let e=0;e<a;e+=1)for(let n=0;n<u;n+=1)t[e][n]=i}class J{rafId=null;step=null;start(i){this.rafId===null&&(this.step=i,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=i=>{if(!this.step){this.stop();return}if(this.step(i)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}}function K(t,i,e){const n=[0,0,0];let r=0;for(let l=u-1;l>=0;l-=1){if(t[l]===0){n[l]=0;continue}n[l]=r;const s=Math.floor(i*B);r+=s+e}return n}function tt(t){let i=!0,e=!0;const r=t.height-t.boardY+t.cellH+2,l=[r,r,r],s=[-t.cellH,-t.cellH*2,-t.cellH*3],o=K(l,M,U),d=M-Z;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const f=t.now-(t.scriptedOutroStartedAt+h*Q);for(let c=0;c<u;c+=1){const g=f-o[c];if(g<=0){t.scriptedOutgoingOffsets[h][c]=0,t.scriptedIncomingOffsets[h][c]=Number.NaN,i=!1,e=!1;continue}const S=w(g/M,0,1),b=m(S);t.scriptedOutgoingOffsets[h][c]=r*b,S<1&&(i=!1);const W=g-d;if(W<=0){t.scriptedIncomingOffsets[h][c]=Number.NaN,e=!1;continue}const I=w(W/M,0,1),y=m(I);t.scriptedIncomingOffsets[h][c]=s[c]*(1-y),I<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function it(t){return t?[p(t[0]),p(t[1]),p(t[2])]:$}function et(t){return t==="rainbow"?"rainbow":"solid"}function nt(t){return t?[p(t[0]),p(t[1]),p(t[2]),V(t[3])]:X}function _(t){if(t.length!==u)throw new Error(`rows must contain ${u} rows`);for(let i=0;i<u;i+=1)if(!Array.isArray(t[i])||t[i].length!==a)throw new Error(`rows[${i}] must contain ${a} columns`);return[[t[0][0],t[1][0],t[2][0]],[t[0][1],t[1][1],t[2][1]],[t[0][2],t[1][2],t[2][2]]]}function P(t,i){if(t.length!==a)throw new Error(`stopGrid must contain ${a} columns`);const e=[];for(let n=0;n<a;n+=1){const r=t[n];if(!Array.isArray(r)||r.length!==u)throw new Error(`stopGrid[${n}] must contain ${u} rows`);e[n]=[E(r[0],i),E(r[1],i),E(r[2],i)]}return e}function st(t,i){return P(_(t),i)}function rt(t){return{stopGrid:t.stopGrid?.map(i=>[...i]),stopRows:t.stopRows?.map(i=>[...i]),finaleSequence:t.finaleSequence?.map(i=>i.map(e=>[...e])),finaleSequenceRows:t.finaleSequenceRows?.map(i=>i.map(e=>[...e])),callback:t.callback}}class lt{queue;constructor(i){this.queue=(i??[]).map(e=>rt(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function ot(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0}}function ct(t,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function ht(t,i){t.phase="idle",t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function N(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function ut(t){t.isSpinning=!1,t.queueFinished=!0}function dt(t){const i=t.columnSway??[0,0,0];for(let e=0;e<a;e+=1){const n=t.boardX+e*t.cellW,r=i[e]??0;for(let l=0;l<u;l+=1){if(t.phase==="winFlash"&&t.isWinningCell(e,l))continue;const s=t.offsets?t.offsets[e][l]:0;if(!Number.isFinite(s))continue;const o=t.boardY+l*t.cellH+s+r;o>t.height||o+t.cellH<0||t.drawSpriteCell(t.grid[e][l],n,o,t.cellW,t.cellH)}}}function C(t,i,e,n){const r=Math.sin(t*127.1+i*311.7+e*74.7+n*19.3)*43758.5453;return r-Math.floor(r)}function at(t){if(t.winningCells.length===0||t.phase!=="winFlash")return;const i=Math.max(0,t.now-t.winFlashStartedAt),e=i%A/A,n=i%F/F,r=1+Math.sin(n*Math.PI*2)*k,l=1-(1-e)**2;for(const s of t.winningCells){const o=t.boardX+s.col*t.cellW,d=t.boardY+s.row*t.cellH;ft(t.ctx,o,d,t.cellW,t.cellH,t.winningCellBorderRgba,i);const h=t.getCell(s.col,s.row),f=t.cellW*r,c=t.cellH*r,g=(t.cellW-f)*.5,S=(t.cellH-c)*.5;t.ctx.save(),t.drawSpriteCell(h,o+g,d+S,f,c),t.ctx.restore(),gt({ctx:t.ctx,cell:s,x:o,y:d,cycleProgress:e,easeOut:l,boardX:t.boardX,boardY:t.boardY,cellW:t.cellW,cellH:t.cellH,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}}function ft(t,i,e,n,r,l,s){const o=Math.max(1,Math.floor(Math.min(n,r)*.04)),d=Math.max(1,Math.floor(Math.min(n,r)*.025)),[,,,h]=l,f=s*.15%360;t.save(),t.lineWidth=d,t.strokeStyle=`hsla(${f}, 90%, 70%, ${h})`;const c=d*.5;t.strokeRect(i+o+c,e+o+c,n-o*2-d,r-o*2-d),t.restore()}function gt(t){const i=t.x+t.cellW*.5,e=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,r=Math.min(t.cellW,t.cellH)*.028,l=.92+(1-t.cycleProgress)*.08;t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*a,t.cellH*u),t.ctx.clip();for(let s=0;s<z;s+=1){const o=C(t.cell.col,t.cell.row,s,1),d=C(t.cell.col,t.cell.row,s,2),h=C(t.cell.col,t.cell.row,s,3),f=C(t.cell.col,t.cell.row,s,4)*.28,c=C(t.cell.col,t.cell.row,s,5),g=w((t.cycleProgress-f)/(1-f),0,1);if(g<=0)continue;const S=o*Math.PI*2,b=n*g*(.35+d*.65)*(.85+t.easeOut*.15),W=i+Math.cos(S)*b,I=e+Math.sin(S)*b,y=.7+.9*Math.max(0,Math.sin((t.cycleProgress*11+c*2)*Math.PI*2)),q=Math.max(1,r*(.55+h*.6)*(1-g*.5)),L=w((.9+y*.2)*l,.9,1);if(!(L<=0)){if(t.particleColorMode==="rainbow"){const x=(o*360+t.cycleProgress*240+t.cell.col*38+t.cell.row*22)%360;t.ctx.fillStyle=`hsla(${x}, 98%, 64%, ${L})`}else{const[x,Y,T]=t.particleColorRgb;t.ctx.fillStyle=`rgba(${x}, ${Y}, ${T}, ${L})`}t.ctx.beginPath(),t.ctx.arc(W,I,q,0,Math.PI*2),t.ctx.fill()}}t.ctx.restore()}class O{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;spriteImage=null;rafLoop=new J;runtime=ot();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutgoingOffsets=H();scriptedIncomingOffsets=H();winningCells=[];grid;constructor(i){this.canvas=i.canvas,this.container=i.container,this.button=i.button,this.spriteUrl=i.sprite,this.spriteElementsCount=Math.max(1,i.spriteElementsCount??D),this.highlightInitialWinningCells=i.highlightInitialWinningCells!==!1,this.spinQueueController=new lt(i.queuedSpinStates),this.particleColorRgb=it(i.particleColorRgb),this.particleColorMode=et(i.particleColorMode),this.winningCellBorderRgba=nt(i.winningCellBorderRgba);const e=this.canvas.getContext("2d");if(!e)throw new Error("2D canvas context is not available");this.ctx=e,this.grid=i.initialSegments?st(i.initialSegments,this.spriteElementsCount):G(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.startLoop()}destroy(){this.unbindEvents(),this.rafLoop.stop(),ut(this.runtime),this.clearWinningCells()}spin(){if(this.runtime.isSpinning||this.runtime.queueFinished)return;if(!this.spinQueueController.hasPending()){this.runtime.queueFinished=!0,this.button&&(this.button.disabled=!0);return}const i=this.spinQueueController.consume(),e=!this.spinQueueController.hasPending();ct(this.runtime,{activeSpinState:i,shouldHighlightCurrentSpin:e}),this.button&&(this.button.disabled=!0);const n=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(s=>_(s)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=n.map(s=>s.map(o=>[...o])),this.clearWinningCells();const r=this.runtime.activeSpinState?.stopRows?_(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,l=this.getNextGrid(r);this.startOutroTransition(l,performance.now())}bindEvents(){window.addEventListener("resize",this.resize),this.button?.addEventListener("click",this.onSpinClick)}unbindEvents(){window.removeEventListener("resize",this.resize),this.button?.removeEventListener("click",this.onSpinClick)}onSpinClick=()=>{this.runtime.isSpinning||this.spin()};getNextGrid(i){return i?P(i,this.spriteElementsCount):G(this.spriteElementsCount)}update(i){if(this.runtime.isSpinning){if(this.runtime.phase==="outro"){this.updateScriptedOutro(i);return}this.runtime.phase}}updateScriptedOutro(i){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}const{allOutgoingDone:e,allIncomingDone:n}=tt({now:i,height:this.height,boardY:this.boardY,cellH:this.cellH,scriptedOutroStartedAt:this.scriptedOutroStartedAt,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets});if(!(!e||!n)){if(!this.scriptedPendingGrid){this.finishSpinWithUi();return}if(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.clearWinningCells(),R(this.scriptedOutgoingOffsets,0),R(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=v(this.grid),N(this.runtime,i)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(P(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),R(this.scriptedIncomingOffsets,Number.NaN),R(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(){const i=this.runtime.activeSpinState?.callback;ht(this.runtime,this.spinQueueController.hasPending()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=v(this.grid),N(this.runtime,performance.now()))}static ZERO_SWAY=[0,0,0];render(i){this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,O.ZERO_SWAY),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,O.ZERO_SWAY)):this.drawBaseGrid(),at({ctx:this.ctx,now:i,phase:this.runtime.phase,winFlashStartedAt:this.runtime.winFlashStartedAt,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(){this.drawLayer(this.grid,null,O.ZERO_SWAY)}drawLayer(i,e,n){dt({ctx:this.ctx,phase:this.runtime.phase,grid:i,offsets:e,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,isWinningCell:this.isWinningCell,drawSpriteCell:this.drawSpriteCell})}getCell=(i,e)=>this.grid[i][e];isWinningCell=(i,e)=>this.winningCells.some(n=>n.col===i&&n.row===e);drawSpriteCell=(i,e,n,r,l)=>{const s=this.spriteImage;if(!s)return;const o=E(i,this.spriteElementsCount),d=s.height/this.spriteElementsCount,h=Math.floor(o*d),f=Math.floor(d);this.ctx.drawImage(s,0,h,s.width,f,e,n,r,l)};clearWinningCells(){this.winningCells=[]}resize=()=>{const i=this.container.getBoundingClientRect();this.dpr=Math.max(1,Math.min(window.devicePixelRatio||1,2));const e=Math.max(300,Math.floor(i.width*this.dpr));this.width=e,this.height=e;const n=Math.floor(Math.min(this.width/a,this.height/u));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*a)/2),this.boardY=Math.floor((this.height-this.cellH*u)/2),this.canvas.width=this.width,this.canvas.height=this.height};async loadSpriteIfProvided(){if(!this.spriteUrl)return;const i=new Image;i.decoding="async",i.src=this.spriteUrl;try{await i.decode(),this.spriteImage=i}catch{this.spriteImage=null}}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(i=>(this.update(i),this.render(i),!0))}}exports.CascadingReel=O;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const $=6,f=3,a=3,Q=34,V=.05,v=1200,m=1800,Z=.075,R=34,F=20,j=12,K=[255,235,110],J=[255,255,255,.78],tt=190,_=650,et=220;function b(t,e,i){return t<e?e:t>i?i:t}function H(t){return 1-(1-t)**3}function it(t){return Math.floor(Math.random()*t)}function A(t,e){return(t%e+e)%e}function O(t){return b(Math.round(t),0,255)}function nt(t){return b(t,0,1)}function G(t){const e=[];for(let i=0;i<f;i+=1){const n=[];for(let s=0;s<a;s+=1)n.push(it(t));e.push(n)}return e}function N(t){const e=new Map;for(let l=0;l<f;l+=1)for(let r=0;r<a;r+=1){const o=t[l][r];e.set(o,(e.get(o)??0)+1)}let i=t[0][0],n=-1;for(const[l,r]of e.entries())r>n&&(n=r,i=l);const s=[];for(let l=0;l<f;l+=1)for(let r=0;r<a;r+=1)t[l][r]===i&&s.push({col:l,row:r});return s}function T(){return Array.from({length:f},()=>Array.from({length:a},()=>0))}function I(t,e){for(let i=0;i<f;i+=1)for(let n=0;n<a;n+=1)t[i][n]=e}class st{rafId=null;step=null;start(e){this.rafId===null&&(this.step=e,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=e=>{if(!this.step){this.stop();return}if(this.step(e)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}}function lt(t,e,i){const n=[0,0,0];let s=0;for(let l=a-1;l>=0;l-=1){if(t[l]===0){n[l]=0;continue}n[l]=s;const r=Math.floor(e*V);s+=r+i}return n}function rt(t){let e=!0,i=!0;const s=t.height-t.boardY+t.cellH+2,l=[s,s,s],r=[-t.cellH,-t.cellH*2,-t.cellH*3],o=lt(l,_,Q),h=_-et;for(let c=0;c<t.scriptedOutgoingOffsets.length;c+=1){const d=t.now-(t.scriptedOutroStartedAt+c*tt);for(let u=0;u<a;u+=1){const g=d-o[u];if(g<=0){t.scriptedOutgoingOffsets[c][u]=0,t.scriptedIncomingOffsets[c][u]=Number.NaN,e=!1,i=!1;continue}const S=b(g/_,0,1),C=H(S);t.scriptedOutgoingOffsets[c][u]=s*C,S<1&&(e=!1);const w=g-h;if(w<=0){t.scriptedIncomingOffsets[c][u]=Number.NaN,i=!1;continue}const E=b(w/_,0,1),M=H(E);t.scriptedIncomingOffsets[c][u]=r[u]*(1-M),E<1&&(i=!1)}}return{allOutgoingDone:e,allIncomingDone:i}}function ot(t){return t?[O(t[0]),O(t[1]),O(t[2])]:K}function ct(t){return t==="rainbow"?"rainbow":"solid"}function ht(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function ut(t){return t?[O(t[0]),O(t[1]),O(t[2]),nt(t[3])]:J}function y(t){if(t.length!==a)throw new Error(`rows must contain ${a} rows`);for(let e=0;e<a;e+=1)if(!Array.isArray(t[e])||t[e].length!==f)throw new Error(`rows[${e}] must contain ${f} columns`);return[[t[0][0],t[1][0],t[2][0]],[t[0][1],t[1][1],t[2][1]],[t[0][2],t[1][2],t[2][2]]]}function L(t,e){if(t.length!==f)throw new Error(`stopGrid must contain ${f} columns`);const i=[];for(let n=0;n<f;n+=1){const s=t[n];if(!Array.isArray(s)||s.length!==a)throw new Error(`stopGrid[${n}] must contain ${a} rows`);i[n]=[A(s[0],e),A(s[1],e),A(s[2],e)]}return i}function dt(t,e){return L(y(t),e)}function at(t){return{stopGrid:t.stopGrid?.map(e=>[...e]),stopRows:t.stopRows?.map(e=>[...e]),finaleSequence:t.finaleSequence?.map(e=>e.map(i=>[...i])),finaleSequenceRows:t.finaleSequenceRows?.map(e=>e.map(i=>[...i])),callback:t.callback}}class ft{queue;constructor(e){this.queue=(e??[]).map(i=>at(i))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function gt(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,swayEnvelope:1,winEffectsEnvelope:1}}function St(t,e){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=e.startedAt,t.activeSpinState=e.activeSpinState,t.shouldHighlightCurrentSpin=e.shouldHighlightCurrentSpin}function pt(t,e,i){t.phase="idle",t.idleStartedAt=i,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!e,t.activeSpinState=null}function q(t,e){t.winFlashStartedAt=e,t.phase="winFlash"}function Ct(t){t.isSpinning=!1,t.queueFinished=!0}const wt=3,Et=.002;function Ot(t){const e=t.columnSway??[0,0,0],i=wt*(Math.PI/180),n=Math.max(0,Math.min(1,t.swayEnvelope));for(let s=0;s<f;s+=1){const l=t.boardX+s*t.cellW,r=e[s]??0;for(let o=0;o<a;o+=1){if((t.phase==="winFlash"||t.phase==="preSpin")&&t.isWinningCell(s,o))continue;const h=t.offsets?t.offsets[s][o]:0;if(!Number.isFinite(h))continue;const c=t.boardY+o*t.cellH+h+r;if(c>t.height||c+t.cellH<0)continue;const u=Math.sin(t.now*Et+s*1.1+o*1.3)*i*n,g=l+t.cellW*.5,S=c+t.cellH*.5;t.ctx.save(),t.ctx.translate(g,S),t.ctx.rotate(u),t.ctx.translate(-t.cellW*.5,-t.cellH*.5),t.drawSpriteCell(t.grid[s][o],0,0,t.cellW,t.cellH),t.ctx.restore()}}}function W(t,e,i,n){const s=Math.sin(t*127.1+e*311.7+i*74.7+n*19.3)*43758.5453;return s-Math.floor(s)}function bt(t){if(t.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const e=Math.max(0,Math.min(1,t.winEffectsEnvelope)),i=Math.max(0,t.now-t.winFlashStartedAt),n=i%v/v,s=i%m/m,l=1+Math.sin(s*Math.PI*2)*Z,r=1-(1-n)**2;for(const o of t.winningCells){const h=t.boardX+o.col*t.cellW,c=t.boardY+o.row*t.cellH;Wt(t.ctx,h,c,t.cellW,t.cellH,t.winningCellBorderRgba,i,e);const d=t.getCell(o.col,o.row),u=1+(l-1)*e,g=t.cellW*u,S=t.cellH*u,C=(t.cellW-g)*.5,w=(t.cellH-S)*.5;t.ctx.save(),t.drawSpriteCell(d,h+C,c+w,g,S),t.ctx.restore()}if(!(t.particlesPerCell<=0)){t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*f,t.cellH*a),t.ctx.clip();for(const o of t.winningCells){const h=t.boardX+o.col*t.cellW,c=t.boardY+o.row*t.cellH;Mt({ctx:t.ctx,cell:o,x:h,y:c,cycleProgress:n,easeOut:r,envelope:e,cellW:t.cellW,cellH:t.cellH,particlesPerCell:t.particlesPerCell,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}t.ctx.restore()}}function Wt(t,e,i,n,s,l,r,o){const h=Math.max(1,Math.floor(Math.min(n,s)*.04)),c=Math.max(1,Math.floor(Math.min(n,s)*.025)),[,,,d]=l,u=r*.15%360;t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${u}, 90%, 70%, ${d*o})`;const g=c*.5;t.strokeRect(e+h+g,i+h+g,n-h*2-c,s-h*2-c),t.restore()}const Y=24;function Mt(t){const e=t.x+t.cellW*.5,i=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,s=Math.min(t.cellW,t.cellH)*.028,l=(.92+(1-t.cycleProgress)*.08)*t.envelope,[r,o,h]=t.particleColorRgb,c=t.particleColorMode==="solid";c&&(t.ctx.fillStyle=`rgb(${r},${o},${h})`);for(let d=0;d<t.particlesPerCell;d+=1){const u=W(t.cell.col,t.cell.row,d,1),g=W(t.cell.col,t.cell.row,d,2),S=W(t.cell.col,t.cell.row,d,3),C=W(t.cell.col,t.cell.row,d,4)*.28,w=W(t.cell.col,t.cell.row,d,5),E=b((t.cycleProgress-C)/(1-C),0,1);if(E<=0)continue;const M=u*Math.PI*2,x=n*E*(.35+g*.65)*(.85+t.easeOut*.15),U=e+Math.cos(M)*x,D=i+Math.sin(M)*x,B=.7+.9*Math.max(0,Math.sin((t.cycleProgress*11+w*2)*Math.PI*2)),k=Math.max(1,s*(.55+S*.6)*(1-E*.5)),P=b((.9+B*.2)*l,.9,1);if(!(P<=0)){if(c)t.ctx.globalAlpha=P;else{const z=(u*360+t.cycleProgress*240+t.cell.col*38+t.cell.row*22)%360,X=Math.floor(z/(360/Y))*(360/Y);t.ctx.fillStyle=`hsla(${X},98%,64%,${P})`}t.ctx.beginPath(),t.ctx.arc(U,D,k,0,Math.PI*2),t.ctx.fill()}}t.ctx.globalAlpha=1}class p{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;quality;spriteImage=null;rafLoop=new st;runtime=gt();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutgoingOffsets=T();scriptedIncomingOffsets=T();winningCells=[];grid;lastRafTime=0;particlesPerCell=R;static SWAY_ENVELOPE_TAU_MS=220;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;constructor(e){this.canvas=e.canvas,this.container=e.container,this.button=e.button,this.spriteUrl=e.sprite,this.spriteElementsCount=Math.max(1,e.spriteElementsCount??$),this.highlightInitialWinningCells=e.highlightInitialWinningCells!==!1,this.spinQueueController=new ft(e.queuedSpinStates),this.particleColorRgb=ot(e.particleColorRgb),this.particleColorMode=ct(e.particleColorMode),this.winningCellBorderRgba=ut(e.winningCellBorderRgba),this.quality=ht(e.quality);const i=this.canvas.getContext("2d");if(!i)throw new Error("2D canvas context is not available");this.ctx=i,this.grid=e.initialSegments?dt(e.initialSegments,this.spriteElementsCount):G(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.startLoop()}destroy(){this.unbindEvents(),this.rafLoop.stop(),Ct(this.runtime),this.clearWinningCells()}spin(){if(this.runtime.isSpinning||this.runtime.queueFinished)return;if(!this.spinQueueController.hasPending()){this.runtime.queueFinished=!0,this.button&&(this.button.disabled=!0);return}const e=this.spinQueueController.consume(),i=!this.spinQueueController.hasPending();this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=e,this.runtime.shouldHighlightCurrentSpin=i,this.runtime.hasStartedFirstSpin=!0,this.button&&(this.button.disabled=!0)}bindEvents(){window.addEventListener("resize",this.resize),this.button?.addEventListener("click",this.onSpinClick)}unbindEvents(){window.removeEventListener("resize",this.resize),this.button?.removeEventListener("click",this.onSpinClick)}onSpinClick=()=>{this.runtime.isSpinning||this.spin()};getNextGrid(e){return e?L(e,this.spriteElementsCount):G(this.spriteElementsCount)}update(e){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(e-this.runtime.preSpinStartedAt<p.PRE_SPIN_MS)return;St(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:e}),this.runtime.preSpinStartedAt=0;const i=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(l=>y(l)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=i.map(l=>l.map(r=>[...r])),this.clearWinningCells();const n=this.runtime.activeSpinState?.stopRows?y(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,s=this.getNextGrid(n);this.startOutroTransition(s,e)}if(this.runtime.phase==="outro"){this.updateScriptedOutro(e);return}this.runtime.phase}}updateScriptedOutro(e){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}const{allOutgoingDone:i,allIncomingDone:n}=rt({now:e,height:this.height,boardY:this.boardY,cellH:this.cellH,scriptedOutroStartedAt:this.scriptedOutroStartedAt,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets});if(!(!i||!n)){if(!this.scriptedPendingGrid){this.finishSpinWithUi();return}if(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.clearWinningCells(),I(this.scriptedOutgoingOffsets,0),I(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(e)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=N(this.grid),q(this.runtime,e)}}}tryStartScriptedCascade(e){if(this.scriptedCascadeQueue.length===0)return!1;const i=this.scriptedCascadeQueue.shift();return i?(this.startOutroTransition(L(i,this.spriteElementsCount),e),!0):!1}startOutroTransition(e,i){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=e.map(n=>[...n]),I(this.scriptedIncomingOffsets,Number.NaN),I(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=i}finishSpinWithUi(){const e=this.runtime.activeSpinState?.callback;pt(this.runtime,this.spinQueueController.hasPending(),performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),e?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=N(this.grid),q(this.runtime,performance.now()))}static ZERO_SWAY=[0,0,0];render(e){const i=this.runtime.phase==="outro"||this.runtime.phase==="preSpin"?0:1,n=this.runtime.phase==="preSpin"?0:this.runtime.phase==="winFlash"?1:0;if(this.lastRafTime>0){const s=Math.min(e-this.lastRafTime,50),l=1-Math.exp(-s/p.SWAY_ENVELOPE_TAU_MS);this.runtime.swayEnvelope+=(i-this.runtime.swayEnvelope)*l;const r=1-Math.exp(-s/p.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(n-this.runtime.winEffectsEnvelope)*r}this.lastRafTime=e,this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,p.ZERO_SWAY,e),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,p.ZERO_SWAY,e)):this.drawBaseGrid(e),bt({ctx:this.ctx,now:e,phase:this.runtime.phase,winFlashStartedAt:this.runtime.winFlashStartedAt,winEffectsEnvelope:this.runtime.winEffectsEnvelope,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:this.particlesPerCell,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(e){this.drawLayer(this.grid,null,p.ZERO_SWAY,e)}drawLayer(e,i,n,s){Ot({ctx:this.ctx,phase:this.runtime.phase,now:s,swayEnvelope:this.runtime.swayEnvelope,grid:e,offsets:i,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,isWinningCell:this.isWinningCell,drawSpriteCell:this.drawSpriteCell})}getCell=(e,i)=>this.grid[e][i];isWinningCell=(e,i)=>this.winningCells.some(n=>n.col===e&&n.row===i);drawSpriteCell=(e,i,n,s,l)=>{const r=this.spriteImage;if(!r)return;const o=A(e,this.spriteElementsCount),h=r.height/this.spriteElementsCount,c=Math.floor(o*h),d=Math.floor(h);this.ctx.drawImage(r,0,c,r.width,d,i,n,s,l)};clearWinningCells(){this.winningCells=[]}resize=()=>{const e=this.container.getBoundingClientRect();this.dpr=Math.max(1,Math.min(window.devicePixelRatio||1,2));const i=Math.max(300,Math.floor(e.width*this.dpr));this.width=i,this.height=i;const n=Math.floor(Math.min(this.width/f,this.height/a));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*f)/2),this.boardY=Math.floor((this.height-this.cellH*a)/2),this.quality==="high"?this.particlesPerCell=R:this.quality==="balanced"?this.particlesPerCell=F:this.quality==="low"?this.particlesPerCell=j:this.particlesPerCell=this.dpr>=2?F:R,this.canvas.width=this.width,this.canvas.height=this.height};async loadSpriteIfProvided(){if(!this.spriteUrl)return;const e=new Image;e.decoding="async",e.src=this.spriteUrl;try{await e.decode(),this.spriteImage=e}catch{this.spriteImage=null}}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(e=>(this.update(e),this.render(e),!0))}}exports.CascadingReel=p;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/outro.ts","../src/normalize.ts","../src/core/spinQueue.ts","../src/core/state.ts","../src/render/canvasRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const FLOW_OUTRO_ROW_GAP_MS = 34;\nexport const FLOW_ROW_BASE_SPACING_RATIO = 0.05;\nexport const FLOW_WIN_FLASH_MS = 1200;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.075;\nexport const FLOW_WIN_PARTICLES_PER_CELL = 34;\nexport const DEFAULT_PARTICLE_COLOR_RGB: [number, number, number] = [255, 235, 110];\nexport const DEFAULT_WINNING_CELL_BORDER_RGBA: [number, number, number, number] = [\n 255, 255, 255, 0.78,\n];\nexport const FLOW_COLUMN_STAGGER_MS = 190;\nexport const FLOW_FALL_MS = 650;\nexport const FLOW_OUTRO_OVERLAP_MS = 220;\n","export function clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nexport function easeOutCubic(t: number): number {\n return 1 - (1 - t) ** 3;\n}\n\nexport function randomInt(maxExclusive: number): number {\n return Math.floor(Math.random() * maxExclusive);\n}\n\nexport function normalizeSegment(segment: number, elementsCount: number): number {\n return ((segment % elementsCount) + elementsCount) % elementsCount;\n}\n\nexport function normalizeRgbChannel(value: number): number {\n return clamp(Math.round(value), 0, 255);\n}\n\nexport function normalizeAlphaChannel(value: number): number {\n return clamp(value, 0, 1);\n}\n","import { GRID_COLS, GRID_ROWS } from '../constants';\nimport type { CellPosition, SymbolId } from '../types';\nimport { randomInt } from '../utils/math';\n\nexport function createRandomGrid(spriteElementsCount: number): SymbolId[][] {\n const grid: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column: SymbolId[] = [];\n for (let row = 0; row < GRID_ROWS; row += 1) {\n column.push(randomInt(spriteElementsCount));\n }\n grid.push(column);\n }\n return grid;\n}\n\nexport function findMostFrequentCells(grid: SymbolId[][]): CellPosition[] {\n const counts = new Map<SymbolId, number>();\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const symbol = grid[col][row];\n counts.set(symbol, (counts.get(symbol) ?? 0) + 1);\n }\n }\n\n let selectedSymbol: SymbolId = grid[0][0];\n let maxCount = -1;\n for (const [symbol, count] of counts.entries()) {\n if (count > maxCount) {\n maxCount = count;\n selectedSymbol = symbol;\n }\n }\n\n const cells: CellPosition[] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (grid[col][row] === selectedSymbol) {\n cells.push({ col, row });\n }\n }\n }\n return cells;\n}\n\nexport function createZeroOffsets(): number[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => 0));\n}\n\nexport function fillOffsets(offsets: number[][], value: number): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n offsets[col][row] = value;\n }\n }\n}\n","export type RafStep = (now: number) => boolean;\n\nexport class RafLoop {\n private rafId: number | null = null;\n private step: RafStep | null = null;\n\n public start(step: RafStep): void {\n if (this.rafId !== null) return;\n this.step = step;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n public stop(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.step = null;\n }\n\n public isRunning(): boolean {\n return this.rafId !== null;\n }\n\n private readonly tick = (now: number): void => {\n if (!this.step) {\n this.stop();\n return;\n }\n\n const shouldContinue = this.step(now);\n if (shouldContinue === false) {\n this.stop();\n return;\n }\n\n if (this.rafId === null) return;\n this.rafId = requestAnimationFrame(this.tick);\n };\n}\n","import {\n FLOW_COLUMN_STAGGER_MS,\n FLOW_FALL_MS,\n FLOW_OUTRO_OVERLAP_MS,\n FLOW_OUTRO_ROW_GAP_MS,\n FLOW_ROW_BASE_SPACING_RATIO,\n GRID_ROWS,\n} from '../constants';\nimport { clamp, easeOutCubic } from '../utils/math';\n\nexport function buildSequentialRowStartDelays(\n fromRowOffsets: [number, number, number],\n duration: number,\n gapMs: number,\n): [number, number, number] {\n const delays: [number, number, number] = [0, 0, 0];\n let nextDelay = 0;\n for (let row = GRID_ROWS - 1; row >= 0; row -= 1) {\n if (fromRowOffsets[row] === 0) {\n delays[row] = 0;\n continue;\n }\n delays[row] = nextDelay;\n const baseSpacing = Math.floor(duration * FLOW_ROW_BASE_SPACING_RATIO);\n nextDelay += baseSpacing + gapMs;\n }\n return delays;\n}\n\nexport function updateOutroOffsets(params: {\n now: number;\n height: number;\n boardY: number;\n cellH: number;\n scriptedOutroStartedAt: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n}): { allOutgoingDone: boolean; allIncomingDone: boolean } {\n let allOutgoingDone = true;\n let allIncomingDone = true;\n\n const exitEpsilon = 2;\n const outgoingDistance = params.height - params.boardY + params.cellH + exitEpsilon;\n const outgoingOffsetsForOrder: [number, number, number] = [\n outgoingDistance,\n outgoingDistance,\n outgoingDistance,\n ];\n const incomingFromOffsets: [number, number, number] = [\n -params.cellH,\n -params.cellH * 2,\n -params.cellH * 3,\n ];\n const rowStartDelays = buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n FLOW_FALL_MS,\n FLOW_OUTRO_ROW_GAP_MS,\n );\n const incomingStartShift = FLOW_FALL_MS - FLOW_OUTRO_OVERLAP_MS;\n\n for (let col = 0; col < params.scriptedOutgoingOffsets.length; col += 1) {\n const columnElapsed =\n params.now - (params.scriptedOutroStartedAt + col * FLOW_COLUMN_STAGGER_MS);\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const rowElapsed = columnElapsed - rowStartDelays[row];\n\n if (rowElapsed <= 0) {\n params.scriptedOutgoingOffsets[col][row] = 0;\n params.scriptedIncomingOffsets[col][row] = Number.NaN;\n allOutgoingDone = false;\n allIncomingDone = false;\n continue;\n }\n\n const tOut = clamp(rowElapsed / FLOW_FALL_MS, 0, 1);\n const easedOut = easeOutCubic(tOut);\n params.scriptedOutgoingOffsets[col][row] = outgoingDistance * easedOut;\n if (tOut < 1) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - incomingStartShift;\n if (incomingElapsed <= 0) {\n params.scriptedIncomingOffsets[col][row] = Number.NaN;\n allIncomingDone = false;\n continue;\n }\n\n const tIn = clamp(incomingElapsed / FLOW_FALL_MS, 0, 1);\n const easedIn = easeOutCubic(tIn);\n params.scriptedIncomingOffsets[col][row] = incomingFromOffsets[row] * (1 - easedIn);\n if (tIn < 1) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import {\n DEFAULT_PARTICLE_COLOR_RGB,\n DEFAULT_WINNING_CELL_BORDER_RGBA,\n GRID_COLS,\n GRID_ROWS,\n} from './constants';\nimport type { SpinState, SymbolId } from './types';\nimport { normalizeAlphaChannel, normalizeRgbChannel, normalizeSegment } from './utils/math';\n\nexport function normalizeParticleColor(color?: [number, number, number]): [number, number, number] {\n if (!color) return DEFAULT_PARTICLE_COLOR_RGB;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n ];\n}\n\nexport function normalizeParticleColorMode(mode?: 'solid' | 'rainbow'): 'solid' | 'rainbow' {\n if (mode === 'rainbow') return 'rainbow';\n return 'solid';\n}\n\nexport function normalizeWinningCellBorderColor(\n color?: [number, number, number, number],\n): [number, number, number, number] {\n if (!color) return DEFAULT_WINNING_CELL_BORDER_RGBA;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n normalizeAlphaChannel(color[3]),\n ];\n}\n\nexport function rowsToStopGrid(rows: number[][]): number[][] {\n if (rows.length !== GRID_ROWS) {\n throw new Error(`rows must contain ${GRID_ROWS} rows`);\n }\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (!Array.isArray(rows[row]) || rows[row].length !== GRID_COLS) {\n throw new Error(`rows[${row}] must contain ${GRID_COLS} columns`);\n }\n }\n\n return [\n [rows[0][0], rows[1][0], rows[2][0]],\n [rows[0][1], rows[1][1], rows[2][1]],\n [rows[0][2], rows[1][2], rows[2][2]],\n ];\n}\n\nexport function normalizeStopGrid(stopGrid: number[][], elementsCount: number): SymbolId[][] {\n if (stopGrid.length !== GRID_COLS) {\n throw new Error(`stopGrid must contain ${GRID_COLS} columns`);\n }\n\n const next: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column = stopGrid[col];\n if (!Array.isArray(column) || column.length !== GRID_ROWS) {\n throw new Error(`stopGrid[${col}] must contain ${GRID_ROWS} rows`);\n }\n next[col] = [\n normalizeSegment(column[0], elementsCount),\n normalizeSegment(column[1], elementsCount),\n normalizeSegment(column[2], elementsCount),\n ];\n }\n return next;\n}\n\nexport function normalizeInitialSegments(\n initialSegments: number[][],\n elementsCount: number,\n): SymbolId[][] {\n return normalizeStopGrid(rowsToStopGrid(initialSegments), elementsCount);\n}\n\nexport function cloneSpinState(state: SpinState): SpinState {\n return {\n stopGrid: state.stopGrid?.map((column) => [...column]),\n stopRows: state.stopRows?.map((row) => [...row]),\n finaleSequence: state.finaleSequence?.map((grid) => grid.map((column) => [...column])),\n finaleSequenceRows: state.finaleSequenceRows?.map((grid) => grid.map((row) => [...row])),\n callback: state.callback,\n };\n}\n","import { cloneSpinState } from '../normalize';\nimport type { SpinState } from '../types';\n\nexport class SpinQueueController {\n private queue: SpinState[];\n\n public constructor(initialQueue?: SpinState[]) {\n this.queue = (initialQueue ?? []).map((entry) => cloneSpinState(entry));\n }\n\n public hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n public consume(): SpinState | null {\n if (this.queue.length === 0) return null;\n return this.queue.shift() ?? null;\n }\n}\n","import type { SpinPhase, SpinState } from '../types';\n\nexport type RuntimeState = {\n isSpinning: boolean;\n hasStartedFirstSpin: boolean;\n queueFinished: boolean;\n shouldHighlightCurrentSpin: boolean;\n activeSpinState: SpinState | null;\n phase: SpinPhase;\n winFlashStartedAt: number;\n};\n\nexport function createRuntimeState(): RuntimeState {\n return {\n isSpinning: false,\n hasStartedFirstSpin: false,\n queueFinished: false,\n shouldHighlightCurrentSpin: false,\n activeSpinState: null,\n phase: 'idle',\n winFlashStartedAt: 0,\n };\n}\n\nexport function beginSpin(\n state: RuntimeState,\n params: {\n activeSpinState: SpinState | null;\n shouldHighlightCurrentSpin: boolean;\n },\n): void {\n state.hasStartedFirstSpin = true;\n state.isSpinning = true;\n state.phase = 'outro';\n state.activeSpinState = params.activeSpinState;\n state.shouldHighlightCurrentSpin = params.shouldHighlightCurrentSpin;\n}\n\nexport function finishSpin(state: RuntimeState, hasPendingInQueue: boolean): void {\n state.phase = 'idle';\n state.isSpinning = false;\n state.shouldHighlightCurrentSpin = false;\n state.queueFinished = !hasPendingInQueue;\n state.activeSpinState = null;\n}\n\nexport function startWinFlash(state: RuntimeState, startedAt: number): void {\n state.winFlashStartedAt = startedAt;\n state.phase = 'winFlash';\n}\n\nexport function destroyState(state: RuntimeState): void {\n state.isSpinning = false;\n state.queueFinished = true;\n}\n","import {\n FLOW_WIN_FLASH_MS,\n FLOW_WIN_PARTICLES_PER_CELL,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n} from '../constants';\nimport type { CellPosition, SpinPhase, SymbolId } from '../types';\nimport { clamp } from '../utils/math';\n\ntype DrawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n) => void;\n\nexport function drawGridLayer(params: {\n ctx: CanvasRenderingContext2D;\n phase: SpinPhase;\n grid: SymbolId[][];\n offsets: number[][] | null;\n columnSway?: [number, number, number];\n boardX: number;\n boardY: number;\n cellW: number;\n cellH: number;\n width: number;\n height: number;\n isWinningCell: (col: number, row: number) => boolean;\n drawSpriteCell: DrawSpriteCell;\n}): void {\n const sway = params.columnSway ?? [0, 0, 0];\n\n for (let col = 0; col < GRID_COLS; col += 1) {\n const x = params.boardX + col * params.cellW;\n const swayY = sway[col] ?? 0;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (params.phase === 'winFlash' && params.isWinningCell(col, row)) continue;\n const offsetY = params.offsets ? params.offsets[col][row] : 0;\n if (!Number.isFinite(offsetY)) continue;\n\n const y = params.boardY + row * params.cellH + offsetY + swayY;\n if (y > params.height || y + params.cellH < 0) continue;\n\n params.drawSpriteCell(params.grid[col][row], x, y, params.cellW, params.cellH);\n }\n }\n}\n\nfunction hash01(a: number, b: number, c: number, d: number): number {\n const value = Math.sin(a * 127.1 + b * 311.7 + c * 74.7 + d * 19.3) * 43758.5453;\n return value - Math.floor(value);\n}\n\nexport function drawWinningEffects(params: {\n ctx: CanvasRenderingContext2D;\n now: number;\n phase: SpinPhase;\n winFlashStartedAt: number;\n winningCells: CellPosition[];\n boardX: number;\n boardY: number;\n cellW: number;\n cellH: number;\n particleColorMode: 'solid' | 'rainbow';\n particleColorRgb: [number, number, number];\n winningCellBorderRgba: [number, number, number, number];\n getCell: (col: number, row: number) => SymbolId;\n drawSpriteCell: DrawSpriteCell;\n}): void {\n if (params.winningCells.length === 0) return;\n if (params.phase !== 'winFlash') return;\n\n const elapsed = Math.max(0, params.now - params.winFlashStartedAt);\n const cycleProgress = (elapsed % FLOW_WIN_FLASH_MS) / FLOW_WIN_FLASH_MS;\n const pulseProgress = (elapsed % FLOW_WIN_PULSE_PERIOD_MS) / FLOW_WIN_PULSE_PERIOD_MS;\n const pulse = 1 + Math.sin(pulseProgress * Math.PI * 2) * FLOW_WIN_PULSE_AMPLITUDE;\n const easeOut = 1 - (1 - cycleProgress) ** 2;\n\n for (const cell of params.winningCells) {\n const x = params.boardX + cell.col * params.cellW;\n const y = params.boardY + cell.row * params.cellH;\n drawWinningCellBorder(\n params.ctx,\n x,\n y,\n params.cellW,\n params.cellH,\n params.winningCellBorderRgba,\n elapsed,\n );\n\n const symbol = params.getCell(cell.col, cell.row);\n const scaledW = params.cellW * pulse;\n const scaledH = params.cellH * pulse;\n const offsetX = (params.cellW - scaledW) * 0.5;\n const offsetY = (params.cellH - scaledH) * 0.5;\n\n params.ctx.save();\n params.drawSpriteCell(symbol, x + offsetX, y + offsetY, scaledW, scaledH);\n params.ctx.restore();\n\n drawCellParticleBurst({\n ctx: params.ctx,\n cell,\n x,\n y,\n cycleProgress,\n easeOut,\n boardX: params.boardX,\n boardY: params.boardY,\n cellW: params.cellW,\n cellH: params.cellH,\n particleColorMode: params.particleColorMode,\n particleColorRgb: params.particleColorRgb,\n });\n }\n}\n\nfunction drawWinningCellBorder(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n cellW: number,\n cellH: number,\n color: [number, number, number, number],\n elapsed: number,\n): void {\n const inset = Math.max(1, Math.floor(Math.min(cellW, cellH) * 0.04));\n const lineWidth = Math.max(1, Math.floor(Math.min(cellW, cellH) * 0.025));\n const [, , , a] = color;\n const hue = (elapsed * 0.15) % 360;\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a})`;\n const halfLine = lineWidth * 0.5;\n ctx.strokeRect(\n x + inset + halfLine,\n y + inset + halfLine,\n cellW - inset * 2 - lineWidth,\n cellH - inset * 2 - lineWidth,\n );\n ctx.restore();\n}\n\nfunction drawCellParticleBurst(params: {\n ctx: CanvasRenderingContext2D;\n cell: CellPosition;\n x: number;\n y: number;\n cycleProgress: number;\n easeOut: number;\n boardX: number;\n boardY: number;\n cellW: number;\n cellH: number;\n particleColorMode: 'solid' | 'rainbow';\n particleColorRgb: [number, number, number];\n}): void {\n const centerX = params.x + params.cellW * 0.5;\n const centerY = params.y + params.cellH * 0.5;\n const maxDistance = Math.min(params.cellW, params.cellH) * 0.72;\n const baseRadius = Math.min(params.cellW, params.cellH) * 0.028;\n const globalFade = 0.92 + (1 - params.cycleProgress) * 0.08;\n\n params.ctx.save();\n params.ctx.beginPath();\n params.ctx.rect(params.boardX, params.boardY, params.cellW * GRID_COLS, params.cellH * GRID_ROWS);\n params.ctx.clip();\n\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL; i += 1) {\n const seedA = hash01(params.cell.col, params.cell.row, i, 1);\n const seedB = hash01(params.cell.col, params.cell.row, i, 2);\n const seedC = hash01(params.cell.col, params.cell.row, i, 3);\n const phaseOffset = hash01(params.cell.col, params.cell.row, i, 4) * 0.28;\n const twinkleSeed = hash01(params.cell.col, params.cell.row, i, 5);\n const particleT = clamp((params.cycleProgress - phaseOffset) / (1 - phaseOffset), 0, 1);\n if (particleT <= 0) continue;\n\n const direction = seedA * Math.PI * 2;\n const distance =\n maxDistance * particleT * (0.35 + seedB * 0.65) * (0.85 + params.easeOut * 0.15);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 +\n 0.9 * Math.max(0, Math.sin((params.cycleProgress * 11 + twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + seedC * 0.6) * (1 - particleT * 0.5));\n const alpha = clamp((0.9 + twinkle * 0.2) * globalFade, 0.9, 1);\n if (alpha <= 0) continue;\n\n if (params.particleColorMode === 'rainbow') {\n const hue =\n (seedA * 360 + params.cycleProgress * 240 + params.cell.col * 38 + params.cell.row * 22) %\n 360;\n params.ctx.fillStyle = `hsla(${hue}, 98%, 64%, ${alpha})`;\n } else {\n const [r, g, b] = params.particleColorRgb;\n params.ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`;\n }\n\n params.ctx.beginPath();\n params.ctx.arc(px, py, radius, 0, Math.PI * 2);\n params.ctx.fill();\n }\n params.ctx.restore();\n}\n","import { DEFAULT_SPRITE_ELEMENTS_COUNT, GRID_COLS, GRID_ROWS } from './constants';\nimport {\n createRandomGrid,\n createZeroOffsets,\n fillOffsets,\n findMostFrequentCells,\n} from './core/grid';\nimport { RafLoop } from './core/loop';\nimport { updateOutroOffsets } from './core/outro';\nimport { SpinQueueController } from './core/spinQueue';\nimport {\n beginSpin,\n createRuntimeState,\n destroyState,\n finishSpin,\n startWinFlash,\n} from './core/state';\nimport {\n normalizeInitialSegments,\n normalizeParticleColor,\n normalizeParticleColorMode,\n normalizeStopGrid,\n normalizeWinningCellBorderColor,\n rowsToStopGrid,\n} from './normalize';\nimport { drawGridLayer, drawWinningEffects } from './render/canvasRenderer';\nimport type { CascadingReelConfig, CellPosition, SymbolId } from './types';\nimport { normalizeSegment } from './utils/math';\n\nexport class CascadingReel {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly spinQueueController: SpinQueueController;\n private readonly spriteUrl?: string;\n private readonly spriteElementsCount: number;\n private readonly highlightInitialWinningCells: boolean;\n private readonly particleColorRgb: [number, number, number];\n private readonly particleColorMode: 'solid' | 'rainbow';\n private readonly winningCellBorderRgba: [number, number, number, number];\n\n private spriteImage: HTMLImageElement | null = null;\n private readonly rafLoop = new RafLoop();\n private readonly runtime = createRuntimeState();\n private dpr = 1;\n private width = 0;\n private height = 0;\n private cellW = 0;\n private cellH = 0;\n private boardX = 0;\n private boardY = 0;\n private scriptedCascadeQueue: number[][][] = [];\n private scriptedOutgoingGrid: SymbolId[][] | null = null;\n private scriptedPendingGrid: SymbolId[][] | null = null;\n private scriptedOutroStartedAt = 0;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private winningCells: CellPosition[] = [];\n private grid: SymbolId[][];\n\n public constructor(config: CascadingReelConfig) {\n this.canvas = config.canvas;\n this.container = config.container;\n this.button = config.button;\n this.spriteUrl = config.sprite;\n this.spriteElementsCount = Math.max(\n 1,\n config.spriteElementsCount ?? DEFAULT_SPRITE_ELEMENTS_COUNT,\n );\n this.highlightInitialWinningCells = config.highlightInitialWinningCells !== false;\n this.spinQueueController = new SpinQueueController(config.queuedSpinStates);\n this.particleColorRgb = normalizeParticleColor(config.particleColorRgb);\n this.particleColorMode = normalizeParticleColorMode(config.particleColorMode);\n this.winningCellBorderRgba = normalizeWinningCellBorderColor(config.winningCellBorderRgba);\n\n const context = this.canvas.getContext('2d');\n if (!context) {\n throw new Error('2D canvas context is not available');\n }\n this.ctx = context;\n this.grid = config.initialSegments\n ? normalizeInitialSegments(config.initialSegments, this.spriteElementsCount)\n : createRandomGrid(this.spriteElementsCount);\n }\n\n public async init(): Promise<void> {\n this.bindEvents();\n this.resize();\n await this.loadSpriteIfProvided();\n this.applyInitialHighlightIfNeeded();\n this.startLoop();\n }\n\n public destroy(): void {\n this.unbindEvents();\n this.rafLoop.stop();\n destroyState(this.runtime);\n this.clearWinningCells();\n }\n\n public spin(): void {\n if (this.runtime.isSpinning) return;\n if (this.runtime.queueFinished) return;\n if (!this.spinQueueController.hasPending()) {\n this.runtime.queueFinished = true;\n if (this.button) this.button.disabled = true;\n return;\n }\n\n const activeSpinState = this.spinQueueController.consume();\n const shouldHighlightCurrentSpin = !this.spinQueueController.hasPending();\n beginSpin(this.runtime, {\n activeSpinState,\n shouldHighlightCurrentSpin,\n });\n\n if (this.button) this.button.disabled = true;\n\n const scriptedSource = this.runtime.activeSpinState?.finaleSequenceRows\n ? this.runtime.activeSpinState.finaleSequenceRows.map((rows) => rowsToStopGrid(rows))\n : (this.runtime.activeSpinState?.finaleSequence ?? []);\n this.scriptedCascadeQueue = scriptedSource.map((grid) => grid.map((column) => [...column]));\n this.clearWinningCells();\n\n const stopGridSource = this.runtime.activeSpinState?.stopRows\n ? rowsToStopGrid(this.runtime.activeSpinState.stopRows)\n : this.runtime.activeSpinState?.stopGrid;\n const nextGrid = this.getNextGrid(stopGridSource);\n this.startOutroTransition(nextGrid, performance.now());\n }\n\n private bindEvents(): void {\n window.addEventListener('resize', this.resize);\n this.button?.addEventListener('click', this.onSpinClick);\n }\n\n private unbindEvents(): void {\n window.removeEventListener('resize', this.resize);\n this.button?.removeEventListener('click', this.onSpinClick);\n }\n\n private readonly onSpinClick = (): void => {\n if (this.runtime.isSpinning) return;\n this.spin();\n };\n\n private getNextGrid(stopGrid?: number[][]): SymbolId[][] {\n if (!stopGrid) return createRandomGrid(this.spriteElementsCount);\n return normalizeStopGrid(stopGrid, this.spriteElementsCount);\n }\n\n private update(now: number): void {\n if (!this.runtime.isSpinning) return;\n if (this.runtime.phase === 'outro') {\n this.updateScriptedOutro(now);\n return;\n }\n if (this.runtime.phase === 'winFlash') return;\n }\n\n private updateScriptedOutro(now: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n now,\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n scriptedOutroStartedAt: this.scriptedOutroStartedAt,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n });\n\n if (!allOutgoingDone || !allIncomingDone) return;\n if (!this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n\n this.grid = this.scriptedPendingGrid;\n this.scriptedOutgoingGrid = null;\n this.scriptedPendingGrid = null;\n this.clearWinningCells();\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n fillOffsets(this.scriptedIncomingOffsets, 0);\n\n if (this.tryStartScriptedCascade(now)) return;\n if (!this.runtime.shouldHighlightCurrentSpin) {\n this.finishSpinWithUi();\n return;\n }\n\n this.winningCells = findMostFrequentCells(this.grid);\n startWinFlash(this.runtime, now);\n }\n\n private tryStartScriptedCascade(now: number): boolean {\n if (this.scriptedCascadeQueue.length === 0) return false;\n const nextGrid = this.scriptedCascadeQueue.shift();\n if (!nextGrid) return false;\n this.startOutroTransition(normalizeStopGrid(nextGrid, this.spriteElementsCount), now);\n return true;\n }\n\n private startOutroTransition(nextGrid: SymbolId[][], now: number): void {\n this.scriptedOutgoingGrid = this.grid.map((column) => [...column]);\n this.scriptedPendingGrid = nextGrid.map((column) => [...column]);\n fillOffsets(this.scriptedIncomingOffsets, Number.NaN);\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n }\n\n private finishSpinWithUi(): void {\n const callback = this.runtime.activeSpinState?.callback;\n finishSpin(this.runtime, this.spinQueueController.hasPending());\n if (this.button) this.button.disabled = this.runtime.queueFinished;\n callback?.();\n }\n\n private applyInitialHighlightIfNeeded(): void {\n if (!this.highlightInitialWinningCells) return;\n this.winningCells = findMostFrequentCells(this.grid);\n startWinFlash(this.runtime, performance.now());\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n this.ctx.clearRect(0, 0, this.width, this.height);\n if (this.runtime.phase === 'outro' && this.scriptedOutgoingGrid && this.scriptedPendingGrid) {\n this.drawLayer(\n this.scriptedOutgoingGrid,\n this.scriptedOutgoingOffsets,\n CascadingReel.ZERO_SWAY,\n );\n this.drawLayer(\n this.scriptedPendingGrid,\n this.scriptedIncomingOffsets,\n CascadingReel.ZERO_SWAY,\n );\n } else {\n this.drawBaseGrid();\n }\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: this.runtime.phase,\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: this.drawSpriteCell,\n });\n }\n\n private drawBaseGrid(): void {\n this.drawLayer(this.grid, null, CascadingReel.ZERO_SWAY);\n }\n\n private drawLayer(\n grid: SymbolId[][],\n offsets: number[][] | null,\n columnSway: [number, number, number],\n ): void {\n drawGridLayer({\n ctx: this.ctx,\n phase: this.runtime.phase,\n grid,\n offsets,\n columnSway,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n width: this.width,\n height: this.height,\n isWinningCell: this.isWinningCell,\n drawSpriteCell: this.drawSpriteCell,\n });\n }\n\n private readonly getCell = (col: number, row: number): SymbolId => {\n return this.grid[col][row];\n };\n\n private readonly isWinningCell = (col: number, row: number): boolean => {\n return this.winningCells.some((cell) => cell.col === col && cell.row === row);\n };\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n const image = this.spriteImage;\n if (!image) return;\n\n const segmentIndex = normalizeSegment(symbolId, this.spriteElementsCount);\n const segmentHeight = image.height / this.spriteElementsCount;\n const sourceY = Math.floor(segmentIndex * segmentHeight);\n const sourceHeight = Math.floor(segmentHeight);\n\n this.ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n };\n\n private clearWinningCells(): void {\n this.winningCells = [];\n }\n\n private readonly resize = (): void => {\n const bounds = this.container.getBoundingClientRect();\n this.dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));\n const side = Math.max(300, Math.floor(bounds.width * this.dpr));\n this.width = side;\n this.height = side;\n const squareSize = Math.floor(Math.min(this.width / GRID_COLS, this.height / GRID_ROWS));\n this.cellW = squareSize;\n this.cellH = squareSize;\n this.boardX = Math.floor((this.width - this.cellW * GRID_COLS) / 2);\n this.boardY = Math.floor((this.height - this.cellH * GRID_ROWS) / 2);\n\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n };\n\n private async loadSpriteIfProvided(): Promise<void> {\n if (!this.spriteUrl) return;\n const image = new Image();\n image.decoding = 'async';\n image.src = this.spriteUrl;\n try {\n await image.decode();\n this.spriteImage = image;\n } catch {\n this.spriteImage = null;\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.update(time);\n this.render(time);\n return true;\n });\n }\n}\n"],"names":["DEFAULT_SPRITE_ELEMENTS_COUNT","GRID_COLS","GRID_ROWS","FLOW_OUTRO_ROW_GAP_MS","FLOW_ROW_BASE_SPACING_RATIO","FLOW_WIN_FLASH_MS","FLOW_WIN_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","FLOW_WIN_PARTICLES_PER_CELL","DEFAULT_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","clamp","value","min","max","easeOutCubic","randomInt","maxExclusive","normalizeSegment","segment","elementsCount","normalizeRgbChannel","normalizeAlphaChannel","createRandomGrid","spriteElementsCount","grid","col","column","row","findMostFrequentCells","counts","symbol","selectedSymbol","maxCount","count","cells","createZeroOffsets","fillOffsets","offsets","RafLoop","step","now","buildSequentialRowStartDelays","fromRowOffsets","duration","gapMs","delays","nextDelay","baseSpacing","updateOutroOffsets","params","allOutgoingDone","allIncomingDone","outgoingDistance","outgoingOffsetsForOrder","incomingFromOffsets","rowStartDelays","incomingStartShift","columnElapsed","rowElapsed","tOut","easedOut","incomingElapsed","tIn","easedIn","normalizeParticleColor","color","normalizeParticleColorMode","mode","normalizeWinningCellBorderColor","rowsToStopGrid","rows","normalizeStopGrid","stopGrid","next","normalizeInitialSegments","initialSegments","cloneSpinState","state","SpinQueueController","initialQueue","entry","createRuntimeState","beginSpin","finishSpin","hasPendingInQueue","startWinFlash","startedAt","destroyState","drawGridLayer","sway","x","swayY","offsetY","y","hash01","a","b","c","d","drawWinningEffects","elapsed","cycleProgress","pulseProgress","pulse","easeOut","cell","drawWinningCellBorder","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","inset","lineWidth","hue","halfLine","centerX","centerY","maxDistance","baseRadius","globalFade","i","seedA","seedB","seedC","phaseOffset","twinkleSeed","particleT","direction","distance","px","py","twinkle","radius","alpha","r","g","CascadingReel","config","context","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","columnSway","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","time"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/outro.ts","../src/normalize.ts","../src/core/spinQueue.ts","../src/core/state.ts","../src/render/canvasRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const FLOW_OUTRO_ROW_GAP_MS = 34;\nexport const FLOW_ROW_BASE_SPACING_RATIO = 0.05;\nexport const FLOW_WIN_FLASH_MS = 1200;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.075;\nexport const FLOW_WIN_PARTICLES_PER_CELL_HIGH = 34;\nexport const FLOW_WIN_PARTICLES_PER_CELL_BALANCED = 20;\nexport const FLOW_WIN_PARTICLES_PER_CELL_LOW = 12;\nexport const DEFAULT_PARTICLE_COLOR_RGB: [number, number, number] = [255, 235, 110];\nexport const DEFAULT_WINNING_CELL_BORDER_RGBA: [number, number, number, number] = [\n 255, 255, 255, 0.78,\n];\nexport const FLOW_COLUMN_STAGGER_MS = 190;\nexport const FLOW_FALL_MS = 650;\nexport const FLOW_OUTRO_OVERLAP_MS = 220;\n","export function clamp(value: number, min: number, max: number): number {\n if (value < min) return min;\n if (value > max) return max;\n return value;\n}\n\nexport function easeOutCubic(t: number): number {\n return 1 - (1 - t) ** 3;\n}\n\nexport function randomInt(maxExclusive: number): number {\n return Math.floor(Math.random() * maxExclusive);\n}\n\nexport function normalizeSegment(segment: number, elementsCount: number): number {\n return ((segment % elementsCount) + elementsCount) % elementsCount;\n}\n\nexport function normalizeRgbChannel(value: number): number {\n return clamp(Math.round(value), 0, 255);\n}\n\nexport function normalizeAlphaChannel(value: number): number {\n return clamp(value, 0, 1);\n}\n","import { GRID_COLS, GRID_ROWS } from '../constants';\nimport type { CellPosition, SymbolId } from '../types';\nimport { randomInt } from '../utils/math';\n\nexport function createRandomGrid(spriteElementsCount: number): SymbolId[][] {\n const grid: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column: SymbolId[] = [];\n for (let row = 0; row < GRID_ROWS; row += 1) {\n column.push(randomInt(spriteElementsCount));\n }\n grid.push(column);\n }\n return grid;\n}\n\nexport function findMostFrequentCells(grid: SymbolId[][]): CellPosition[] {\n const counts = new Map<SymbolId, number>();\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const symbol = grid[col][row];\n counts.set(symbol, (counts.get(symbol) ?? 0) + 1);\n }\n }\n\n let selectedSymbol: SymbolId = grid[0][0];\n let maxCount = -1;\n for (const [symbol, count] of counts.entries()) {\n if (count > maxCount) {\n maxCount = count;\n selectedSymbol = symbol;\n }\n }\n\n const cells: CellPosition[] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (grid[col][row] === selectedSymbol) {\n cells.push({ col, row });\n }\n }\n }\n return cells;\n}\n\nexport function createZeroOffsets(): number[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => 0));\n}\n\nexport function fillOffsets(offsets: number[][], value: number): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n offsets[col][row] = value;\n }\n }\n}\n","export type RafStep = (now: number) => boolean;\n\nexport class RafLoop {\n private rafId: number | null = null;\n private step: RafStep | null = null;\n\n public start(step: RafStep): void {\n if (this.rafId !== null) return;\n this.step = step;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n public stop(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.step = null;\n }\n\n public isRunning(): boolean {\n return this.rafId !== null;\n }\n\n private readonly tick = (now: number): void => {\n if (!this.step) {\n this.stop();\n return;\n }\n\n const shouldContinue = this.step(now);\n if (shouldContinue === false) {\n this.stop();\n return;\n }\n\n if (this.rafId === null) return;\n this.rafId = requestAnimationFrame(this.tick);\n };\n}\n","import {\n FLOW_COLUMN_STAGGER_MS,\n FLOW_FALL_MS,\n FLOW_OUTRO_OVERLAP_MS,\n FLOW_OUTRO_ROW_GAP_MS,\n FLOW_ROW_BASE_SPACING_RATIO,\n GRID_ROWS,\n} from '../constants';\nimport { clamp, easeOutCubic } from '../utils/math';\n\nexport function buildSequentialRowStartDelays(\n fromRowOffsets: [number, number, number],\n duration: number,\n gapMs: number,\n): [number, number, number] {\n const delays: [number, number, number] = [0, 0, 0];\n let nextDelay = 0;\n for (let row = GRID_ROWS - 1; row >= 0; row -= 1) {\n if (fromRowOffsets[row] === 0) {\n delays[row] = 0;\n continue;\n }\n delays[row] = nextDelay;\n const baseSpacing = Math.floor(duration * FLOW_ROW_BASE_SPACING_RATIO);\n nextDelay += baseSpacing + gapMs;\n }\n return delays;\n}\n\nexport function updateOutroOffsets(params: {\n now: number;\n height: number;\n boardY: number;\n cellH: number;\n scriptedOutroStartedAt: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n}): { allOutgoingDone: boolean; allIncomingDone: boolean } {\n let allOutgoingDone = true;\n let allIncomingDone = true;\n\n const exitEpsilon = 2;\n const outgoingDistance = params.height - params.boardY + params.cellH + exitEpsilon;\n const outgoingOffsetsForOrder: [number, number, number] = [\n outgoingDistance,\n outgoingDistance,\n outgoingDistance,\n ];\n const incomingFromOffsets: [number, number, number] = [\n -params.cellH,\n -params.cellH * 2,\n -params.cellH * 3,\n ];\n const rowStartDelays = buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n FLOW_FALL_MS,\n FLOW_OUTRO_ROW_GAP_MS,\n );\n const incomingStartShift = FLOW_FALL_MS - FLOW_OUTRO_OVERLAP_MS;\n\n for (let col = 0; col < params.scriptedOutgoingOffsets.length; col += 1) {\n const columnElapsed =\n params.now - (params.scriptedOutroStartedAt + col * FLOW_COLUMN_STAGGER_MS);\n for (let row = 0; row < GRID_ROWS; row += 1) {\n const rowElapsed = columnElapsed - rowStartDelays[row];\n\n if (rowElapsed <= 0) {\n params.scriptedOutgoingOffsets[col][row] = 0;\n params.scriptedIncomingOffsets[col][row] = Number.NaN;\n allOutgoingDone = false;\n allIncomingDone = false;\n continue;\n }\n\n const tOut = clamp(rowElapsed / FLOW_FALL_MS, 0, 1);\n const easedOut = easeOutCubic(tOut);\n params.scriptedOutgoingOffsets[col][row] = outgoingDistance * easedOut;\n if (tOut < 1) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - incomingStartShift;\n if (incomingElapsed <= 0) {\n params.scriptedIncomingOffsets[col][row] = Number.NaN;\n allIncomingDone = false;\n continue;\n }\n\n const tIn = clamp(incomingElapsed / FLOW_FALL_MS, 0, 1);\n const easedIn = easeOutCubic(tIn);\n params.scriptedIncomingOffsets[col][row] = incomingFromOffsets[row] * (1 - easedIn);\n if (tIn < 1) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import {\n DEFAULT_PARTICLE_COLOR_RGB,\n DEFAULT_WINNING_CELL_BORDER_RGBA,\n GRID_COLS,\n GRID_ROWS,\n} from './constants';\nimport type { QualityPreset, SpinState, SymbolId } from './types';\nimport { normalizeAlphaChannel, normalizeRgbChannel, normalizeSegment } from './utils/math';\n\nexport function normalizeParticleColor(color?: [number, number, number]): [number, number, number] {\n if (!color) return DEFAULT_PARTICLE_COLOR_RGB;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n ];\n}\n\nexport function normalizeParticleColorMode(mode?: 'solid' | 'rainbow'): 'solid' | 'rainbow' {\n if (mode === 'rainbow') return 'rainbow';\n return 'solid';\n}\n\nexport function normalizeQuality(preset?: QualityPreset): QualityPreset {\n if (preset === 'high' || preset === 'balanced' || preset === 'low' || preset === 'auto') {\n return preset;\n }\n return 'auto';\n}\n\nexport function normalizeWinningCellBorderColor(\n color?: [number, number, number, number],\n): [number, number, number, number] {\n if (!color) return DEFAULT_WINNING_CELL_BORDER_RGBA;\n return [\n normalizeRgbChannel(color[0]),\n normalizeRgbChannel(color[1]),\n normalizeRgbChannel(color[2]),\n normalizeAlphaChannel(color[3]),\n ];\n}\n\nexport function rowsToStopGrid(rows: number[][]): number[][] {\n if (rows.length !== GRID_ROWS) {\n throw new Error(`rows must contain ${GRID_ROWS} rows`);\n }\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (!Array.isArray(rows[row]) || rows[row].length !== GRID_COLS) {\n throw new Error(`rows[${row}] must contain ${GRID_COLS} columns`);\n }\n }\n\n return [\n [rows[0][0], rows[1][0], rows[2][0]],\n [rows[0][1], rows[1][1], rows[2][1]],\n [rows[0][2], rows[1][2], rows[2][2]],\n ];\n}\n\nexport function normalizeStopGrid(stopGrid: number[][], elementsCount: number): SymbolId[][] {\n if (stopGrid.length !== GRID_COLS) {\n throw new Error(`stopGrid must contain ${GRID_COLS} columns`);\n }\n\n const next: SymbolId[][] = [];\n for (let col = 0; col < GRID_COLS; col += 1) {\n const column = stopGrid[col];\n if (!Array.isArray(column) || column.length !== GRID_ROWS) {\n throw new Error(`stopGrid[${col}] must contain ${GRID_ROWS} rows`);\n }\n next[col] = [\n normalizeSegment(column[0], elementsCount),\n normalizeSegment(column[1], elementsCount),\n normalizeSegment(column[2], elementsCount),\n ];\n }\n return next;\n}\n\nexport function normalizeInitialSegments(\n initialSegments: number[][],\n elementsCount: number,\n): SymbolId[][] {\n return normalizeStopGrid(rowsToStopGrid(initialSegments), elementsCount);\n}\n\nexport function cloneSpinState(state: SpinState): SpinState {\n return {\n stopGrid: state.stopGrid?.map((column) => [...column]),\n stopRows: state.stopRows?.map((row) => [...row]),\n finaleSequence: state.finaleSequence?.map((grid) => grid.map((column) => [...column])),\n finaleSequenceRows: state.finaleSequenceRows?.map((grid) => grid.map((row) => [...row])),\n callback: state.callback,\n };\n}\n","import { cloneSpinState } from '../normalize';\nimport type { SpinState } from '../types';\n\nexport class SpinQueueController {\n private queue: SpinState[];\n\n public constructor(initialQueue?: SpinState[]) {\n this.queue = (initialQueue ?? []).map((entry) => cloneSpinState(entry));\n }\n\n public hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n public consume(): SpinState | null {\n if (this.queue.length === 0) return null;\n return this.queue.shift() ?? null;\n }\n}\n","import type { SpinPhase, SpinState } from '../types';\n\nexport type RuntimeState = {\n isSpinning: boolean;\n hasStartedFirstSpin: boolean;\n queueFinished: boolean;\n shouldHighlightCurrentSpin: boolean;\n activeSpinState: SpinState | null;\n phase: SpinPhase;\n winFlashStartedAt: number;\n outroStartedAt: number;\n idleStartedAt: number;\n preSpinStartedAt: number;\n swayEnvelope: number;\n winEffectsEnvelope: number;\n};\n\nexport function createRuntimeState(): RuntimeState {\n return {\n isSpinning: false,\n hasStartedFirstSpin: false,\n queueFinished: false,\n shouldHighlightCurrentSpin: false,\n activeSpinState: null,\n phase: 'idle',\n winFlashStartedAt: 0,\n outroStartedAt: 0,\n idleStartedAt: 0,\n preSpinStartedAt: 0,\n swayEnvelope: 1,\n winEffectsEnvelope: 1,\n };\n}\n\nexport function beginSpin(\n state: RuntimeState,\n params: {\n activeSpinState: SpinState | null;\n shouldHighlightCurrentSpin: boolean;\n startedAt: number;\n },\n): void {\n state.hasStartedFirstSpin = true;\n state.isSpinning = true;\n state.phase = 'outro';\n state.outroStartedAt = params.startedAt;\n state.activeSpinState = params.activeSpinState;\n state.shouldHighlightCurrentSpin = params.shouldHighlightCurrentSpin;\n}\n\nexport function finishSpin(state: RuntimeState, hasPendingInQueue: boolean, now: number): void {\n state.phase = 'idle';\n state.idleStartedAt = now;\n state.isSpinning = false;\n state.shouldHighlightCurrentSpin = false;\n state.queueFinished = !hasPendingInQueue;\n state.activeSpinState = null;\n}\n\nexport function startWinFlash(state: RuntimeState, startedAt: number): void {\n state.winFlashStartedAt = startedAt;\n state.phase = 'winFlash';\n}\n\nexport function destroyState(state: RuntimeState): void {\n state.isSpinning = false;\n state.queueFinished = true;\n}\n","import {\n FLOW_WIN_FLASH_MS,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n} from '../constants';\nimport type { CellPosition, SpinPhase, SymbolId } from '../types';\nimport { clamp } from '../utils/math';\n\ntype DrawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n) => void;\n\nconst SWAY_AMPLITUDE_DEG = 3;\nconst SWAY_OMEGA = 0.002;\n\nexport function drawGridLayer(params: {\n ctx: CanvasRenderingContext2D;\n phase: SpinPhase;\n now: number;\n swayEnvelope: number;\n grid: SymbolId[][];\n offsets: number[][] | null;\n columnSway?: [number, number, number];\n boardX: number;\n boardY: number;\n cellW: number;\n cellH: number;\n width: number;\n height: number;\n isWinningCell: (col: number, row: number) => boolean;\n drawSpriteCell: DrawSpriteCell;\n}): void {\n const sway = params.columnSway ?? [0, 0, 0];\n const swayAmplitudeRad = SWAY_AMPLITUDE_DEG * (Math.PI / 180);\n const envelope = Math.max(0, Math.min(1, params.swayEnvelope));\n\n for (let col = 0; col < GRID_COLS; col += 1) {\n const x = params.boardX + col * params.cellW;\n const swayY = sway[col] ?? 0;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (\n (params.phase === 'winFlash' || params.phase === 'preSpin') &&\n params.isWinningCell(col, row)\n )\n continue;\n const offsetY = params.offsets ? params.offsets[col][row] : 0;\n if (!Number.isFinite(offsetY)) continue;\n\n const y = params.boardY + row * params.cellH + offsetY + swayY;\n if (y > params.height || y + params.cellH < 0) continue;\n\n const rawAngle = Math.sin(params.now * SWAY_OMEGA + col * 1.1 + row * 1.3) * swayAmplitudeRad;\n const angle = rawAngle * envelope;\n\n const centerX = x + params.cellW * 0.5;\n const centerY = y + params.cellH * 0.5;\n\n params.ctx.save();\n params.ctx.translate(centerX, centerY);\n params.ctx.rotate(angle);\n params.ctx.translate(-params.cellW * 0.5, -params.cellH * 0.5);\n params.drawSpriteCell(params.grid[col][row], 0, 0, params.cellW, params.cellH);\n params.ctx.restore();\n }\n }\n}\n\nfunction hash01(a: number, b: number, c: number, d: number): number {\n const value = Math.sin(a * 127.1 + b * 311.7 + c * 74.7 + d * 19.3) * 43758.5453;\n return value - Math.floor(value);\n}\n\nexport function drawWinningEffects(params: {\n ctx: CanvasRenderingContext2D;\n now: number;\n phase: SpinPhase;\n winFlashStartedAt: number;\n winEffectsEnvelope: number;\n winningCells: CellPosition[];\n boardX: number;\n boardY: number;\n cellW: number;\n cellH: number;\n particlesPerCell: number;\n particleColorMode: 'solid' | 'rainbow';\n particleColorRgb: [number, number, number];\n winningCellBorderRgba: [number, number, number, number];\n getCell: (col: number, row: number) => SymbolId;\n drawSpriteCell: DrawSpriteCell;\n}): void {\n if (params.winningCells.length === 0) return;\n if (params.phase !== 'winFlash' && params.phase !== 'preSpin') return;\n\n const envelope = Math.max(0, Math.min(1, params.winEffectsEnvelope));\n const elapsed = Math.max(0, params.now - params.winFlashStartedAt);\n const cycleProgress = (elapsed % FLOW_WIN_FLASH_MS) / FLOW_WIN_FLASH_MS;\n const pulseProgress = (elapsed % FLOW_WIN_PULSE_PERIOD_MS) / FLOW_WIN_PULSE_PERIOD_MS;\n const pulse = 1 + Math.sin(pulseProgress * Math.PI * 2) * FLOW_WIN_PULSE_AMPLITUDE;\n const easeOut = 1 - (1 - cycleProgress) ** 2;\n\n for (const cell of params.winningCells) {\n const x = params.boardX + cell.col * params.cellW;\n const y = params.boardY + cell.row * params.cellH;\n drawWinningCellBorder(\n params.ctx,\n x,\n y,\n params.cellW,\n params.cellH,\n params.winningCellBorderRgba,\n elapsed,\n envelope,\n );\n\n const symbol = params.getCell(cell.col, cell.row);\n const scale = 1 + (pulse - 1) * envelope;\n const scaledW = params.cellW * scale;\n const scaledH = params.cellH * scale;\n const offsetX = (params.cellW - scaledW) * 0.5;\n const offsetY = (params.cellH - scaledH) * 0.5;\n\n params.ctx.save();\n params.drawSpriteCell(symbol, x + offsetX, y + offsetY, scaledW, scaledH);\n params.ctx.restore();\n }\n\n if (params.particlesPerCell <= 0) return;\n\n params.ctx.save();\n params.ctx.beginPath();\n params.ctx.rect(params.boardX, params.boardY, params.cellW * GRID_COLS, params.cellH * GRID_ROWS);\n params.ctx.clip();\n\n for (const cell of params.winningCells) {\n const x = params.boardX + cell.col * params.cellW;\n const y = params.boardY + cell.row * params.cellH;\n drawCellParticleBurst({\n ctx: params.ctx,\n cell,\n x,\n y,\n cycleProgress,\n easeOut,\n envelope,\n cellW: params.cellW,\n cellH: params.cellH,\n particlesPerCell: params.particlesPerCell,\n particleColorMode: params.particleColorMode,\n particleColorRgb: params.particleColorRgb,\n });\n }\n\n params.ctx.restore();\n}\n\nfunction drawWinningCellBorder(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n cellW: number,\n cellH: number,\n color: [number, number, number, number],\n elapsed: number,\n envelope: number,\n): void {\n const inset = Math.max(1, Math.floor(Math.min(cellW, cellH) * 0.04));\n const lineWidth = Math.max(1, Math.floor(Math.min(cellW, cellH) * 0.025));\n const [, , , a] = color;\n const hue = (elapsed * 0.15) % 360;\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n const halfLine = lineWidth * 0.5;\n ctx.strokeRect(\n x + inset + halfLine,\n y + inset + halfLine,\n cellW - inset * 2 - lineWidth,\n cellH - inset * 2 - lineWidth,\n );\n ctx.restore();\n}\n\nconst RAINBOW_HUE_BUCKETS = 24;\n\nfunction drawCellParticleBurst(params: {\n ctx: CanvasRenderingContext2D;\n cell: CellPosition;\n x: number;\n y: number;\n cycleProgress: number;\n easeOut: number;\n envelope: number;\n cellW: number;\n cellH: number;\n particlesPerCell: number;\n particleColorMode: 'solid' | 'rainbow';\n particleColorRgb: [number, number, number];\n}): void {\n const centerX = params.x + params.cellW * 0.5;\n const centerY = params.y + params.cellH * 0.5;\n const maxDistance = Math.min(params.cellW, params.cellH) * 0.72;\n const baseRadius = Math.min(params.cellW, params.cellH) * 0.028;\n const globalFade = (0.92 + (1 - params.cycleProgress) * 0.08) * params.envelope;\n\n const [r, g, b] = params.particleColorRgb;\n const isSolid = params.particleColorMode === 'solid';\n if (isSolid) {\n params.ctx.fillStyle = `rgb(${r},${g},${b})`;\n }\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const seedA = hash01(params.cell.col, params.cell.row, i, 1);\n const seedB = hash01(params.cell.col, params.cell.row, i, 2);\n const seedC = hash01(params.cell.col, params.cell.row, i, 3);\n const phaseOffset = hash01(params.cell.col, params.cell.row, i, 4) * 0.28;\n const twinkleSeed = hash01(params.cell.col, params.cell.row, i, 5);\n const particleT = clamp((params.cycleProgress - phaseOffset) / (1 - phaseOffset), 0, 1);\n if (particleT <= 0) continue;\n\n const direction = seedA * Math.PI * 2;\n const distance =\n maxDistance * particleT * (0.35 + seedB * 0.65) * (0.85 + params.easeOut * 0.15);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 +\n 0.9 * Math.max(0, Math.sin((params.cycleProgress * 11 + twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + seedC * 0.6) * (1 - particleT * 0.5));\n const alpha = clamp((0.9 + twinkle * 0.2) * globalFade, 0.9, 1);\n if (alpha <= 0) continue;\n\n if (isSolid) {\n params.ctx.globalAlpha = alpha;\n } else {\n const hueRaw =\n (seedA * 360 + params.cycleProgress * 240 + params.cell.col * 38 + params.cell.row * 22) %\n 360;\n const hue = Math.floor(hueRaw / (360 / RAINBOW_HUE_BUCKETS)) * (360 / RAINBOW_HUE_BUCKETS);\n params.ctx.fillStyle = `hsla(${hue},98%,64%,${alpha})`;\n }\n\n params.ctx.beginPath();\n params.ctx.arc(px, py, radius, 0, Math.PI * 2);\n params.ctx.fill();\n }\n\n params.ctx.globalAlpha = 1;\n}\n","import {\n DEFAULT_SPRITE_ELEMENTS_COUNT,\n FLOW_WIN_PARTICLES_PER_CELL_BALANCED,\n FLOW_WIN_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PARTICLES_PER_CELL_LOW,\n GRID_COLS,\n GRID_ROWS,\n} from './constants';\nimport {\n createRandomGrid,\n createZeroOffsets,\n fillOffsets,\n findMostFrequentCells,\n} from './core/grid';\nimport { RafLoop } from './core/loop';\nimport { updateOutroOffsets } from './core/outro';\nimport { SpinQueueController } from './core/spinQueue';\nimport {\n beginSpin,\n createRuntimeState,\n destroyState,\n finishSpin,\n startWinFlash,\n} from './core/state';\nimport {\n normalizeInitialSegments,\n normalizeParticleColor,\n normalizeParticleColorMode,\n normalizeQuality,\n normalizeStopGrid,\n normalizeWinningCellBorderColor,\n rowsToStopGrid,\n} from './normalize';\nimport { drawGridLayer, drawWinningEffects } from './render/canvasRenderer';\nimport type { CascadingReelConfig, CellPosition, QualityPreset, SymbolId } from './types';\nimport { normalizeSegment } from './utils/math';\n\nexport class CascadingReel {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly spinQueueController: SpinQueueController;\n private readonly spriteUrl?: string;\n private readonly spriteElementsCount: number;\n private readonly highlightInitialWinningCells: boolean;\n private readonly particleColorRgb: [number, number, number];\n private readonly particleColorMode: 'solid' | 'rainbow';\n private readonly winningCellBorderRgba: [number, number, number, number];\n private readonly quality: QualityPreset;\n\n private spriteImage: HTMLImageElement | null = null;\n private readonly rafLoop = new RafLoop();\n private readonly runtime = createRuntimeState();\n private dpr = 1;\n private width = 0;\n private height = 0;\n private cellW = 0;\n private cellH = 0;\n private boardX = 0;\n private boardY = 0;\n private scriptedCascadeQueue: number[][][] = [];\n private scriptedOutgoingGrid: SymbolId[][] | null = null;\n private scriptedPendingGrid: SymbolId[][] | null = null;\n private scriptedOutroStartedAt = 0;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private winningCells: CellPosition[] = [];\n private grid: SymbolId[][];\n private lastRafTime = 0;\n private particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n private static readonly SWAY_ENVELOPE_TAU_MS = 220;\n private static readonly PRE_SPIN_MS = 150;\n private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS = 120;\n\n public constructor(config: CascadingReelConfig) {\n this.canvas = config.canvas;\n this.container = config.container;\n this.button = config.button;\n this.spriteUrl = config.sprite;\n this.spriteElementsCount = Math.max(\n 1,\n config.spriteElementsCount ?? DEFAULT_SPRITE_ELEMENTS_COUNT,\n );\n this.highlightInitialWinningCells = config.highlightInitialWinningCells !== false;\n this.spinQueueController = new SpinQueueController(config.queuedSpinStates);\n this.particleColorRgb = normalizeParticleColor(config.particleColorRgb);\n this.particleColorMode = normalizeParticleColorMode(config.particleColorMode);\n this.winningCellBorderRgba = normalizeWinningCellBorderColor(config.winningCellBorderRgba);\n this.quality = normalizeQuality(config.quality);\n\n const context = this.canvas.getContext('2d');\n if (!context) {\n throw new Error('2D canvas context is not available');\n }\n this.ctx = context;\n this.grid = config.initialSegments\n ? normalizeInitialSegments(config.initialSegments, this.spriteElementsCount)\n : createRandomGrid(this.spriteElementsCount);\n }\n\n public async init(): Promise<void> {\n this.bindEvents();\n this.resize();\n await this.loadSpriteIfProvided();\n this.applyInitialHighlightIfNeeded();\n this.startLoop();\n }\n\n public destroy(): void {\n this.unbindEvents();\n this.rafLoop.stop();\n destroyState(this.runtime);\n this.clearWinningCells();\n }\n\n public spin(): void {\n if (this.runtime.isSpinning) return;\n if (this.runtime.queueFinished) return;\n if (!this.spinQueueController.hasPending()) {\n this.runtime.queueFinished = true;\n if (this.button) this.button.disabled = true;\n return;\n }\n\n const activeSpinState = this.spinQueueController.consume();\n const shouldHighlightCurrentSpin = !this.spinQueueController.hasPending();\n this.runtime.isSpinning = true;\n this.runtime.phase = 'preSpin';\n this.runtime.preSpinStartedAt = performance.now();\n this.runtime.activeSpinState = activeSpinState;\n this.runtime.shouldHighlightCurrentSpin = shouldHighlightCurrentSpin;\n this.runtime.hasStartedFirstSpin = true;\n\n if (this.button) this.button.disabled = true;\n }\n\n private bindEvents(): void {\n window.addEventListener('resize', this.resize);\n this.button?.addEventListener('click', this.onSpinClick);\n }\n\n private unbindEvents(): void {\n window.removeEventListener('resize', this.resize);\n this.button?.removeEventListener('click', this.onSpinClick);\n }\n\n private readonly onSpinClick = (): void => {\n if (this.runtime.isSpinning) return;\n this.spin();\n };\n\n private getNextGrid(stopGrid?: number[][]): SymbolId[][] {\n if (!stopGrid) return createRandomGrid(this.spriteElementsCount);\n return normalizeStopGrid(stopGrid, this.spriteElementsCount);\n }\n\n private update(now: number): void {\n if (!this.runtime.isSpinning) return;\n\n if (this.runtime.phase === 'preSpin') {\n if (now - this.runtime.preSpinStartedAt < CascadingReel.PRE_SPIN_MS) {\n return;\n }\n beginSpin(this.runtime, {\n activeSpinState: this.runtime.activeSpinState,\n shouldHighlightCurrentSpin: this.runtime.shouldHighlightCurrentSpin,\n startedAt: now,\n });\n this.runtime.preSpinStartedAt = 0;\n\n const scriptedSource = this.runtime.activeSpinState?.finaleSequenceRows\n ? this.runtime.activeSpinState.finaleSequenceRows.map((rows) => rowsToStopGrid(rows))\n : (this.runtime.activeSpinState?.finaleSequence ?? []);\n this.scriptedCascadeQueue = scriptedSource.map((grid) => grid.map((column) => [...column]));\n this.clearWinningCells();\n\n const stopGridSource = this.runtime.activeSpinState?.stopRows\n ? rowsToStopGrid(this.runtime.activeSpinState.stopRows)\n : this.runtime.activeSpinState?.stopGrid;\n const nextGrid = this.getNextGrid(stopGridSource);\n this.startOutroTransition(nextGrid, now);\n }\n\n if (this.runtime.phase === 'outro') {\n this.updateScriptedOutro(now);\n return;\n }\n if (this.runtime.phase === 'winFlash') return;\n }\n\n private updateScriptedOutro(now: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n now,\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n scriptedOutroStartedAt: this.scriptedOutroStartedAt,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n });\n\n if (!allOutgoingDone || !allIncomingDone) return;\n if (!this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n\n this.grid = this.scriptedPendingGrid;\n this.scriptedOutgoingGrid = null;\n this.scriptedPendingGrid = null;\n this.clearWinningCells();\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n fillOffsets(this.scriptedIncomingOffsets, 0);\n\n if (this.tryStartScriptedCascade(now)) return;\n if (!this.runtime.shouldHighlightCurrentSpin) {\n this.finishSpinWithUi();\n return;\n }\n\n this.winningCells = findMostFrequentCells(this.grid);\n startWinFlash(this.runtime, now);\n }\n\n private tryStartScriptedCascade(now: number): boolean {\n if (this.scriptedCascadeQueue.length === 0) return false;\n const nextGrid = this.scriptedCascadeQueue.shift();\n if (!nextGrid) return false;\n this.startOutroTransition(normalizeStopGrid(nextGrid, this.spriteElementsCount), now);\n return true;\n }\n\n private startOutroTransition(nextGrid: SymbolId[][], now: number): void {\n this.scriptedOutgoingGrid = this.grid.map((column) => [...column]);\n this.scriptedPendingGrid = nextGrid.map((column) => [...column]);\n fillOffsets(this.scriptedIncomingOffsets, Number.NaN);\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n }\n\n private finishSpinWithUi(): void {\n const callback = this.runtime.activeSpinState?.callback;\n finishSpin(this.runtime, this.spinQueueController.hasPending(), performance.now());\n if (this.button) this.button.disabled = this.runtime.queueFinished;\n callback?.();\n }\n\n private applyInitialHighlightIfNeeded(): void {\n if (!this.highlightInitialWinningCells) return;\n this.winningCells = findMostFrequentCells(this.grid);\n startWinFlash(this.runtime, performance.now());\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n const swayTarget = this.runtime.phase === 'outro' || this.runtime.phase === 'preSpin' ? 0 : 1;\n const winEffectsTarget =\n this.runtime.phase === 'preSpin' ? 0 : this.runtime.phase === 'winFlash' ? 1 : 0;\n if (this.lastRafTime > 0) {\n const dt = Math.min(now - this.lastRafTime, 50);\n const kSway = 1 - Math.exp(-dt / CascadingReel.SWAY_ENVELOPE_TAU_MS);\n this.runtime.swayEnvelope += (swayTarget - this.runtime.swayEnvelope) * kSway;\n const kWin = 1 - Math.exp(-dt / CascadingReel.WIN_EFFECTS_ENVELOPE_TAU_MS);\n this.runtime.winEffectsEnvelope +=\n (winEffectsTarget - this.runtime.winEffectsEnvelope) * kWin;\n }\n this.lastRafTime = now;\n\n this.ctx.clearRect(0, 0, this.width, this.height);\n if (this.runtime.phase === 'outro' && this.scriptedOutgoingGrid && this.scriptedPendingGrid) {\n this.drawLayer(\n this.scriptedOutgoingGrid,\n this.scriptedOutgoingOffsets,\n CascadingReel.ZERO_SWAY,\n now,\n );\n this.drawLayer(\n this.scriptedPendingGrid,\n this.scriptedIncomingOffsets,\n CascadingReel.ZERO_SWAY,\n now,\n );\n } else {\n this.drawBaseGrid(now);\n }\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: this.runtime.phase,\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: this.runtime.winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: this.particlesPerCell,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: this.drawSpriteCell,\n });\n }\n\n private drawBaseGrid(now: number): void {\n this.drawLayer(this.grid, null, CascadingReel.ZERO_SWAY, now);\n }\n\n private drawLayer(\n grid: SymbolId[][],\n offsets: number[][] | null,\n columnSway: [number, number, number],\n now: number,\n ): void {\n drawGridLayer({\n ctx: this.ctx,\n phase: this.runtime.phase,\n now,\n swayEnvelope: this.runtime.swayEnvelope,\n grid,\n offsets,\n columnSway,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n width: this.width,\n height: this.height,\n isWinningCell: this.isWinningCell,\n drawSpriteCell: this.drawSpriteCell,\n });\n }\n\n private readonly getCell = (col: number, row: number): SymbolId => {\n return this.grid[col][row];\n };\n\n private readonly isWinningCell = (col: number, row: number): boolean => {\n return this.winningCells.some((cell) => cell.col === col && cell.row === row);\n };\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n const image = this.spriteImage;\n if (!image) return;\n\n const segmentIndex = normalizeSegment(symbolId, this.spriteElementsCount);\n const segmentHeight = image.height / this.spriteElementsCount;\n const sourceY = Math.floor(segmentIndex * segmentHeight);\n const sourceHeight = Math.floor(segmentHeight);\n\n this.ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n };\n\n private clearWinningCells(): void {\n this.winningCells = [];\n }\n\n private readonly resize = (): void => {\n const bounds = this.container.getBoundingClientRect();\n this.dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));\n const side = Math.max(300, Math.floor(bounds.width * this.dpr));\n this.width = side;\n this.height = side;\n const squareSize = Math.floor(Math.min(this.width / GRID_COLS, this.height / GRID_ROWS));\n this.cellW = squareSize;\n this.cellH = squareSize;\n this.boardX = Math.floor((this.width - this.cellW * GRID_COLS) / 2);\n this.boardY = Math.floor((this.height - this.cellH * GRID_ROWS) / 2);\n\n if (this.quality === 'high') this.particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n else if (this.quality === 'balanced')\n this.particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_BALANCED;\n else if (this.quality === 'low') this.particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_LOW;\n else\n this.particlesPerCell =\n this.dpr >= 2 ? FLOW_WIN_PARTICLES_PER_CELL_BALANCED : FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n };\n\n private async loadSpriteIfProvided(): Promise<void> {\n if (!this.spriteUrl) return;\n const image = new Image();\n image.decoding = 'async';\n image.src = this.spriteUrl;\n try {\n await image.decode();\n this.spriteImage = image;\n } catch {\n this.spriteImage = null;\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.update(time);\n this.render(time);\n return true;\n });\n }\n}\n"],"names":["DEFAULT_SPRITE_ELEMENTS_COUNT","GRID_COLS","GRID_ROWS","FLOW_OUTRO_ROW_GAP_MS","FLOW_ROW_BASE_SPACING_RATIO","FLOW_WIN_FLASH_MS","FLOW_WIN_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","FLOW_WIN_PARTICLES_PER_CELL_HIGH","FLOW_WIN_PARTICLES_PER_CELL_BALANCED","FLOW_WIN_PARTICLES_PER_CELL_LOW","DEFAULT_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","clamp","value","min","max","easeOutCubic","randomInt","maxExclusive","normalizeSegment","segment","elementsCount","normalizeRgbChannel","normalizeAlphaChannel","createRandomGrid","spriteElementsCount","grid","col","column","row","findMostFrequentCells","counts","symbol","selectedSymbol","maxCount","count","cells","createZeroOffsets","fillOffsets","offsets","RafLoop","step","now","buildSequentialRowStartDelays","fromRowOffsets","duration","gapMs","delays","nextDelay","baseSpacing","updateOutroOffsets","params","allOutgoingDone","allIncomingDone","outgoingDistance","outgoingOffsetsForOrder","incomingFromOffsets","rowStartDelays","incomingStartShift","columnElapsed","rowElapsed","tOut","easedOut","incomingElapsed","tIn","easedIn","normalizeParticleColor","color","normalizeParticleColorMode","mode","normalizeQuality","preset","normalizeWinningCellBorderColor","rowsToStopGrid","rows","normalizeStopGrid","stopGrid","next","normalizeInitialSegments","initialSegments","cloneSpinState","state","SpinQueueController","initialQueue","entry","createRuntimeState","beginSpin","finishSpin","hasPendingInQueue","startWinFlash","startedAt","destroyState","SWAY_AMPLITUDE_DEG","SWAY_OMEGA","drawGridLayer","sway","swayAmplitudeRad","envelope","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","drawWinningEffects","elapsed","cycleProgress","pulseProgress","pulse","easeOut","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","inset","lineWidth","hue","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","i","seedA","seedB","seedC","phaseOffset","twinkleSeed","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","columnSway","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","time"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class CascadingReel {
|
|
|
10
10
|
private readonly particleColorRgb;
|
|
11
11
|
private readonly particleColorMode;
|
|
12
12
|
private readonly winningCellBorderRgba;
|
|
13
|
+
private readonly quality;
|
|
13
14
|
private spriteImage;
|
|
14
15
|
private readonly rafLoop;
|
|
15
16
|
private readonly runtime;
|
|
@@ -28,6 +29,11 @@ export declare class CascadingReel {
|
|
|
28
29
|
private scriptedIncomingOffsets;
|
|
29
30
|
private winningCells;
|
|
30
31
|
private grid;
|
|
32
|
+
private lastRafTime;
|
|
33
|
+
private particlesPerCell;
|
|
34
|
+
private static readonly SWAY_ENVELOPE_TAU_MS;
|
|
35
|
+
private static readonly PRE_SPIN_MS;
|
|
36
|
+
private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS;
|
|
31
37
|
constructor(config: CascadingReelConfig);
|
|
32
38
|
init(): Promise<void>;
|
|
33
39
|
destroy(): void;
|
|
@@ -67,8 +73,13 @@ export declare type CascadingReelConfig = {
|
|
|
67
73
|
particleColorRgb?: [number, number, number];
|
|
68
74
|
particleColorMode?: 'solid' | 'rainbow';
|
|
69
75
|
winningCellBorderRgba?: [number, number, number, number];
|
|
76
|
+
/** Win-effect particle density. 'auto' reduces on high-DPR/mobile for smoother animation. */
|
|
77
|
+
quality?: QualityPreset;
|
|
70
78
|
};
|
|
71
79
|
|
|
80
|
+
/** Quality preset: 'auto' uses device hints for mobile; 'high'/'balanced'/'low' fix particle count. */
|
|
81
|
+
export declare type QualityPreset = 'auto' | 'high' | 'balanced' | 'low';
|
|
82
|
+
|
|
72
83
|
export declare type SpinState = {
|
|
73
84
|
stopGrid?: number[][];
|
|
74
85
|
stopRows?: number[][];
|