cascading-reel 0.0.8 → 0.0.9

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 CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=6,g=3,f=3,Z=34,j=.05,y=1800,K=.1,x=720,_=34,H=20,T=12,J=[255,235,110],tt=[255,255,255,.78],it=190,R=650,et=220,nt=200;function m(t,i,e){return t<i?i:t>e?e:t}function q(t){return 1-(1-t)**3}function st(t){return Math.floor(Math.random()*t)}function I(t,i){return(t%i+i)%i}function b(t){return m(Math.round(t),0,255)}function lt(t){return m(t,0,1)}function G(t){const i=[];for(let e=0;e<g;e+=1){const n=[];for(let l=0;l<f;l+=1)n.push(st(t));i.push(n)}return i}function N(t){const i=new Map;for(let s=0;s<g;s+=1)for(let r=0;r<f;r+=1){const u=t[s][r];i.set(u,(i.get(u)??0)+1)}let e=t[0][0],n=-1;for(const[s,r]of i.entries())r>n&&(n=r,e=s);const l=[];for(let s=0;s<g;s+=1)for(let r=0;r<f;r+=1)t[s][r]===e&&l.push({col:s,row:r});return l}function Y(){return Array.from({length:g},()=>Array.from({length:f},()=>0))}function W(t,i){for(let e=0;e<g;e+=1)for(let n=0;n<f;n+=1)t[e][n]=i}class rt{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 ot(t,i,e){const n=[0,0,0];let l=0;for(let s=f-1;s>=0;s-=1){if(t[s]===0){n[s]=0;continue}n[s]=l;const r=Math.floor(i*j);l+=r+e}return n}function ht(t){let i=!0,e=!0;const l=t.height-t.boardY+t.cellH+2,s=[l,l,l],r=[-t.cellH,-t.cellH*2,-t.cellH*3],u=ot(s,R,Z),o=R-et;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const a=t.now-(t.scriptedOutroStartedAt+h*it);for(let c=0;c<f;c+=1){const d=a-u[c];if(d<=0){t.scriptedOutgoingOffsets[h][c]=0,t.scriptedIncomingOffsets[h][c]=Number.NaN,i=!1,e=!1;continue}const p=m(d/R,0,1),S=q(p);t.scriptedOutgoingOffsets[h][c]=l*S,p<1&&(i=!1);const E=d-o;if(E<=0){t.scriptedIncomingOffsets[h][c]=Number.NaN,e=!1;continue}const C=m(E/R,0,1),O=q(C);t.scriptedIncomingOffsets[h][c]=r[c]*(1-O),C<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function ct(t){return t?[b(t[0]),b(t[1]),b(t[2])]:J}function ut(t){return t==="rainbow"?"rainbow":"solid"}function at(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function dt(t){return t?[b(t[0]),b(t[1]),b(t[2]),lt(t[3])]:tt}function F(t){if(t.length!==f)throw new Error(`rows must contain ${f} rows`);for(let i=0;i<f;i+=1)if(!Array.isArray(t[i])||t[i].length!==g)throw new Error(`rows[${i}] must contain ${g} 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 v(t,i){if(t.length!==g)throw new Error(`stopGrid must contain ${g} columns`);const e=[];for(let n=0;n<g;n+=1){const l=t[n];if(!Array.isArray(l)||l.length!==f)throw new Error(`stopGrid[${n}] must contain ${f} rows`);e[n]=[I(l[0],i),I(l[1],i),I(l[2],i)]}return e}function ft(t,i){return v(F(t),i)}function gt(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 pt{queue;constructor(i){this.queue=(i??[]).map(e=>gt(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function St(){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 Ct(t,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=i.startedAt,t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function wt(t,i,e){t.phase="idle",t.idleStartedAt=e,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function D(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function Et(t){t.isSpinning=!1,t.queueFinished=!0}const Ot=3,bt=.002;function At(t){const i=t.columnSway??[0,0,0],e=Ot*(Math.PI/180),n=Math.max(0,Math.min(1,t.swayEnvelope)),l=t.skipWinningCells??!1;for(let s=0;s<g;s+=1){const r=t.boardX+s*t.cellW,u=i[s]??0;for(let o=0;o<f;o+=1){if((l||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 a=t.boardY+o*t.cellH+h+u;if(a>t.height||a+t.cellH<0)continue;const d=Math.sin(t.now*bt+s*1.1+o*1.3)*e*n,p=r+t.cellW*.5,S=a+t.cellH*.5;t.ctx.save(),t.ctx.translate(p,S),t.ctx.rotate(d),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 A(t,i,e,n){const l=Math.sin(t*127.1+i*311.7+e*74.7+n*19.3)*43758.5453;return l-Math.floor(l)}const U=new Map;function mt(t,i){const e=`${t},${i}`;let n=U.get(e);if(n)return n;n=[];for(let l=0;l<_;l+=1)n.push({seedA:A(t,i,l,1),seedB:A(t,i,l,2),seedC:A(t,i,l,3),phaseOffset:A(t,i,l,4),twinkleSeed:A(t,i,l,5)});return U.set(e,n),n}function k(t){if(t.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),e=Math.max(0,t.now-t.winFlashStartedAt),n=e%y/y,l=1+Math.sin(n*Math.PI*2)*K;for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;Mt(t.ctx,r,u,t.cellW,t.cellH,s.col,s.row,t.winningCellBorderRgba,e,i);const o=t.getCell(s.col,s.row),h=1+(l-1)*i,a=t.cellW*h,c=t.cellH*h,d=(t.cellW-a)*.5,p=(t.cellH-c)*.5;t.ctx.save(),t.drawSpriteCell(o,r+d,u+p,a,c),t.ctx.restore()}if(!(t.particlesPerCell<=0)){t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*g,t.cellH*f),t.ctx.clip();for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;Rt({ctx:t.ctx,cell:s,x:r,y:u,elapsed:e,envelope:i,cellW:t.cellW,cellH:t.cellH,particlesPerCell:t.particlesPerCell,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}t.ctx.restore()}}function Mt(t,i,e,n,l,s,r,u,o,h){const a=Math.max(1,Math.floor(Math.min(n,l)*.04)),c=Math.max(1,Math.floor(Math.min(n,l)*.025)),[,,,d]=u,p=(s+.5)/g,S=(r+.5)/f,E=(o*.15+p*80+S*45)%360,C=Math.min(n,l),O=Math.max(4,Math.floor(C*.04)),L=Math.max(3,Math.floor(C*.03));t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${E}, 90%, 70%, ${d*h})`,t.setLineDash([O,L]),t.lineDashOffset=-o*.03;const M=c*.5;t.strokeRect(i+a+M,e+a+M,n-a*2-c,l-a*2-c),t.setLineDash([]),t.restore()}const B=24;function Rt(t){const i=t.x+t.cellW*.5,e=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,l=Math.min(t.cellW,t.cellH)*.028,s=.96*t.envelope,[r,u,o]=t.particleColorRgb,h=t.particleColorMode==="solid";h&&(t.ctx.fillStyle=`rgb(${r},${u},${o})`);const a=mt(t.cell.col,t.cell.row);for(let c=0;c<t.particlesPerCell;c+=1){const d=a[c],p=d.phaseOffset*x,S=t.elapsed-p;if(S<0)continue;const E=S%x/x,C=d.seedA*Math.PI*2,O=n*E*(.35+d.seedB*.65),L=i+Math.cos(C)*O,M=e+Math.sin(C)*O,z=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+d.twinkleSeed*2)*Math.PI*2)),X=Math.max(1,l*(.55+d.seedC*.6)*(1-E*.5)),P=m((.9+z*.2)*s,.9,1);if(!(P<=0)){if(h)t.ctx.globalAlpha=P;else{const $=(d.seedA*360+t.elapsed*.24+t.cell.col*38+t.cell.row*22)%360,Q=Math.floor($/(360/B))*(360/B);t.ctx.fillStyle=`hsla(${Q},98%,64%,${P})`}t.ctx.beginPath(),t.ctx.arc(L,M,X,0,Math.PI*2),t.ctx.fill()}}t.ctx.globalAlpha=1}class w{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;quality;spriteImage=null;rafLoop=new rt;runtime=St();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutgoingOffsets=Y();scriptedIncomingOffsets=Y();winningCells=[];grid;lastRafTime=0;particlesPerCell=_;initialHighlightRequestedAt=0;static SWAY_ENVELOPE_TAU_MS=220;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;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??V),this.highlightInitialWinningCells=i.highlightInitialWinningCells!==!1,this.spinQueueController=new pt(i.queuedSpinStates),this.particleColorRgb=ct(i.particleColorRgb),this.particleColorMode=ut(i.particleColorMode),this.winningCellBorderRgba=dt(i.winningCellBorderRgba),this.quality=at(i.quality);const e=this.canvas.getContext("2d");if(!e)throw new Error("2D canvas context is not available");this.ctx=e,this.grid=i.initialSegments?ft(i.initialSegments,this.spriteElementsCount):G(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.prewarmWinEffects(),requestAnimationFrame(i=>{this.render(i),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),Et(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();this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=i,this.runtime.shouldHighlightCurrentSpin=e,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(i){return i?v(i,this.spriteElementsCount):G(this.spriteElementsCount)}update(i){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(i-this.runtime.preSpinStartedAt<w.PRE_SPIN_MS)return;Ct(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:i}),this.runtime.preSpinStartedAt=0;const e=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(s=>F(s)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=e.map(s=>s.map(r=>[...r])),this.clearWinningCells();const n=this.runtime.activeSpinState?.stopRows?F(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,l=this.getNextGrid(n);this.startOutroTransition(l,i)}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}=ht({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(),W(this.scriptedOutgoingOffsets,0),W(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=N(this.grid),D(this.runtime,i)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(v(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),W(this.scriptedIncomingOffsets,Number.NaN),W(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(){const i=this.runtime.activeSpinState?.callback;wt(this.runtime,this.spinQueueController.hasPending(),performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=N(this.grid),this.initialHighlightRequestedAt=performance.now())}static ZERO_SWAY=[0,0,0];render(i){this.initialHighlightRequestedAt>0&&i-this.initialHighlightRequestedAt>=nt&&(D(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0);const e=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 u=Math.min(i-this.lastRafTime,50),o=1-Math.exp(-u/w.SWAY_ENVELOPE_TAU_MS);this.runtime.swayEnvelope+=(e-this.runtime.swayEnvelope)*o;const h=1-Math.exp(-u/w.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(n-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=i,this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,w.ZERO_SWAY,i),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,w.ZERO_SWAY,i)):this.drawBaseGrid(i);const l=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,s=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,r=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;k({ctx:this.ctx,now:i,phase:l,winFlashStartedAt:s,winEffectsEnvelope:r,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin?this.particlesPerCell:0,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(i){this.drawLayer(this.grid,null,w.ZERO_SWAY,i)}drawLayer(i,e,n,l){const s=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;At({ctx:this.ctx,phase:this.runtime.phase,now:l,swayEnvelope:this.runtime.swayEnvelope,grid:i,offsets:e,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,skipWinningCells:s,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);drawSpriteCellToCtx(i,e,n,l,s,r){const u=this.spriteImage;if(!u)return;const o=I(e,this.spriteElementsCount),h=u.height/this.spriteElementsCount,a=Math.floor(o*h),c=Math.floor(h);i.drawImage(u,0,a,u.width,c,n,l,s,r)}drawSpriteCell=(i,e,n,l,s)=>{this.drawSpriteCellToCtx(this.ctx,i,e,n,l,s)};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/g,this.height/f));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*g)/2),this.boardY=Math.floor((this.height-this.cellH*f)/2),this.quality==="high"?this.particlesPerCell=_:this.quality==="balanced"?this.particlesPerCell=H:this.quality==="low"?this.particlesPerCell=T:this.particlesPerCell=this.dpr>=2?H:_,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}}prewarmWinEffects(){if(this.winningCells.length===0)return;const i=document.createElement("canvas");i.width=this.width,i.height=this.height;const e=i.getContext("2d");if(!e)return;const n=this.runtime.phase,l=this.runtime.winFlashStartedAt;this.runtime.phase="winFlash",this.runtime.winFlashStartedAt=performance.now()-300;const s=performance.now(),r=Math.min(this.particlesPerCell,T),u=(o,h,a,c,d)=>{this.drawSpriteCellToCtx(e,o,h,a,c,d)};for(let o=0;o<2;o+=1)k({ctx:e,now:s+o*16,phase:"winFlash",winFlashStartedAt:this.runtime.winFlashStartedAt,winEffectsEnvelope:1,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:r,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:u});this.runtime.phase=n,this.runtime.winFlashStartedAt=l}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(i=>(this.update(i),this.render(i),!0))}}exports.CascadingReel=w;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=6,g=3,f=3,Z=34,j=.05,y=1800,K=.1,x=720,_=34,H=20,T=12,J=[255,235,110],tt=[255,255,255,.78],it=190,R=650,et=220,nt=200;function m(t,i,e){return t<i?i:t>e?e:t}function q(t){return 1-(1-t)**3}function st(t){return Math.floor(Math.random()*t)}function I(t,i){return(t%i+i)%i}function b(t){return m(Math.round(t),0,255)}function lt(t){return m(t,0,1)}function G(t){const i=[];for(let e=0;e<g;e+=1){const n=[];for(let l=0;l<f;l+=1)n.push(st(t));i.push(n)}return i}function N(t){const i=new Map;for(let s=0;s<g;s+=1)for(let r=0;r<f;r+=1){const u=t[s][r];i.set(u,(i.get(u)??0)+1)}let e=t[0][0],n=-1;for(const[s,r]of i.entries())r>n&&(n=r,e=s);const l=[];for(let s=0;s<g;s+=1)for(let r=0;r<f;r+=1)t[s][r]===e&&l.push({col:s,row:r});return l}function Y(){return Array.from({length:g},()=>Array.from({length:f},()=>0))}function W(t,i){for(let e=0;e<g;e+=1)for(let n=0;n<f;n+=1)t[e][n]=i}class rt{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 ot(t,i,e){const n=[0,0,0];let l=0;for(let s=f-1;s>=0;s-=1){if(t[s]===0){n[s]=0;continue}n[s]=l;const r=Math.floor(i*j);l+=r+e}return n}function ht(t){let i=!0,e=!0;const l=t.height-t.boardY+t.cellH+2,s=[l,l,l],r=[-t.cellH,-t.cellH*2,-t.cellH*3],u=ot(s,R,Z),o=R-et;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const a=t.now-(t.scriptedOutroStartedAt+h*it);for(let c=0;c<f;c+=1){const d=a-u[c];if(d<=0){t.scriptedOutgoingOffsets[h][c]=0,t.scriptedIncomingOffsets[h][c]=Number.NaN,i=!1,e=!1;continue}const p=m(d/R,0,1),S=q(p);t.scriptedOutgoingOffsets[h][c]=l*S,p<1&&(i=!1);const E=d-o;if(E<=0){t.scriptedIncomingOffsets[h][c]=Number.NaN,e=!1;continue}const C=m(E/R,0,1),O=q(C);t.scriptedIncomingOffsets[h][c]=r[c]*(1-O),C<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function ct(t){return t?[b(t[0]),b(t[1]),b(t[2])]:J}function ut(t){return t==="rainbow"?"rainbow":"solid"}function at(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function dt(t){return t?[b(t[0]),b(t[1]),b(t[2]),lt(t[3])]:tt}function F(t){if(t.length!==f)throw new Error(`rows must contain ${f} rows`);for(let i=0;i<f;i+=1)if(!Array.isArray(t[i])||t[i].length!==g)throw new Error(`rows[${i}] must contain ${g} 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 v(t,i){if(t.length!==g)throw new Error(`stopGrid must contain ${g} columns`);const e=[];for(let n=0;n<g;n+=1){const l=t[n];if(!Array.isArray(l)||l.length!==f)throw new Error(`stopGrid[${n}] must contain ${f} rows`);e[n]=[I(l[0],i),I(l[1],i),I(l[2],i)]}return e}function ft(t,i){return v(F(t),i)}function gt(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 pt{queue;constructor(i){this.queue=(i??[]).map(e=>gt(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function St(){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 Ct(t,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=i.startedAt,t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function wt(t,i,e){t.phase="idle",t.idleStartedAt=e,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function D(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function Et(t){t.isSpinning=!1,t.queueFinished=!0}const Ot=3,bt=.002;function At(t){const i=t.columnSway??[0,0,0],e=Ot*(Math.PI/180),n=Math.max(0,Math.min(1,t.swayEnvelope)),l=t.skipWinningCells??!1;for(let s=0;s<g;s+=1){const r=t.boardX+s*t.cellW,u=i[s]??0;for(let o=0;o<f;o+=1){if((l||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 a=t.boardY+o*t.cellH+h+u;if(a>t.height||a+t.cellH<0)continue;const d=Math.sin(t.now*bt+s*1.1+o*1.3)*e*n,p=r+t.cellW*.5,S=a+t.cellH*.5;t.ctx.save(),t.ctx.translate(p,S),t.ctx.rotate(d),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 A(t,i,e,n){const l=Math.sin(t*127.1+i*311.7+e*74.7+n*19.3)*43758.5453;return l-Math.floor(l)}const U=new Map;function mt(t,i){const e=`${t},${i}`;let n=U.get(e);if(n)return n;n=[];for(let l=0;l<_;l+=1)n.push({seedA:A(t,i,l,1),seedB:A(t,i,l,2),seedC:A(t,i,l,3),phaseOffset:A(t,i,l,4),twinkleSeed:A(t,i,l,5)});return U.set(e,n),n}function k(t){if(t.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),e=Math.max(0,t.now-t.winFlashStartedAt),n=e%y/y,l=1+Math.sin(n*Math.PI*2)*K;for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;Mt(t.ctx,r,u,t.cellW,t.cellH,s.col,s.row,t.winningCellBorderRgba,e,i);const o=t.getCell(s.col,s.row),h=1+(l-1)*i,a=t.cellW*h,c=t.cellH*h,d=(t.cellW-a)*.5,p=(t.cellH-c)*.5;t.ctx.save(),t.drawSpriteCell(o,r+d,u+p,a,c),t.ctx.restore()}if(!(t.particlesPerCell<=0)){t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*g,t.cellH*f),t.ctx.clip();for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;Rt({ctx:t.ctx,cell:s,x:r,y:u,elapsed:e,envelope:i,cellW:t.cellW,cellH:t.cellH,particlesPerCell:t.particlesPerCell,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}t.ctx.restore()}}function Mt(t,i,e,n,l,s,r,u,o,h){const a=Math.max(1,Math.floor(Math.min(n,l)*.04)),c=Math.max(1,Math.floor(Math.min(n,l)*.03)),[,,,d]=u,p=(s+.5)/g,S=(r+.5)/f,E=(o*.2+p*80+S*40)%360,C=Math.min(n,l),O=Math.max(4,Math.floor(C*.04)),L=Math.max(3,Math.floor(C*.03));t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${E}, 90%, 70%, ${d*h})`,t.setLineDash([O,L]),t.lineDashOffset=-o*.03;const M=c*.5;t.strokeRect(i+a+M,e+a+M,n-a*2-c,l-a*2-c),t.setLineDash([]),t.restore()}const B=24;function Rt(t){const i=t.x+t.cellW*.5,e=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,l=Math.min(t.cellW,t.cellH)*.028,s=.96*t.envelope,[r,u,o]=t.particleColorRgb,h=t.particleColorMode==="solid";h&&(t.ctx.fillStyle=`rgb(${r},${u},${o})`);const a=mt(t.cell.col,t.cell.row);for(let c=0;c<t.particlesPerCell;c+=1){const d=a[c],p=d.phaseOffset*x,S=t.elapsed-p;if(S<0)continue;const E=S%x/x,C=d.seedA*Math.PI*2,O=n*E*(.35+d.seedB*.65),L=i+Math.cos(C)*O,M=e+Math.sin(C)*O,z=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+d.twinkleSeed*2)*Math.PI*2)),X=Math.max(1,l*(.55+d.seedC*.6)*(1-E*.5)),P=m((.9+z*.2)*s,.9,1);if(!(P<=0)){if(h)t.ctx.globalAlpha=P;else{const $=(d.seedA*360+t.elapsed*.24+t.cell.col*38+t.cell.row*22)%360,Q=Math.floor($/(360/B))*(360/B);t.ctx.fillStyle=`hsla(${Q},98%,64%,${P})`}t.ctx.beginPath(),t.ctx.arc(L,M,X,0,Math.PI*2),t.ctx.fill()}}t.ctx.globalAlpha=1}class w{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;quality;spriteImage=null;rafLoop=new rt;runtime=St();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutgoingOffsets=Y();scriptedIncomingOffsets=Y();winningCells=[];grid;lastRafTime=0;particlesPerCell=_;initialHighlightRequestedAt=0;static SWAY_ENVELOPE_TAU_MS=220;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;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??V),this.highlightInitialWinningCells=i.highlightInitialWinningCells!==!1,this.spinQueueController=new pt(i.queuedSpinStates),this.particleColorRgb=ct(i.particleColorRgb),this.particleColorMode=ut(i.particleColorMode),this.winningCellBorderRgba=dt(i.winningCellBorderRgba),this.quality=at(i.quality);const e=this.canvas.getContext("2d");if(!e)throw new Error("2D canvas context is not available");this.ctx=e,this.grid=i.initialSegments?ft(i.initialSegments,this.spriteElementsCount):G(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.prewarmWinEffects(),requestAnimationFrame(i=>{this.render(i),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),Et(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();this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=i,this.runtime.shouldHighlightCurrentSpin=e,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(i){return i?v(i,this.spriteElementsCount):G(this.spriteElementsCount)}update(i){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(i-this.runtime.preSpinStartedAt<w.PRE_SPIN_MS)return;Ct(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:i}),this.runtime.preSpinStartedAt=0;const e=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(s=>F(s)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=e.map(s=>s.map(r=>[...r])),this.clearWinningCells();const n=this.runtime.activeSpinState?.stopRows?F(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,l=this.getNextGrid(n);this.startOutroTransition(l,i)}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}=ht({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(),W(this.scriptedOutgoingOffsets,0),W(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=N(this.grid),D(this.runtime,i)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(v(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),W(this.scriptedIncomingOffsets,Number.NaN),W(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(){const i=this.runtime.activeSpinState?.callback;wt(this.runtime,this.spinQueueController.hasPending(),performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=N(this.grid),this.initialHighlightRequestedAt=performance.now())}static ZERO_SWAY=[0,0,0];render(i){this.initialHighlightRequestedAt>0&&i-this.initialHighlightRequestedAt>=nt&&(D(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0);const e=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 u=Math.min(i-this.lastRafTime,50),o=1-Math.exp(-u/w.SWAY_ENVELOPE_TAU_MS);this.runtime.swayEnvelope+=(e-this.runtime.swayEnvelope)*o;const h=1-Math.exp(-u/w.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(n-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=i,this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,w.ZERO_SWAY,i),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,w.ZERO_SWAY,i)):this.drawBaseGrid(i);const l=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,s=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,r=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;k({ctx:this.ctx,now:i,phase:l,winFlashStartedAt:s,winEffectsEnvelope:r,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin?this.particlesPerCell:0,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(i){this.drawLayer(this.grid,null,w.ZERO_SWAY,i)}drawLayer(i,e,n,l){const s=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;At({ctx:this.ctx,phase:this.runtime.phase,now:l,swayEnvelope:this.runtime.swayEnvelope,grid:i,offsets:e,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,skipWinningCells:s,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);drawSpriteCellToCtx(i,e,n,l,s,r){const u=this.spriteImage;if(!u)return;const o=I(e,this.spriteElementsCount),h=u.height/this.spriteElementsCount,a=Math.floor(o*h),c=Math.floor(h);i.drawImage(u,0,a,u.width,c,n,l,s,r)}drawSpriteCell=(i,e,n,l,s)=>{this.drawSpriteCellToCtx(this.ctx,i,e,n,l,s)};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/g,this.height/f));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*g)/2),this.boardY=Math.floor((this.height-this.cellH*f)/2),this.quality==="high"?this.particlesPerCell=_:this.quality==="balanced"?this.particlesPerCell=H:this.quality==="low"?this.particlesPerCell=T:this.particlesPerCell=this.dpr>=2?H:_,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}}prewarmWinEffects(){if(this.winningCells.length===0)return;const i=document.createElement("canvas");i.width=this.width,i.height=this.height;const e=i.getContext("2d");if(!e)return;const n=this.runtime.phase,l=this.runtime.winFlashStartedAt;this.runtime.phase="winFlash",this.runtime.winFlashStartedAt=performance.now()-300;const s=performance.now(),r=Math.min(this.particlesPerCell,T),u=(o,h,a,c,d)=>{this.drawSpriteCellToCtx(e,o,h,a,c,d)};for(let o=0;o<2;o+=1)k({ctx:e,now:s+o*16,phase:"winFlash",winFlashStartedAt:this.runtime.winFlashStartedAt,winEffectsEnvelope:1,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:r,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:u});this.runtime.phase=n,this.runtime.winFlashStartedAt=l}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(i=>(this.update(i),this.render(i),!0))}}exports.CascadingReel=w;
2
2
  //# sourceMappingURL=index.cjs.js.map
@@ -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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.15 + localX * 80 + localY * 45) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","PARTICLE_FLY_DURATION_MS","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","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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.03));\n const [, , , a] = color;\n\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.2 + localX * 80 + localY * 40) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","PARTICLE_FLY_DURATION_MS","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","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","time"],"mappings":"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"}
package/dist/index.es.js CHANGED
@@ -310,7 +310,7 @@ function T(t) {
310
310
  }
311
311
  }
312
312
  function ft(t, i, e, n, l, s, r, u, o, h) {
313
- const a = Math.max(1, Math.floor(Math.min(n, l) * 0.04)), c = Math.max(1, Math.floor(Math.min(n, l) * 0.025)), [, , , d] = u, f = (s + 0.5) / 3, g = (r + 0.5) / 3, p = (o * 0.15 + f * 80 + g * 45) % 360, S = Math.min(n, l), _ = Math.max(4, Math.floor(S * 0.04)), A = Math.max(3, Math.floor(S * 0.03));
313
+ const a = Math.max(1, Math.floor(Math.min(n, l) * 0.04)), c = Math.max(1, Math.floor(Math.min(n, l) * 0.03)), [, , , d] = u, f = (s + 0.5) / 3, g = (r + 0.5) / 3, p = (o * 0.2 + f * 80 + g * 40) % 360, S = Math.min(n, l), _ = Math.max(4, Math.floor(S * 0.04)), A = Math.max(3, Math.floor(S * 0.03));
314
314
  t.save(), t.lineWidth = c, t.strokeStyle = `hsla(${p}, 90%, 70%, ${d * h})`, t.setLineDash([_, A]), t.lineDashOffset = -o * 0.03;
315
315
  const w = c * 0.5;
316
316
  t.strokeRect(
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.15 + localX * 80 + localY * 45) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","time"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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.03));\n const [, , , a] = color;\n\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.2 + localX * 80 + localY * 40) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","time"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(_,I){typeof exports=="object"&&typeof module<"u"?I(exports):typeof define=="function"&&define.amd?define(["exports"],I):(_=typeof globalThis<"u"?globalThis:_||self,I(_.CascadingReel={}))})(this,(function(_){"use strict";const q=[255,235,110],U=[255,255,255,.78],Y=190,L=650,B=220,k=200;function E(t,i,e){return t<i?i:t>e?e:t}function G(t){return 1-(1-t)**3}function z(t){return Math.floor(Math.random()*t)}function W(t,i){return(t%i+i)%i}function R(t){return E(Math.round(t),0,255)}function X(t){return E(t,0,1)}function D(t){const i=[];for(let e=0;e<3;e+=1){const n=[];for(let l=0;l<3;l+=1)n.push(z(t));i.push(n)}return i}function H(t){const i=new Map;for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1){const u=t[s][r];i.set(u,(i.get(u)??0)+1)}let e=t[0][0],n=-1;for(const[s,r]of i.entries())r>n&&(n=r,e=s);const l=[];for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1)t[s][r]===e&&l.push({col:s,row:r});return l}function T(){return Array.from({length:3},()=>Array.from({length:3},()=>0))}function A(t,i){for(let e=0;e<3;e+=1)for(let n=0;n<3;n+=1)t[e][n]=i}class ${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 Q(t,i,e){const n=[0,0,0];let l=0;for(let s=2;s>=0;s-=1){if(t[s]===0){n[s]=0;continue}n[s]=l;const r=Math.floor(i*.05);l+=r+e}return n}function V(t){let i=!0,e=!0;const l=t.height-t.boardY+t.cellH+2,s=[l,l,l],r=[-t.cellH,-t.cellH*2,-t.cellH*3],u=Q(s,L,34),o=L-B;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const a=t.now-(t.scriptedOutroStartedAt+h*Y);for(let c=0;c<3;c+=1){const d=a-u[c];if(d<=0){t.scriptedOutgoingOffsets[h][c]=0,t.scriptedIncomingOffsets[h][c]=Number.NaN,i=!1,e=!1;continue}const f=E(d/L,0,1),S=G(f);t.scriptedOutgoingOffsets[h][c]=l*S,f<1&&(i=!1);const C=d-o;if(C<=0){t.scriptedIncomingOffsets[h][c]=Number.NaN,e=!1;continue}const p=E(C/L,0,1),O=G(p);t.scriptedIncomingOffsets[h][c]=r[c]*(1-O),p<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function Z(t){return t?[R(t[0]),R(t[1]),R(t[2])]:q}function j(t){return t==="rainbow"?"rainbow":"solid"}function K(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function J(t){return t?[R(t[0]),R(t[1]),R(t[2]),X(t[3])]:U}function P(t){if(t.length!==3)throw new Error("rows must contain 3 rows");for(let i=0;i<3;i+=1)if(!Array.isArray(t[i])||t[i].length!==3)throw new Error(`rows[${i}] must contain 3 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 M(t,i){if(t.length!==3)throw new Error("stopGrid must contain 3 columns");const e=[];for(let n=0;n<3;n+=1){const l=t[n];if(!Array.isArray(l)||l.length!==3)throw new Error(`stopGrid[${n}] must contain 3 rows`);e[n]=[W(l[0],i),W(l[1],i),W(l[2],i)]}return e}function tt(t,i){return M(P(t),i)}function it(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 et{queue;constructor(i){this.queue=(i??[]).map(e=>it(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function nt(){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,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=i.startedAt,t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function lt(t,i,e){t.phase="idle",t.idleStartedAt=e,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function x(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function rt(t){t.isSpinning=!1,t.queueFinished=!0}const ot=3,ht=.002;function ct(t){const i=t.columnSway??[0,0,0],e=ot*(Math.PI/180),n=Math.max(0,Math.min(1,t.swayEnvelope)),l=t.skipWinningCells??!1;for(let s=0;s<3;s+=1){const r=t.boardX+s*t.cellW,u=i[s]??0;for(let o=0;o<3;o+=1){if((l||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 a=t.boardY+o*t.cellH+h+u;if(a>t.height||a+t.cellH<0)continue;const d=Math.sin(t.now*ht+s*1.1+o*1.3)*e*n,f=r+t.cellW*.5,S=a+t.cellH*.5;t.ctx.save(),t.ctx.translate(f,S),t.ctx.rotate(d),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,i,e,n){const l=Math.sin(t*127.1+i*311.7+e*74.7+n*19.3)*43758.5453;return l-Math.floor(l)}const y=new Map;function ut(t,i){const e=`${t},${i}`;let n=y.get(e);if(n)return n;n=[];for(let l=0;l<34;l+=1)n.push({seedA:w(t,i,l,1),seedB:w(t,i,l,2),seedC:w(t,i,l,3),phaseOffset:w(t,i,l,4),twinkleSeed:w(t,i,l,5)});return y.set(e,n),n}function v(t){if(t.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),e=Math.max(0,t.now-t.winFlashStartedAt),n=e%1800/1800,l=1+Math.sin(n*Math.PI*2)*.1;for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;at(t.ctx,r,u,t.cellW,t.cellH,s.col,s.row,t.winningCellBorderRgba,e,i);const o=t.getCell(s.col,s.row),h=1+(l-1)*i,a=t.cellW*h,c=t.cellH*h,d=(t.cellW-a)*.5,f=(t.cellH-c)*.5;t.ctx.save(),t.drawSpriteCell(o,r+d,u+f,a,c),t.ctx.restore()}if(!(t.particlesPerCell<=0)){t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*3,t.cellH*3),t.ctx.clip();for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;dt({ctx:t.ctx,cell:s,x:r,y:u,elapsed:e,envelope:i,cellW:t.cellW,cellH:t.cellH,particlesPerCell:t.particlesPerCell,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}t.ctx.restore()}}function at(t,i,e,n,l,s,r,u,o,h){const a=Math.max(1,Math.floor(Math.min(n,l)*.04)),c=Math.max(1,Math.floor(Math.min(n,l)*.025)),[,,,d]=u,f=(s+.5)/3,S=(r+.5)/3,C=(o*.15+f*80+S*45)%360,p=Math.min(n,l),O=Math.max(4,Math.floor(p*.04)),m=Math.max(3,Math.floor(p*.03));t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${C}, 90%, 70%, ${d*h})`,t.setLineDash([O,m]),t.lineDashOffset=-o*.03;const b=c*.5;t.strokeRect(i+a+b,e+a+b,n-a*2-c,l-a*2-c),t.setLineDash([]),t.restore()}const N=24;function dt(t){const i=t.x+t.cellW*.5,e=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,l=Math.min(t.cellW,t.cellH)*.028,s=.96*t.envelope,[r,u,o]=t.particleColorRgb,h=t.particleColorMode==="solid";h&&(t.ctx.fillStyle=`rgb(${r},${u},${o})`);const a=ut(t.cell.col,t.cell.row);for(let c=0;c<t.particlesPerCell;c+=1){const d=a[c],f=d.phaseOffset*720,S=t.elapsed-f;if(S<0)continue;const C=S%720/720,p=d.seedA*Math.PI*2,O=n*C*(.35+d.seedB*.65),m=i+Math.cos(p)*O,b=e+Math.sin(p)*O,ft=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+d.twinkleSeed*2)*Math.PI*2)),gt=Math.max(1,l*(.55+d.seedC*.6)*(1-C*.5)),F=E((.9+ft*.2)*s,.9,1);if(!(F<=0)){if(h)t.ctx.globalAlpha=F;else{const St=(d.seedA*360+t.elapsed*.24+t.cell.col*38+t.cell.row*22)%360,pt=Math.floor(St/(360/N))*(360/N);t.ctx.fillStyle=`hsla(${pt},98%,64%,${F})`}t.ctx.beginPath(),t.ctx.arc(m,b,gt,0,Math.PI*2),t.ctx.fill()}}t.ctx.globalAlpha=1}class g{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;quality;spriteImage=null;rafLoop=new $;runtime=nt();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=34;initialHighlightRequestedAt=0;static SWAY_ENVELOPE_TAU_MS=220;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;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??6),this.highlightInitialWinningCells=i.highlightInitialWinningCells!==!1,this.spinQueueController=new et(i.queuedSpinStates),this.particleColorRgb=Z(i.particleColorRgb),this.particleColorMode=j(i.particleColorMode),this.winningCellBorderRgba=J(i.winningCellBorderRgba),this.quality=K(i.quality);const e=this.canvas.getContext("2d");if(!e)throw new Error("2D canvas context is not available");this.ctx=e,this.grid=i.initialSegments?tt(i.initialSegments,this.spriteElementsCount):D(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.prewarmWinEffects(),requestAnimationFrame(i=>{this.render(i),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),rt(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();this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=i,this.runtime.shouldHighlightCurrentSpin=e,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(i){return i?M(i,this.spriteElementsCount):D(this.spriteElementsCount)}update(i){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(i-this.runtime.preSpinStartedAt<g.PRE_SPIN_MS)return;st(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:i}),this.runtime.preSpinStartedAt=0;const e=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(s=>P(s)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=e.map(s=>s.map(r=>[...r])),this.clearWinningCells();const n=this.runtime.activeSpinState?.stopRows?P(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,l=this.getNextGrid(n);this.startOutroTransition(l,i)}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}=V({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(),A(this.scriptedOutgoingOffsets,0),A(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=H(this.grid),x(this.runtime,i)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(M(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),A(this.scriptedIncomingOffsets,Number.NaN),A(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(){const i=this.runtime.activeSpinState?.callback;lt(this.runtime,this.spinQueueController.hasPending(),performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=H(this.grid),this.initialHighlightRequestedAt=performance.now())}static ZERO_SWAY=[0,0,0];render(i){this.initialHighlightRequestedAt>0&&i-this.initialHighlightRequestedAt>=k&&(x(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0);const e=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 u=Math.min(i-this.lastRafTime,50),o=1-Math.exp(-u/g.SWAY_ENVELOPE_TAU_MS);this.runtime.swayEnvelope+=(e-this.runtime.swayEnvelope)*o;const h=1-Math.exp(-u/g.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(n-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=i,this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,g.ZERO_SWAY,i),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,g.ZERO_SWAY,i)):this.drawBaseGrid(i);const l=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,s=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,r=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;v({ctx:this.ctx,now:i,phase:l,winFlashStartedAt:s,winEffectsEnvelope:r,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin?this.particlesPerCell:0,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(i){this.drawLayer(this.grid,null,g.ZERO_SWAY,i)}drawLayer(i,e,n,l){const s=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;ct({ctx:this.ctx,phase:this.runtime.phase,now:l,swayEnvelope:this.runtime.swayEnvelope,grid:i,offsets:e,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,skipWinningCells:s,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);drawSpriteCellToCtx(i,e,n,l,s,r){const u=this.spriteImage;if(!u)return;const o=W(e,this.spriteElementsCount),h=u.height/this.spriteElementsCount,a=Math.floor(o*h),c=Math.floor(h);i.drawImage(u,0,a,u.width,c,n,l,s,r)}drawSpriteCell=(i,e,n,l,s)=>{this.drawSpriteCellToCtx(this.ctx,i,e,n,l,s)};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/3,this.height/3));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*3)/2),this.boardY=Math.floor((this.height-this.cellH*3)/2),this.quality==="high"?this.particlesPerCell=34:this.quality==="balanced"?this.particlesPerCell=20:this.quality==="low"?this.particlesPerCell=12:this.particlesPerCell=this.dpr>=2?20:34,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}}prewarmWinEffects(){if(this.winningCells.length===0)return;const i=document.createElement("canvas");i.width=this.width,i.height=this.height;const e=i.getContext("2d");if(!e)return;const n=this.runtime.phase,l=this.runtime.winFlashStartedAt;this.runtime.phase="winFlash",this.runtime.winFlashStartedAt=performance.now()-300;const s=performance.now(),r=Math.min(this.particlesPerCell,12),u=(o,h,a,c,d)=>{this.drawSpriteCellToCtx(e,o,h,a,c,d)};for(let o=0;o<2;o+=1)v({ctx:e,now:s+o*16,phase:"winFlash",winFlashStartedAt:this.runtime.winFlashStartedAt,winEffectsEnvelope:1,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:r,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:u});this.runtime.phase=n,this.runtime.winFlashStartedAt=l}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(i=>(this.update(i),this.render(i),!0))}}_.CascadingReel=g,Object.defineProperty(_,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(_,I){typeof exports=="object"&&typeof module<"u"?I(exports):typeof define=="function"&&define.amd?define(["exports"],I):(_=typeof globalThis<"u"?globalThis:_||self,I(_.CascadingReel={}))})(this,(function(_){"use strict";const q=[255,235,110],U=[255,255,255,.78],Y=190,L=650,B=220,k=200;function E(t,i,e){return t<i?i:t>e?e:t}function G(t){return 1-(1-t)**3}function z(t){return Math.floor(Math.random()*t)}function W(t,i){return(t%i+i)%i}function R(t){return E(Math.round(t),0,255)}function X(t){return E(t,0,1)}function D(t){const i=[];for(let e=0;e<3;e+=1){const n=[];for(let l=0;l<3;l+=1)n.push(z(t));i.push(n)}return i}function H(t){const i=new Map;for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1){const u=t[s][r];i.set(u,(i.get(u)??0)+1)}let e=t[0][0],n=-1;for(const[s,r]of i.entries())r>n&&(n=r,e=s);const l=[];for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1)t[s][r]===e&&l.push({col:s,row:r});return l}function T(){return Array.from({length:3},()=>Array.from({length:3},()=>0))}function A(t,i){for(let e=0;e<3;e+=1)for(let n=0;n<3;n+=1)t[e][n]=i}class ${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 Q(t,i,e){const n=[0,0,0];let l=0;for(let s=2;s>=0;s-=1){if(t[s]===0){n[s]=0;continue}n[s]=l;const r=Math.floor(i*.05);l+=r+e}return n}function V(t){let i=!0,e=!0;const l=t.height-t.boardY+t.cellH+2,s=[l,l,l],r=[-t.cellH,-t.cellH*2,-t.cellH*3],u=Q(s,L,34),o=L-B;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const a=t.now-(t.scriptedOutroStartedAt+h*Y);for(let c=0;c<3;c+=1){const d=a-u[c];if(d<=0){t.scriptedOutgoingOffsets[h][c]=0,t.scriptedIncomingOffsets[h][c]=Number.NaN,i=!1,e=!1;continue}const f=E(d/L,0,1),S=G(f);t.scriptedOutgoingOffsets[h][c]=l*S,f<1&&(i=!1);const C=d-o;if(C<=0){t.scriptedIncomingOffsets[h][c]=Number.NaN,e=!1;continue}const p=E(C/L,0,1),O=G(p);t.scriptedIncomingOffsets[h][c]=r[c]*(1-O),p<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function Z(t){return t?[R(t[0]),R(t[1]),R(t[2])]:q}function j(t){return t==="rainbow"?"rainbow":"solid"}function K(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function J(t){return t?[R(t[0]),R(t[1]),R(t[2]),X(t[3])]:U}function P(t){if(t.length!==3)throw new Error("rows must contain 3 rows");for(let i=0;i<3;i+=1)if(!Array.isArray(t[i])||t[i].length!==3)throw new Error(`rows[${i}] must contain 3 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 M(t,i){if(t.length!==3)throw new Error("stopGrid must contain 3 columns");const e=[];for(let n=0;n<3;n+=1){const l=t[n];if(!Array.isArray(l)||l.length!==3)throw new Error(`stopGrid[${n}] must contain 3 rows`);e[n]=[W(l[0],i),W(l[1],i),W(l[2],i)]}return e}function tt(t,i){return M(P(t),i)}function it(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 et{queue;constructor(i){this.queue=(i??[]).map(e=>it(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function nt(){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,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=i.startedAt,t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function lt(t,i,e){t.phase="idle",t.idleStartedAt=e,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function x(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function rt(t){t.isSpinning=!1,t.queueFinished=!0}const ot=3,ht=.002;function ct(t){const i=t.columnSway??[0,0,0],e=ot*(Math.PI/180),n=Math.max(0,Math.min(1,t.swayEnvelope)),l=t.skipWinningCells??!1;for(let s=0;s<3;s+=1){const r=t.boardX+s*t.cellW,u=i[s]??0;for(let o=0;o<3;o+=1){if((l||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 a=t.boardY+o*t.cellH+h+u;if(a>t.height||a+t.cellH<0)continue;const d=Math.sin(t.now*ht+s*1.1+o*1.3)*e*n,f=r+t.cellW*.5,S=a+t.cellH*.5;t.ctx.save(),t.ctx.translate(f,S),t.ctx.rotate(d),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,i,e,n){const l=Math.sin(t*127.1+i*311.7+e*74.7+n*19.3)*43758.5453;return l-Math.floor(l)}const y=new Map;function ut(t,i){const e=`${t},${i}`;let n=y.get(e);if(n)return n;n=[];for(let l=0;l<34;l+=1)n.push({seedA:w(t,i,l,1),seedB:w(t,i,l,2),seedC:w(t,i,l,3),phaseOffset:w(t,i,l,4),twinkleSeed:w(t,i,l,5)});return y.set(e,n),n}function v(t){if(t.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const i=Math.max(0,Math.min(1,t.winEffectsEnvelope)),e=Math.max(0,t.now-t.winFlashStartedAt),n=e%1800/1800,l=1+Math.sin(n*Math.PI*2)*.1;for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;at(t.ctx,r,u,t.cellW,t.cellH,s.col,s.row,t.winningCellBorderRgba,e,i);const o=t.getCell(s.col,s.row),h=1+(l-1)*i,a=t.cellW*h,c=t.cellH*h,d=(t.cellW-a)*.5,f=(t.cellH-c)*.5;t.ctx.save(),t.drawSpriteCell(o,r+d,u+f,a,c),t.ctx.restore()}if(!(t.particlesPerCell<=0)){t.ctx.save(),t.ctx.beginPath(),t.ctx.rect(t.boardX,t.boardY,t.cellW*3,t.cellH*3),t.ctx.clip();for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH;dt({ctx:t.ctx,cell:s,x:r,y:u,elapsed:e,envelope:i,cellW:t.cellW,cellH:t.cellH,particlesPerCell:t.particlesPerCell,particleColorMode:t.particleColorMode,particleColorRgb:t.particleColorRgb})}t.ctx.restore()}}function at(t,i,e,n,l,s,r,u,o,h){const a=Math.max(1,Math.floor(Math.min(n,l)*.04)),c=Math.max(1,Math.floor(Math.min(n,l)*.03)),[,,,d]=u,f=(s+.5)/3,S=(r+.5)/3,C=(o*.2+f*80+S*40)%360,p=Math.min(n,l),O=Math.max(4,Math.floor(p*.04)),m=Math.max(3,Math.floor(p*.03));t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${C}, 90%, 70%, ${d*h})`,t.setLineDash([O,m]),t.lineDashOffset=-o*.03;const b=c*.5;t.strokeRect(i+a+b,e+a+b,n-a*2-c,l-a*2-c),t.setLineDash([]),t.restore()}const N=24;function dt(t){const i=t.x+t.cellW*.5,e=t.y+t.cellH*.5,n=Math.min(t.cellW,t.cellH)*.72,l=Math.min(t.cellW,t.cellH)*.028,s=.96*t.envelope,[r,u,o]=t.particleColorRgb,h=t.particleColorMode==="solid";h&&(t.ctx.fillStyle=`rgb(${r},${u},${o})`);const a=ut(t.cell.col,t.cell.row);for(let c=0;c<t.particlesPerCell;c+=1){const d=a[c],f=d.phaseOffset*720,S=t.elapsed-f;if(S<0)continue;const C=S%720/720,p=d.seedA*Math.PI*2,O=n*C*(.35+d.seedB*.65),m=i+Math.cos(p)*O,b=e+Math.sin(p)*O,ft=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+d.twinkleSeed*2)*Math.PI*2)),gt=Math.max(1,l*(.55+d.seedC*.6)*(1-C*.5)),F=E((.9+ft*.2)*s,.9,1);if(!(F<=0)){if(h)t.ctx.globalAlpha=F;else{const St=(d.seedA*360+t.elapsed*.24+t.cell.col*38+t.cell.row*22)%360,pt=Math.floor(St/(360/N))*(360/N);t.ctx.fillStyle=`hsla(${pt},98%,64%,${F})`}t.ctx.beginPath(),t.ctx.arc(m,b,gt,0,Math.PI*2),t.ctx.fill()}}t.ctx.globalAlpha=1}class g{canvas;container;button;ctx;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;winningCellBorderRgba;quality;spriteImage=null;rafLoop=new $;runtime=nt();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=34;initialHighlightRequestedAt=0;static SWAY_ENVELOPE_TAU_MS=220;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;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??6),this.highlightInitialWinningCells=i.highlightInitialWinningCells!==!1,this.spinQueueController=new et(i.queuedSpinStates),this.particleColorRgb=Z(i.particleColorRgb),this.particleColorMode=j(i.particleColorMode),this.winningCellBorderRgba=J(i.winningCellBorderRgba),this.quality=K(i.quality);const e=this.canvas.getContext("2d");if(!e)throw new Error("2D canvas context is not available");this.ctx=e,this.grid=i.initialSegments?tt(i.initialSegments,this.spriteElementsCount):D(this.spriteElementsCount)}async init(){this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),this.applyInitialHighlightIfNeeded(),this.prewarmWinEffects(),requestAnimationFrame(i=>{this.render(i),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),rt(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();this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=i,this.runtime.shouldHighlightCurrentSpin=e,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(i){return i?M(i,this.spriteElementsCount):D(this.spriteElementsCount)}update(i){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(i-this.runtime.preSpinStartedAt<g.PRE_SPIN_MS)return;st(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:i}),this.runtime.preSpinStartedAt=0;const e=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(s=>P(s)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=e.map(s=>s.map(r=>[...r])),this.clearWinningCells();const n=this.runtime.activeSpinState?.stopRows?P(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,l=this.getNextGrid(n);this.startOutroTransition(l,i)}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}=V({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(),A(this.scriptedOutgoingOffsets,0),A(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=H(this.grid),x(this.runtime,i)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(M(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),A(this.scriptedIncomingOffsets,Number.NaN),A(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(){const i=this.runtime.activeSpinState?.callback;lt(this.runtime,this.spinQueueController.hasPending(),performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),i?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=H(this.grid),this.initialHighlightRequestedAt=performance.now())}static ZERO_SWAY=[0,0,0];render(i){this.initialHighlightRequestedAt>0&&i-this.initialHighlightRequestedAt>=k&&(x(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0);const e=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 u=Math.min(i-this.lastRafTime,50),o=1-Math.exp(-u/g.SWAY_ENVELOPE_TAU_MS);this.runtime.swayEnvelope+=(e-this.runtime.swayEnvelope)*o;const h=1-Math.exp(-u/g.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(n-this.runtime.winEffectsEnvelope)*h}this.lastRafTime=i,this.ctx.clearRect(0,0,this.width,this.height),this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawLayer(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,g.ZERO_SWAY,i),this.drawLayer(this.scriptedPendingGrid,this.scriptedIncomingOffsets,g.ZERO_SWAY,i)):this.drawBaseGrid(i);const l=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,s=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,r=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;v({ctx:this.ctx,now:i,phase:l,winFlashStartedAt:s,winEffectsEnvelope:r,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin?this.particlesPerCell:0,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:this.drawSpriteCell})}drawBaseGrid(i){this.drawLayer(this.grid,null,g.ZERO_SWAY,i)}drawLayer(i,e,n,l){const s=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;ct({ctx:this.ctx,phase:this.runtime.phase,now:l,swayEnvelope:this.runtime.swayEnvelope,grid:i,offsets:e,columnSway:n,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,width:this.width,height:this.height,skipWinningCells:s,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);drawSpriteCellToCtx(i,e,n,l,s,r){const u=this.spriteImage;if(!u)return;const o=W(e,this.spriteElementsCount),h=u.height/this.spriteElementsCount,a=Math.floor(o*h),c=Math.floor(h);i.drawImage(u,0,a,u.width,c,n,l,s,r)}drawSpriteCell=(i,e,n,l,s)=>{this.drawSpriteCellToCtx(this.ctx,i,e,n,l,s)};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/3,this.height/3));this.cellW=n,this.cellH=n,this.boardX=Math.floor((this.width-this.cellW*3)/2),this.boardY=Math.floor((this.height-this.cellH*3)/2),this.quality==="high"?this.particlesPerCell=34:this.quality==="balanced"?this.particlesPerCell=20:this.quality==="low"?this.particlesPerCell=12:this.particlesPerCell=this.dpr>=2?20:34,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}}prewarmWinEffects(){if(this.winningCells.length===0)return;const i=document.createElement("canvas");i.width=this.width,i.height=this.height;const e=i.getContext("2d");if(!e)return;const n=this.runtime.phase,l=this.runtime.winFlashStartedAt;this.runtime.phase="winFlash",this.runtime.winFlashStartedAt=performance.now()-300;const s=performance.now(),r=Math.min(this.particlesPerCell,12),u=(o,h,a,c,d)=>{this.drawSpriteCellToCtx(e,o,h,a,c,d)};for(let o=0;o<2;o+=1)v({ctx:e,now:s+o*16,phase:"winFlash",winFlashStartedAt:this.runtime.winFlashStartedAt,winEffectsEnvelope:1,winningCells:this.winningCells,boardX:this.boardX,boardY:this.boardY,cellW:this.cellW,cellH:this.cellH,particlesPerCell:r,particleColorMode:this.particleColorMode,particleColorRgb:this.particleColorRgb,winningCellBorderRgba:this.winningCellBorderRgba,getCell:this.getCell,drawSpriteCell:u});this.runtime.phase=n,this.runtime.winFlashStartedAt=l}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(i=>(this.update(i),this.render(i),!0))}}_.CascadingReel=g,Object.defineProperty(_,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.15 + localX * 80 + localY * 45) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","time"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.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.1;\n/** Duration for one particle to fly from center to edge (continuous spray). */\nexport const PARTICLE_FLY_DURATION_MS = 720;\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/** Delay before starting initial win-effect so canvas/JIT can warm up. */\nexport const INITIAL_WIN_FLASH_DELAY_MS = 200;\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_PARTICLES_PER_CELL_HIGH,\n FLOW_WIN_PULSE_AMPLITUDE,\n FLOW_WIN_PULSE_PERIOD_MS,\n GRID_COLS,\n GRID_ROWS,\n PARTICLE_FLY_DURATION_MS,\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 /** When true, winning cells are not drawn here (they are drawn by win effects). */\n skipWinningCells?: boolean;\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 const skipWinning = params.skipWinningCells ?? false;\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 (skipWinning || 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\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nconst particleSeedsCache = new Map<string, ParticleSeeds[]>();\n\nfunction getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n let seeds = particleSeedsCache.get(key);\n if (seeds) return seeds;\n\n seeds = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n seeds.push({\n seedA: hash01(col, row, i, 1),\n seedB: hash01(col, row, i, 2),\n seedC: hash01(col, row, i, 3),\n phaseOffset: hash01(col, row, i, 4),\n twinkleSeed: hash01(col, row, i, 5),\n });\n }\n particleSeedsCache.set(key, seeds);\n return seeds;\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 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\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 cell.col,\n cell.row,\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 elapsed,\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 cellCol: number,\n cellRow: 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.03));\n const [, , , a] = color;\n\n const localX = (cellCol + 0.5) / GRID_COLS;\n const localY = (cellRow + 0.5) / GRID_ROWS;\n const hue = (elapsed * 0.2 + localX * 80 + localY * 40) % 360;\n\n const minSide = Math.min(cellW, cellH);\n const dashLength = Math.max(4, Math.floor(minSide * 0.04));\n const gapLength = Math.max(3, Math.floor(minSide * 0.03));\n\n ctx.save();\n ctx.lineWidth = lineWidth;\n ctx.strokeStyle = `hsla(${hue}, 90%, 70%, ${a * envelope})`;\n ctx.setLineDash([dashLength, gapLength]);\n ctx.lineDashOffset = -elapsed * 0.03;\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.setLineDash([]);\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 elapsed: 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.96 * 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 const seedsList = getParticleSeeds(params.cell.col, params.cell.row);\n\n for (let i = 0; i < params.particlesPerCell; i += 1) {\n const s = seedsList[i];\n const startTime = s.phaseOffset * PARTICLE_FLY_DURATION_MS;\n const age = params.elapsed - startTime;\n if (age < 0) continue;\n const particleT = (age % PARTICLE_FLY_DURATION_MS) / PARTICLE_FLY_DURATION_MS;\n\n const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = centerX + Math.cos(direction) * distance;\n const py = centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 + 0.9 * Math.max(0, Math.sin((params.elapsed * 0.012 + s.twinkleSeed * 2) * Math.PI * 2));\n const radius = Math.max(1, baseRadius * (0.55 + s.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 (s.seedA * 360 + params.elapsed * 0.24 + params.cell.col * 38 + params.cell.row * 22) % 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 INITIAL_WIN_FLASH_DELAY_MS,\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 initialHighlightRequestedAt = 0;\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.prewarmWinEffects();\n requestAnimationFrame((warmNow) => {\n this.render(warmNow);\n this.startLoop();\n });\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 this.initialHighlightRequestedAt = performance.now();\n }\n\n private static readonly ZERO_SWAY: [number, number, number] = [0, 0, 0];\n\n private render(now: number): void {\n if (\n this.initialHighlightRequestedAt > 0 &&\n now - this.initialHighlightRequestedAt >= INITIAL_WIN_FLASH_DELAY_MS\n ) {\n startWinFlash(this.runtime, this.initialHighlightRequestedAt);\n this.runtime.winEffectsEnvelope = 1;\n this.initialHighlightRequestedAt = 0;\n }\n\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 const winPhase = this.initialHighlightRequestedAt > 0 ? 'winFlash' : this.runtime.phase;\n const winFlashStartedAt =\n this.initialHighlightRequestedAt > 0\n ? this.initialHighlightRequestedAt\n : this.runtime.winFlashStartedAt;\n const winEffectsEnvelope =\n this.initialHighlightRequestedAt > 0 ? 1 : this.runtime.winEffectsEnvelope;\n\n drawWinningEffects({\n ctx: this.ctx,\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell:\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin\n ? this.particlesPerCell\n : 0,\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 const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\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 skipWinningCells,\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 drawSpriteCellToCtx(\n ctx: CanvasRenderingContext2D,\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 ctx.drawImage(image, 0, sourceY, image.width, sourceHeight, x, y, width, height);\n }\n\n private readonly drawSpriteCell = (\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n ): void => {\n this.drawSpriteCellToCtx(this.ctx, symbolId, 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 prewarmWinEffects(): void {\n if (this.winningCells.length === 0) return;\n\n const offscreen = document.createElement('canvas');\n offscreen.width = this.width;\n offscreen.height = this.height;\n const offscreenCtx = offscreen.getContext('2d');\n if (!offscreenCtx) return;\n\n const savedPhase = this.runtime.phase;\n const savedWinFlashStartedAt = this.runtime.winFlashStartedAt;\n\n this.runtime.phase = 'winFlash';\n this.runtime.winFlashStartedAt = performance.now() - 300;\n\n const prewarmNow = performance.now();\n const particlesPrewarm = Math.min(this.particlesPerCell, FLOW_WIN_PARTICLES_PER_CELL_LOW);\n\n const drawSpriteToOffscreen = (\n symbolId: SymbolId,\n x: number,\n y: number,\n w: number,\n h: number,\n ): void => {\n this.drawSpriteCellToCtx(offscreenCtx, symbolId, x, y, w, h);\n };\n\n for (let frame = 0; frame < 2; frame += 1) {\n drawWinningEffects({\n ctx: offscreenCtx,\n now: prewarmNow + frame * 16,\n phase: 'winFlash',\n winFlashStartedAt: this.runtime.winFlashStartedAt,\n winEffectsEnvelope: 1,\n winningCells: this.winningCells,\n boardX: this.boardX,\n boardY: this.boardY,\n cellW: this.cellW,\n cellH: this.cellH,\n particlesPerCell: particlesPrewarm,\n particleColorMode: this.particleColorMode,\n particleColorRgb: this.particleColorRgb,\n winningCellBorderRgba: this.winningCellBorderRgba,\n getCell: this.getCell,\n drawSpriteCell: drawSpriteToOffscreen,\n });\n }\n\n this.runtime.phase = savedPhase;\n this.runtime.winFlashStartedAt = savedWinFlashStartedAt;\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_PARTICLE_COLOR_RGB","DEFAULT_WINNING_CELL_BORDER_RGBA","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","FLOW_OUTRO_OVERLAP_MS","INITIAL_WIN_FLASH_DELAY_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","skipWinning","x","swayY","offsetY","y","angle","centerX","centerY","hash01","a","b","c","d","particleSeedsCache","getParticleSeeds","key","seeds","i","drawWinningEffects","elapsed","pulseProgress","pulse","cell","drawWinningCellBorder","scale","scaledW","scaledH","offsetX","drawCellParticleBurst","ctx","cellW","cellH","cellCol","cellRow","inset","lineWidth","localX","localY","hue","minSide","dashLength","gapLength","halfLine","RAINBOW_HUE_BUCKETS","maxDistance","baseRadius","globalFade","g","isSolid","seedsList","s","startTime","age","particleT","direction","distance","px","py","twinkle","radius","alpha","hueRaw","CascadingReel","config","context","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","callback","swayTarget","winEffectsTarget","dt","kSway","kWin","winPhase","winFlashStartedAt","winEffectsEnvelope","columnSway","skipWinningCells","symbolId","width","height","image","segmentIndex","segmentHeight","sourceY","sourceHeight","bounds","side","squareSize","offscreen","offscreenCtx","savedPhase","savedWinFlashStartedAt","prewarmNow","particlesPrewarm","drawSpriteToOffscreen","w","h","frame","time"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cascading-reel",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Canvas-based cascading reel animation for slot-style UIs.",
5
5
  "author": "ux-ui.pro",
6
6
  "license": "MIT",