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 +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const 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
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/outro.ts","../src/normalize.ts","../src/core/spinQueue.ts","../src/core/state.ts","../src/render/canvasRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const FLOW_OUTRO_ROW_GAP_MS = 34;\nexport const FLOW_ROW_BASE_SPACING_RATIO = 0.05;\nexport const FLOW_WIN_FLASH_MS = 1200;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.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.
|
|
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(
|
package/dist/index.es.js.map
CHANGED
|
@@ -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
|
package/dist/index.umd.js.map
CHANGED
|
@@ -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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"}
|