cascading-reel 0.0.12 → 0.0.13
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 +53 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +25 -18
- package/dist/index.es.js +522 -426
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +53 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
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 ROW_COMPACT_OFFSETS_RATIO: [number, number, number] = [0.04, 0, -0.04];\nexport const FLOW_OUTRO_ROW_GAP_MS = 34;\nexport const FLOW_ROW_BASE_SPACING_RATIO = 0.05;\n/** Длительность фазы подсветки выигрыша (мс). Подсветка отключается по таймауту. */\nexport const FLOW_WIN_FLASH_MS = 5000;\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 highlightWin: state.highlightWin,\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 ROW_COMPACT_OFFSETS_RATIO,\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\nfunction getRowCompactOffset(row: number, cellH: number): number {\n return (ROW_COMPACT_OFFSETS_RATIO[row] ?? 0) * cellH;\n}\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 =\n params.boardY +\n row * params.cellH +\n offsetY +\n swayY +\n getRowCompactOffset(row, params.cellH);\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 + getRowCompactOffset(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 + getRowCompactOffset(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_FLASH_MS,\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 = activeSpinState?.highlightWin === true;\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 // Подсветка до клика: в winFlash с подсветкой кнопка включена, по клику завершаем и при наличии очереди запускаем следующий спин\n if (\n this.runtime.phase === 'winFlash' &&\n (this.runtime.shouldHighlightCurrentSpin || !this.runtime.hasStartedFirstSpin)\n ) {\n // Только закрываем подсветку и запускаем следующий спин; callback уже вызван по окончании прокрутки\n this.finishSpinWithUi(true);\n if (this.spinQueueController.hasPending()) this.spin();\n return;\n }\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') {\n // Подсветка без ограничения по времени: выход только по клику «Spin»\n if (\n !this.runtime.shouldHighlightCurrentSpin &&\n now - this.runtime.winFlashStartedAt >= FLOW_WIN_FLASH_MS\n ) {\n this.finishSpinWithUi();\n }\n return;\n }\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 // Callback по окончании прокрутки, а не по клику\n this.runtime.activeSpinState?.callback?.();\n // Включаем кнопку только если в очереди есть следующий спин; после последнего — оставляем disabled\n if (\n this.button &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.spinQueueController.hasPending()\n )\n this.button.disabled = false;\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 /** @param skipCallback true при закрытии подсветки по клику (callback уже вызван по окончании прокрутки) */\n private finishSpinWithUi(skipCallback = false): void {\n const finishedSpinState = this.runtime.activeSpinState;\n const callback = skipCallback ? undefined : finishedSpinState?.callback;\n const hasPending = this.spinQueueController.hasPending();\n finishSpin(this.runtime, 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 if (this.button) this.button.disabled = false;\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":["ROW_COMPACT_OFFSETS_RATIO","FLOW_OUTRO_ROW_GAP_MS","FLOW_ROW_BASE_SPACING_RATIO","FLOW_WIN_FLASH_MS","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","getRowCompactOffset","cellH","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","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","skipCallback","finishedSpinState","callback","hasPending","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/webglRenderer.ts","../src/CascadingReel.ts"],"sourcesContent":["export const DEFAULT_SPRITE_ELEMENTS_COUNT = 6;\nexport const GRID_COLS = 3;\nexport const GRID_ROWS = 3;\nexport const ROW_COMPACT_OFFSETS_RATIO: [number, number, number] = [0.04, 0, -0.04];\nexport const FLOW_OUTRO_ROW_GAP_MS = 34;\nexport const FLOW_ROW_BASE_SPACING_RATIO = 0.05;\n/** Длительность фазы подсветки выигрыша (мс). Подсветка отключается по таймауту. */\nexport const FLOW_WIN_FLASH_MS = 5000;\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 DEFAULT_PARTICLE_COLOR_RGB: [number, number, number] = [255, 235, 110];\nexport const FLOW_COLUMN_STAGGER_MS = 190;\nexport const FLOW_FALL_MS = 520;\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 easeOutQuad(t: number): number {\n return 1 - (1 - t) ** 2;\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_ROW_GAP_MS,\n FLOW_ROW_BASE_SPACING_RATIO,\n GRID_ROWS,\n} from '../constants';\nimport { clamp } from '../utils/math';\n\nfunction easeFall(t: number): number {\n const x = clamp(t, 0, 1);\n // Smoothstep: мягкий старт и мягкое завершение без визуального \"снэпа\" в конце.\n return x * x * (3 - 2 * x);\n}\n\nexport type OutroMotionPlan = {\n outgoingDistance: number;\n incomingFromOffsets: [number, number, number];\n rowStartDelays: [number, number, number];\n incomingStartShift: number;\n};\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 buildOutroMotionPlan(params: {\n height: number;\n boardY: number;\n cellH: number;\n}): OutroMotionPlan {\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 return {\n outgoingDistance,\n incomingFromOffsets: [-params.cellH, -params.cellH * 2, -params.cellH * 3],\n rowStartDelays: buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n FLOW_FALL_MS,\n FLOW_OUTRO_ROW_GAP_MS,\n ),\n // Start incoming symbols only after outgoing ones fully complete to avoid\n // perceived jitter from overlapping opposite-direction visual motion.\n incomingStartShift: FLOW_FALL_MS,\n };\n}\n\nexport function updateOutroOffsets(params: {\n now: number;\n scriptedOutroStartedAt: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n motionPlan: OutroMotionPlan;\n}): { allOutgoingDone: boolean; allIncomingDone: boolean } {\n let allOutgoingDone = true;\n let allIncomingDone = true;\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 - params.motionPlan.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 = easeFall(tOut);\n params.scriptedOutgoingOffsets[col][row] = params.motionPlan.outgoingDistance * easedOut;\n if (tOut < 1) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - params.motionPlan.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 = easeFall(tIn);\n params.scriptedIncomingOffsets[col][row] =\n params.motionPlan.incomingFromOffsets[row] * (1 - easedIn);\n if (tIn < 1) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import { DEFAULT_PARTICLE_COLOR_RGB, GRID_COLS, GRID_ROWS } from './constants';\nimport type { SpinState, SymbolId } from './types';\nimport { 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 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 highlightWin: state.highlightWin,\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 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 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 type { SymbolId } from '../types';\nimport { normalizeSegment } from '../utils/math';\n\ntype WebGLUniforms = {\n resolution: WebGLUniformLocation | null;\n destRect: WebGLUniformLocation | null;\n srcRect: WebGLUniformLocation | null;\n color: WebGLUniformLocation | null;\n useTexture: WebGLUniformLocation | null;\n shapeMode: WebGLUniformLocation | null;\n texture: WebGLUniformLocation | null;\n};\n\nconst VERTEX_SHADER_SOURCE = `\nattribute vec2 a_pos;\nuniform vec2 u_resolution;\nuniform vec4 u_destRect;\nvarying vec2 v_uv;\n\nvoid main() {\n vec2 local = a_pos;\n vec2 pixel = vec2(\n u_destRect.x + local.x * u_destRect.z,\n u_destRect.y + local.y * u_destRect.w\n );\n\n vec2 zeroToOne = pixel / u_resolution;\n vec2 clip = vec2(\n zeroToOne.x * 2.0 - 1.0,\n 1.0 - zeroToOne.y * 2.0\n );\n\n gl_Position = vec4(clip, 0.0, 1.0);\n v_uv = local;\n}\n`;\n\nconst FRAGMENT_SHADER_SOURCE = `\nprecision mediump float;\n\nuniform sampler2D u_texture;\nuniform vec4 u_srcRect;\nuniform vec4 u_color;\nuniform float u_useTexture;\nuniform float u_shapeMode;\nvarying vec2 v_uv;\n\nvoid main() {\n if (u_shapeMode > 0.5) {\n vec2 centered = v_uv - vec2(0.5, 0.5);\n float dist = length(centered) * 2.0;\n if (dist > 1.0) {\n discard;\n }\n float feather = smoothstep(1.0, 0.72, dist);\n gl_FragColor = vec4(u_color.rgb, u_color.a * feather);\n } else if (u_useTexture > 0.5) {\n vec2 uv = vec2(\n mix(u_srcRect.x, u_srcRect.z, v_uv.x),\n mix(u_srcRect.y, u_srcRect.w, v_uv.y)\n );\n vec4 tex = texture2D(u_texture, uv);\n gl_FragColor = tex * u_color;\n } else {\n gl_FragColor = u_color;\n }\n}\n`;\n\nexport class WebGLRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly spriteImage: HTMLImageElement;\n private readonly spriteElementsCount: number;\n\n private readonly gl: WebGLRenderingContext;\n private readonly program: WebGLProgram;\n private readonly uniforms: WebGLUniforms;\n private readonly quadBuffer: WebGLBuffer;\n private readonly texture: WebGLTexture;\n\n private viewportW = 1;\n private viewportH = 1;\n private readonly spriteWidth: number;\n private readonly spriteHeight: number;\n private readonly spriteSegmentHeight: number;\n\n public constructor(params: {\n canvas: HTMLCanvasElement;\n spriteImage: HTMLImageElement;\n spriteElementsCount: number;\n }) {\n this.canvas = params.canvas;\n this.spriteImage = params.spriteImage;\n this.spriteElementsCount = Math.max(1, params.spriteElementsCount);\n this.spriteWidth = this.spriteImage.width;\n this.spriteHeight = this.spriteImage.height;\n this.spriteSegmentHeight = this.spriteHeight / this.spriteElementsCount;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n alpha: true,\n antialias: false,\n }) as WebGLRenderingContext | null) ??\n this.canvas.getContext('webgl', { alpha: true, antialias: false });\n if (!gl) {\n throw new Error('WebGL context is not available');\n }\n this.gl = gl;\n\n const vertexShader = this.createShader(this.gl.VERTEX_SHADER, VERTEX_SHADER_SOURCE);\n const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);\n this.program = this.createProgram(vertexShader, fragmentShader);\n this.gl.deleteShader(vertexShader);\n this.gl.deleteShader(fragmentShader);\n\n const quadBuffer = this.gl.createBuffer();\n if (!quadBuffer) {\n throw new Error('Failed to create WebGL quad buffer');\n }\n this.quadBuffer = quadBuffer;\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.bufferData(\n this.gl.ARRAY_BUFFER,\n new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]),\n this.gl.STATIC_DRAW,\n );\n\n const texture = this.gl.createTexture();\n if (!texture) {\n throw new Error('Failed to create WebGL texture');\n }\n this.texture = texture;\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, 0);\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n this.spriteImage,\n );\n this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n\n this.gl.useProgram(this.program);\n const posLocation = this.gl.getAttribLocation(this.program, 'a_pos');\n this.gl.enableVertexAttribArray(posLocation);\n this.gl.vertexAttribPointer(posLocation, 2, this.gl.FLOAT, false, 8, 0);\n\n this.uniforms = {\n resolution: this.gl.getUniformLocation(this.program, 'u_resolution'),\n destRect: this.gl.getUniformLocation(this.program, 'u_destRect'),\n srcRect: this.gl.getUniformLocation(this.program, 'u_srcRect'),\n color: this.gl.getUniformLocation(this.program, 'u_color'),\n useTexture: this.gl.getUniformLocation(this.program, 'u_useTexture'),\n shapeMode: this.gl.getUniformLocation(this.program, 'u_shapeMode'),\n texture: this.gl.getUniformLocation(this.program, 'u_texture'),\n };\n\n this.gl.uniform1i(this.uniforms.texture, 0);\n this.gl.clearColor(0, 0, 0, 0);\n this.gl.enable(this.gl.BLEND);\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public resize(width: number, height: number): void {\n this.viewportW = Math.max(1, Math.floor(width));\n this.viewportH = Math.max(1, Math.floor(height));\n this.gl.viewport(0, 0, this.viewportW, this.viewportH);\n }\n\n public beginFrame(): void {\n this.gl.useProgram(this.program);\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quadBuffer);\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);\n this.gl.uniform2f(this.uniforms.resolution, this.viewportW, this.viewportH);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n }\n\n public drawSprite(\n symbolId: SymbolId,\n x: number,\n y: number,\n width: number,\n height: number,\n alpha = 1,\n ): void {\n const segmentIndex = normalizeSegment(symbolId, this.spriteElementsCount);\n const srcTop = segmentIndex * this.spriteSegmentHeight;\n const srcBottom = srcTop + this.spriteSegmentHeight;\n\n // Half-texel inset reduces sprite bleeding between vertical segments.\n const inset = 0.5;\n const u0 = inset / this.spriteWidth;\n const u1 = 1 - inset / this.spriteWidth;\n const v0 = 1 - (srcBottom - inset) / this.spriteHeight;\n const v1 = 1 - (srcTop + inset) / this.spriteHeight;\n\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, u0, v0, u1, v1);\n this.gl.uniform4f(this.uniforms.color, 1, 1, 1, alpha);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSolidRect(\n x: number,\n y: number,\n width: number,\n height: number,\n rgba: [number, number, number, number],\n ): void {\n this.gl.uniform4f(this.uniforms.destRect, x, y, width, height);\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n }\n\n public drawSoftCircle(\n centerX: number,\n centerY: number,\n radius: number,\n rgba: [number, number, number, number],\n ): void {\n const diameter = radius * 2;\n this.gl.uniform4f(\n this.uniforms.destRect,\n centerX - radius,\n centerY - radius,\n diameter,\n diameter,\n );\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(this.uniforms.color, rgba[0], rgba[1], rgba[2], rgba[3]);\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.uniform1f(this.uniforms.shapeMode, 1);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n this.gl.uniform1f(this.uniforms.shapeMode, 0);\n }\n\n public beginAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);\n }\n\n public endAdditiveBlend(): void {\n this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);\n }\n\n public dispose(): void {\n this.gl.deleteTexture(this.texture);\n this.gl.deleteBuffer(this.quadBuffer);\n this.gl.deleteProgram(this.program);\n }\n\n private createShader(type: number, source: string): WebGLShader {\n const shader = this.gl.createShader(type);\n if (!shader) {\n throw new Error('Failed to create WebGL shader');\n }\n this.gl.shaderSource(shader, source);\n this.gl.compileShader(shader);\n\n if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {\n const message = this.gl.getShaderInfoLog(shader) ?? 'unknown error';\n this.gl.deleteShader(shader);\n throw new Error(`WebGL shader compile failed: ${message}`);\n }\n return shader;\n }\n\n private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {\n const program = this.gl.createProgram();\n if (!program) {\n throw new Error('Failed to create WebGL program');\n }\n this.gl.attachShader(program, vertexShader);\n this.gl.attachShader(program, fragmentShader);\n this.gl.linkProgram(program);\n\n if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {\n const message = this.gl.getProgramInfoLog(program) ?? 'unknown error';\n this.gl.deleteProgram(program);\n throw new Error(`WebGL program link failed: ${message}`);\n }\n return program;\n }\n}\n","import {\n DEFAULT_SPRITE_ELEMENTS_COUNT,\n FLOW_WIN_FLASH_MS,\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 INITIAL_WIN_FLASH_DELAY_MS,\n PARTICLE_FLY_DURATION_MS,\n ROW_COMPACT_OFFSETS_RATIO,\n} from './constants';\nimport {\n createRandomGrid,\n createZeroOffsets,\n fillOffsets,\n findMostFrequentCells,\n} from './core/grid';\nimport { RafLoop } from './core/loop';\nimport type { OutroMotionPlan } from './core/outro';\nimport { buildOutroMotionPlan, updateOutroOffsets } from './core/outro';\nimport { SpinQueueController } from './core/spinQueue';\nimport {\n beginSpin,\n createRuntimeState,\n destroyState,\n finishSpin,\n startWinFlash,\n} from './core/state';\nimport {\n normalizeInitialSegments,\n normalizeParticleColor,\n normalizeParticleColorMode,\n normalizeStopGrid,\n rowsToStopGrid,\n} from './normalize';\nimport { WebGLRenderer } from './render/webglRenderer';\nimport type { CascadingReelConfig, CellPosition, SymbolId } from './types';\n\ntype ParticleSeeds = {\n seedA: number;\n seedB: number;\n seedC: number;\n phaseOffset: number;\n twinkleSeed: number;\n};\n\nexport class CascadingReel {\n private static readonly RAINBOW_HUE_BUCKETS = 24;\n private static readonly PARTICLE_GLOBAL_ALPHA = 0.9;\n private static readonly PARTICLE_MAX_DISTANCE = 0.72;\n private static readonly PARTICLE_BASE_RADIUS = 0.028;\n\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\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\n private spriteImage: HTMLImageElement | null = null;\n private webglRenderer: WebGLRenderer | 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 scriptedOutroVirtualElapsedMs = 0;\n private scriptedOutroLastNow = 0;\n private outroMotionPlan: OutroMotionPlan | null = null;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private winningCells: CellPosition[] = [];\n private readonly winningCellKeys = new Set<string>();\n private grid: SymbolId[][];\n private readonly particlesPerCell = FLOW_WIN_PARTICLES_PER_CELL_HIGH;\n private lastRafTime = 0;\n private initialHighlightRequestedAt = 0;\n private static readonly PRE_SPIN_MS = 150;\n private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS = 120;\n private static readonly OUTRO_MAX_STEP_MS = 20;\n private static readonly WIN_BORDER_ALPHA = 0.72;\n private static readonly WIN_BORDER_INSET_RATIO = 0.06;\n private static readonly particleSeedsCache = new Map<string, ParticleSeeds[]>();\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.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 if (!this.spriteImage) {\n throw new Error('sprite is required for WebGL renderer');\n }\n this.webglRenderer = new WebGLRenderer({\n canvas: this.canvas,\n spriteImage: this.spriteImage,\n spriteElementsCount: this.spriteElementsCount,\n });\n this.webglRenderer.resize(this.width, this.height);\n this.applyInitialHighlightIfNeeded();\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.webglRenderer?.dispose();\n this.webglRenderer = null;\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 = activeSpinState?.highlightWin === true;\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 this.startLoop();\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 // Подсветка до клика: в winFlash с подсветкой кнопка включена, по клику завершаем и при наличии очереди запускаем следующий спин\n if (\n this.runtime.phase === 'winFlash' &&\n (this.runtime.shouldHighlightCurrentSpin || !this.runtime.hasStartedFirstSpin)\n ) {\n // Только закрываем подсветку и запускаем следующий спин; callback уже вызван по окончании прокрутки\n this.finishSpinWithUi(true);\n if (this.spinQueueController.hasPending()) this.spin();\n return;\n }\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') {\n // Подсветка без ограничения по времени: выход только по клику «Spin»\n if (\n !this.runtime.shouldHighlightCurrentSpin &&\n now - this.runtime.winFlashStartedAt >= FLOW_WIN_FLASH_MS\n ) {\n this.finishSpinWithUi();\n }\n return;\n }\n }\n\n private updateScriptedOutro(now: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n if (!this.outroMotionPlan) {\n this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n });\n }\n\n const motionNow = this.getSmoothedOutroNow(now);\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n now: motionNow,\n scriptedOutroStartedAt: this.scriptedOutroStartedAt,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n motionPlan: this.outroMotionPlan,\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.outroMotionPlan = 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.setWinningCells(findMostFrequentCells(this.grid));\n startWinFlash(this.runtime, now);\n // Callback по окончании прокрутки, а не по клику\n this.runtime.activeSpinState?.callback?.();\n // Включаем кнопку только если в очереди есть следующий спин; после последнего — оставляем disabled\n if (\n this.button &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.spinQueueController.hasPending()\n )\n this.button.disabled = false;\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 this.outroMotionPlan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n });\n fillOffsets(this.scriptedIncomingOffsets, Number.NaN);\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n this.scriptedOutroVirtualElapsedMs = 0;\n this.scriptedOutroLastNow = now;\n }\n\n private getSmoothedOutroNow(now: number): number {\n const realDt = Math.max(0, now - this.scriptedOutroLastNow);\n this.scriptedOutroLastNow = now;\n const safeStep = Math.min(realDt, CascadingReel.OUTRO_MAX_STEP_MS);\n this.scriptedOutroVirtualElapsedMs += safeStep;\n return this.scriptedOutroStartedAt + this.scriptedOutroVirtualElapsedMs;\n }\n\n /** @param skipCallback true при закрытии подсветки по клику (callback уже вызван по окончании прокрутки) */\n private finishSpinWithUi(skipCallback = false): void {\n const finishedSpinState = this.runtime.activeSpinState;\n const callback = skipCallback ? undefined : finishedSpinState?.callback;\n const hasPending = this.spinQueueController.hasPending();\n finishSpin(this.runtime, 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.setWinningCells(findMostFrequentCells(this.grid));\n this.initialHighlightRequestedAt = performance.now();\n }\n\n private render(now: number): void {\n if (!this.webglRenderer) return;\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 if (this.button) this.button.disabled = false;\n }\n\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 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.webglRenderer.beginFrame();\n\n const skipWinningCells =\n this.runtime.phase === 'winFlash' ||\n this.runtime.phase === 'preSpin' ||\n (this.initialHighlightRequestedAt > 0 && this.winningCells.length > 0);\n\n if (this.runtime.phase === 'outro' && this.scriptedOutgoingGrid && this.scriptedPendingGrid) {\n this.drawGrid(this.scriptedOutgoingGrid, this.scriptedOutgoingOffsets, skipWinningCells);\n this.drawGrid(this.scriptedPendingGrid, this.scriptedIncomingOffsets, skipWinningCells);\n } else {\n this.drawGrid(this.grid, null, skipWinningCells);\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 this.drawWinningEffects({\n now,\n phase: winPhase,\n winFlashStartedAt,\n winEffectsEnvelope,\n });\n }\n\n private readonly isWinningCell = (col: number, row: number): boolean => {\n return this.winningCellKeys.has(`${col}:${row}`);\n };\n\n private getRowCompactOffset(row: number): number {\n return (ROW_COMPACT_OFFSETS_RATIO[row] ?? 0) * this.cellH;\n }\n\n private drawGrid(\n grid: SymbolId[][],\n offsets: number[][] | null,\n skipWinningCells: boolean,\n ): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n for (let col = 0; col < GRID_COLS; col += 1) {\n const x = this.boardX + col * this.cellW;\n for (let row = 0; row < GRID_ROWS; row += 1) {\n if (skipWinningCells && this.isWinningCell(col, row)) continue;\n const offsetY = offsets ? offsets[col][row] : 0;\n if (!Number.isFinite(offsetY)) continue;\n const y = this.boardY + row * this.cellH + offsetY + this.getRowCompactOffset(row);\n if (y > this.height || y + this.cellH < 0) continue;\n renderer.drawSprite(grid[col][row], x, y, this.cellW, this.cellH, 1);\n }\n }\n }\n\n private drawWinningEffects(params: {\n now: number;\n phase: 'idle' | 'winFlash' | 'outro' | 'preSpin';\n winFlashStartedAt: number;\n winEffectsEnvelope: number;\n }): void {\n const renderer = this.webglRenderer;\n if (!renderer) return;\n if (this.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 * envelope;\n const borderInset = Math.max(1, this.cellW * CascadingReel.WIN_BORDER_INSET_RATIO);\n const borderThickness = Math.max(1, this.cellW * 0.03);\n const particleModeActive =\n this.runtime.phase === 'winFlash' &&\n this.runtime.shouldHighlightCurrentSpin &&\n this.runtime.hasStartedFirstSpin;\n for (const cell of this.winningCells) {\n const baseX = this.boardX + cell.col * this.cellW;\n const baseY = this.boardY + cell.row * this.cellH + this.getRowCompactOffset(cell.row);\n const symbol = this.grid[cell.col][cell.row];\n const alpha = CascadingReel.WIN_BORDER_ALPHA * envelope;\n\n const borderColor =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.hslToRgb01(\n (elapsed * 0.2 + cell.col * 36 + cell.row * 22) % 360,\n 0.96,\n 0.64,\n )\n : [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n const scaledW = this.cellW * pulse;\n const scaledH = this.cellH * pulse;\n const offsetX = (this.cellW - scaledW) * 0.5;\n const offsetY = (this.cellH - scaledH) * 0.5;\n renderer.drawSprite(symbol, baseX + offsetX, baseY + offsetY, scaledW, scaledH, 1);\n\n const innerX = baseX + borderInset;\n const innerY = baseY + borderInset;\n const innerW = this.cellW - borderInset * 2;\n const innerH = this.cellH - borderInset * 2;\n if (innerW <= borderThickness * 2 || innerH <= borderThickness * 2) continue;\n\n renderer.drawSolidRect(innerX, innerY, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY + innerH - borderThickness, innerW, borderThickness, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n renderer.drawSolidRect(innerX + innerW - borderThickness, innerY, borderThickness, innerH, [\n borderColor[0],\n borderColor[1],\n borderColor[2],\n alpha,\n ]);\n\n if (particleModeActive) {\n this.drawCellParticleBurst({\n renderer,\n cell,\n centerX: baseX + this.cellW * 0.5,\n centerY: baseY + this.cellH * 0.5,\n elapsed,\n envelope,\n });\n }\n }\n }\n\n private drawCellParticleBurst(params: {\n renderer: WebGLRenderer;\n cell: CellPosition;\n centerX: number;\n centerY: number;\n elapsed: number;\n envelope: number;\n }): void {\n const maxDistance = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_MAX_DISTANCE;\n const baseRadius = Math.min(this.cellW, this.cellH) * CascadingReel.PARTICLE_BASE_RADIUS;\n const seedsList = CascadingReel.getParticleSeeds(params.cell.col, params.cell.row);\n const solidColor: [number, number, number] = [\n this.particleColorRgb[0] / 255,\n this.particleColorRgb[1] / 255,\n this.particleColorRgb[2] / 255,\n ];\n\n params.renderer.beginAdditiveBlend();\n for (let i = 0; i < this.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 const direction = s.seedA * Math.PI * 2;\n const distance = maxDistance * particleT * (0.35 + s.seedB * 0.65);\n const px = params.centerX + Math.cos(direction) * distance;\n const py = params.centerY + Math.sin(direction) * distance;\n const twinkle =\n 0.7 +\n 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 = Math.max(\n 0,\n Math.min(1, (0.9 + twinkle * 0.2) * CascadingReel.PARTICLE_GLOBAL_ALPHA * params.envelope),\n );\n if (alpha <= 0) continue;\n\n const rgb =\n this.particleColorMode === 'rainbow'\n ? CascadingReel.getRainbowParticleColor(\n params.elapsed,\n params.cell.col,\n params.cell.row,\n s.seedA,\n )\n : solidColor;\n\n params.renderer.drawSoftCircle(px, py, radius, [rgb[0], rgb[1], rgb[2], alpha]);\n }\n params.renderer.endAdditiveBlend();\n }\n\n private static hslToRgb01(\n hueDeg: number,\n saturation: number,\n lightness: number,\n ): [number, number, number] {\n const h = ((hueDeg % 360) + 360) % 360;\n const s = Math.max(0, Math.min(1, saturation));\n const l = Math.max(0, Math.min(1, lightness));\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const hp = h / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp >= 0 && hp < 1) {\n r = c;\n g = x;\n } else if (hp < 2) {\n r = x;\n g = c;\n } else if (hp < 3) {\n g = c;\n b = x;\n } else if (hp < 4) {\n g = x;\n b = c;\n } else if (hp < 5) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n const m = l - c * 0.5;\n return [r + m, g + m, b + m];\n }\n\n private static getRainbowParticleColor(\n elapsed: number,\n col: number,\n row: number,\n seedA: number,\n ): [number, number, number] {\n const hueRaw = (seedA * 360 + elapsed * 0.24 + col * 38 + row * 22) % 360;\n const bucket = 360 / CascadingReel.RAINBOW_HUE_BUCKETS;\n const hue = Math.floor(hueRaw / bucket) * bucket;\n return CascadingReel.hslToRgb01(hue, 0.98, 0.64);\n }\n\n private static 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\n private static getParticleSeeds(col: number, row: number): ParticleSeeds[] {\n const key = `${col},${row}`;\n const cached = CascadingReel.particleSeedsCache.get(key);\n if (cached) return cached;\n\n const generated: ParticleSeeds[] = [];\n for (let i = 0; i < FLOW_WIN_PARTICLES_PER_CELL_HIGH; i += 1) {\n generated.push({\n seedA: CascadingReel.hash01(col, row, i, 1),\n seedB: CascadingReel.hash01(col, row, i, 2),\n seedC: CascadingReel.hash01(col, row, i, 3),\n phaseOffset: CascadingReel.hash01(col, row, i, 4),\n twinkleSeed: CascadingReel.hash01(col, row, i, 5),\n });\n }\n CascadingReel.particleSeedsCache.set(key, generated);\n return generated;\n }\n\n private clearWinningCells(): void {\n this.winningCells = [];\n this.winningCellKeys.clear();\n }\n\n private setWinningCells(cells: CellPosition[]): void {\n this.winningCells = cells;\n this.winningCellKeys.clear();\n for (const cell of cells) {\n this.winningCellKeys.add(`${cell.col}:${cell.row}`);\n }\n }\n\n private readonly resize = (): void => {\n const bounds = this.container.getBoundingClientRect();\n this.dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));\n const side = Math.max(300, Math.floor(bounds.width * this.dpr));\n this.width = side;\n this.height = side;\n const squareSize = Math.floor(Math.min(this.width / GRID_COLS, this.height / GRID_ROWS));\n this.cellW = squareSize;\n this.cellH = squareSize;\n this.boardX = Math.floor((this.width - this.cellW * GRID_COLS) / 2);\n this.boardY = Math.floor((this.height - this.cellH * GRID_ROWS) / 2);\n\n this.canvas.width = this.width;\n this.canvas.height = this.height;\n this.webglRenderer?.resize(this.width, this.height);\n };\n\n private async loadSpriteIfProvided(): Promise<void> {\n if (!this.spriteUrl) return;\n const image = new Image();\n image.decoding = 'async';\n image.src = this.spriteUrl;\n try {\n await image.decode();\n this.spriteImage = image;\n } catch {\n this.spriteImage = null;\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.update(time);\n this.render(time);\n return this.shouldKeepAnimating();\n });\n }\n\n private shouldKeepAnimating(): boolean {\n if (this.runtime.isSpinning) return true;\n if (this.initialHighlightRequestedAt > 0) return true;\n return this.runtime.winEffectsEnvelope > 0.001;\n }\n}\n"],"names":["ROW_COMPACT_OFFSETS_RATIO","FLOW_OUTRO_ROW_GAP_MS","FLOW_ROW_BASE_SPACING_RATIO","FLOW_WIN_FLASH_MS","FLOW_WIN_PULSE_PERIOD_MS","FLOW_WIN_PULSE_AMPLITUDE","PARTICLE_FLY_DURATION_MS","FLOW_WIN_PARTICLES_PER_CELL_HIGH","DEFAULT_PARTICLE_COLOR_RGB","FLOW_COLUMN_STAGGER_MS","FLOW_FALL_MS","INITIAL_WIN_FLASH_DELAY_MS","clamp","value","min","max","randomInt","maxExclusive","normalizeSegment","segment","elementsCount","normalizeRgbChannel","createRandomGrid","spriteElementsCount","grid","col","column","row","findMostFrequentCells","counts","symbol","selectedSymbol","maxCount","count","cells","createZeroOffsets","fillOffsets","offsets","RafLoop","step","now","easeFall","t","x","buildSequentialRowStartDelays","fromRowOffsets","duration","gapMs","delays","nextDelay","baseSpacing","buildOutroMotionPlan","params","outgoingDistance","outgoingOffsetsForOrder","updateOutroOffsets","allOutgoingDone","allIncomingDone","columnElapsed","rowElapsed","tOut","easedOut","incomingElapsed","tIn","easedIn","normalizeParticleColor","color","normalizeParticleColorMode","mode","rowsToStopGrid","rows","normalizeStopGrid","stopGrid","next","normalizeInitialSegments","initialSegments","cloneSpinState","state","SpinQueueController","initialQueue","entry","createRuntimeState","beginSpin","finishSpin","hasPendingInQueue","startWinFlash","startedAt","destroyState","VERTEX_SHADER_SOURCE","FRAGMENT_SHADER_SOURCE","WebGLRenderer","gl","vertexShader","fragmentShader","quadBuffer","texture","posLocation","width","height","symbolId","y","alpha","srcTop","srcBottom","inset","u0","u1","v0","v1","rgba","centerX","centerY","radius","diameter","type","source","shader","message","program","CascadingReel","config","warmNow","activeSpinState","shouldHighlightCurrentSpin","scriptedSource","stopGridSource","nextGrid","motionNow","realDt","safeStep","skipCallback","finishedSpinState","callback","hasPending","winEffectsTarget","dt","kWin","skipWinningCells","winPhase","winFlashStartedAt","winEffectsEnvelope","renderer","offsetY","envelope","elapsed","pulseProgress","pulse","borderInset","borderThickness","particleModeActive","cell","baseX","baseY","borderColor","scaledW","scaledH","offsetX","innerX","innerY","innerW","innerH","maxDistance","baseRadius","seedsList","solidColor","i","s","startTime","age","particleT","direction","distance","px","py","twinkle","rgb","hueDeg","saturation","lightness","h","l","c","hp","r","g","b","m","seedA","hueRaw","bucket","hue","a","d","key","cached","generated","bounds","side","squareSize","image","time"],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,54 @@
|
|
|
1
|
-
(function(w,I){typeof exports=="object"&&typeof module<"u"?I(exports):typeof define=="function"&&define.amd?define(["exports"],I):(w=typeof globalThis<"u"?globalThis:w||self,I(w.CascadingReel={}))})(this,(function(w){"use strict";const X=[.04,0,-.04],$=34,Q=.05,V=5e3,v=1800,Z=.1,M=720,W=34,y=20,D=12,j=[255,235,110],K=[255,255,255,.78],J=190,b=650,tt=220,it=200;function _(t,i,e){return t<i?i:t>e?e:t}function T(t){return 1-(1-t)**3}function et(t){return Math.floor(Math.random()*t)}function A(t,i){return(t%i+i)%i}function R(t){return _(Math.round(t),0,255)}function nt(t){return _(t,0,1)}function N(t){const i=[];for(let e=0;e<3;e+=1){const n=[];for(let l=0;l<3;l+=1)n.push(et(t));i.push(n)}return i}function q(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 Y(){return Array.from({length:3},()=>Array.from({length:3},()=>0))}function m(t,i){for(let e=0;e<3;e+=1)for(let n=0;n<3;n+=1)t[e][n]=i}class st{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 lt(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*Q);l+=r+e}return n}function rt(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=lt(s,b,$),o=b-tt;for(let h=0;h<t.scriptedOutgoingOffsets.length;h+=1){const a=t.now-(t.scriptedOutroStartedAt+h*J);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=_(d/b,0,1),S=T(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=_(C/b,0,1),O=T(p);t.scriptedIncomingOffsets[h][c]=r[c]*(1-O),p<1&&(e=!1)}}return{allOutgoingDone:i,allIncomingDone:e}}function ot(t){return t?[R(t[0]),R(t[1]),R(t[2])]:j}function ht(t){return t==="rainbow"?"rainbow":"solid"}function ct(t){return t==="high"||t==="balanced"||t==="low"||t==="auto"?t:"auto"}function ut(t){return t?[R(t[0]),R(t[1]),R(t[2]),nt(t[3])]:K}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 G(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]=[A(l[0],i),A(l[1],i),A(l[2],i)]}return e}function at(t,i){return G(P(t),i)}function dt(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])),highlightWin:t.highlightWin,callback:t.callback}}class ft{queue;constructor(i){this.queue=(i??[]).map(e=>dt(e))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function gt(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,swayEnvelope:1,winEffectsEnvelope:1}}function St(t,i){t.hasStartedFirstSpin=!0,t.isSpinning=!0,t.phase="outro",t.outroStartedAt=i.startedAt,t.activeSpinState=i.activeSpinState,t.shouldHighlightCurrentSpin=i.shouldHighlightCurrentSpin}function pt(t,i,e){t.phase="idle",t.idleStartedAt=e,t.isSpinning=!1,t.shouldHighlightCurrentSpin=!1,t.queueFinished=!i,t.activeSpinState=null}function U(t,i){t.winFlashStartedAt=i,t.phase="winFlash"}function Ct(t){t.isSpinning=!1,t.queueFinished=!0}const wt=3,Rt=.002;function F(t,i){return(X[t]??0)*i}function Ot(t){const i=t.columnSway??[0,0,0],e=wt*(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+F(o,t.cellH);if(a>t.height||a+t.cellH<0)continue;const d=Math.sin(t.now*Rt+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 E(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 k=new Map;function _t(t,i){const e=`${t},${i}`;let n=k.get(e);if(n)return n;n=[];for(let l=0;l<W;l+=1)n.push({seedA:E(t,i,l,1),seedB:E(t,i,l,2),seedC:E(t,i,l,3),phaseOffset:E(t,i,l,4),twinkleSeed:E(t,i,l,5)});return k.set(e,n),n}function B(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%v/v,l=1+Math.sin(n*Math.PI*2)*Z;for(const s of t.winningCells){const r=t.boardX+s.col*t.cellW,u=t.boardY+s.row*t.cellH+F(s.row,t.cellH);Et(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+F(s.row,t.cellH);It({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 Et(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)),H=Math.max(3,Math.floor(p*.03));t.save(),t.lineWidth=c,t.strokeStyle=`hsla(${C}, 90%, 70%, ${d*h})`,t.setLineDash([O,H]),t.lineDashOffset=-o*.03;const L=c*.5;t.strokeRect(i+a+L,e+a+L,n-a*2-c,l-a*2-c),t.setLineDash([]),t.restore()}const z=24;function It(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=_t(t.cell.col,t.cell.row);for(let c=0;c<t.particlesPerCell;c+=1){const d=a[c],f=d.phaseOffset*M,S=t.elapsed-f;if(S<0)continue;const C=S%M/M,p=d.seedA*Math.PI*2,O=n*C*(.35+d.seedB*.65),H=i+Math.cos(p)*O,L=e+Math.sin(p)*O,Wt=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+d.twinkleSeed*2)*Math.PI*2)),bt=Math.max(1,l*(.55+d.seedC*.6)*(1-C*.5)),x=_((.9+Wt*.2)*s,.9,1);if(!(x<=0)){if(h)t.ctx.globalAlpha=x;else{const At=(d.seedA*360+t.elapsed*.24+t.cell.col*38+t.cell.row*22)%360,mt=Math.floor(At/(360/z))*(360/z);t.ctx.fillStyle=`hsla(${mt},98%,64%,${x})`}t.ctx.beginPath(),t.ctx.arc(H,L,bt,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 st;runtime=gt();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutgoingOffsets=Y();scriptedIncomingOffsets=Y();winningCells=[];grid;lastRafTime=0;particlesPerCell=W;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 ft(i.queuedSpinStates),this.particleColorRgb=ot(i.particleColorRgb),this.particleColorMode=ht(i.particleColorMode),this.winningCellBorderRgba=ut(i.winningCellBorderRgba),this.quality=ct(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?at(i.initialSegments,this.spriteElementsCount):N(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(),Ct(this.runtime),this.clearWinningCells()}spin(){if(this.runtime.isSpinning||this.runtime.queueFinished)return;if(!this.spinQueueController.hasPending()){this.runtime.queueFinished=!0,this.button&&(this.button.disabled=!0);return}const i=this.spinQueueController.consume(),e=i?.highlightWin===!0;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=()=>{if(this.runtime.phase==="winFlash"&&(this.runtime.shouldHighlightCurrentSpin||!this.runtime.hasStartedFirstSpin)){this.finishSpinWithUi(!0),this.spinQueueController.hasPending()&&this.spin();return}this.runtime.isSpinning||this.spin()};getNextGrid(i){return i?G(i,this.spriteElementsCount):N(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}if(this.runtime.phase==="winFlash"){!this.runtime.shouldHighlightCurrentSpin&&i-this.runtime.winFlashStartedAt>=V&&this.finishSpinWithUi();return}}}updateScriptedOutro(i){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}const{allOutgoingDone:e,allIncomingDone:n}=rt({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(),m(this.scriptedOutgoingOffsets,0),m(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(i)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.winningCells=q(this.grid),U(this.runtime,i),this.runtime.activeSpinState?.callback?.(),this.button&&this.runtime.shouldHighlightCurrentSpin&&this.spinQueueController.hasPending()&&(this.button.disabled=!1)}}}tryStartScriptedCascade(i){if(this.scriptedCascadeQueue.length===0)return!1;const e=this.scriptedCascadeQueue.shift();return e?(this.startOutroTransition(G(e,this.spriteElementsCount),i),!0):!1}startOutroTransition(i,e){this.scriptedOutgoingGrid=this.grid.map(n=>[...n]),this.scriptedPendingGrid=i.map(n=>[...n]),m(this.scriptedIncomingOffsets,Number.NaN),m(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=e}finishSpinWithUi(i=!1){const e=this.runtime.activeSpinState,n=i?void 0:e?.callback,l=this.spinQueueController.hasPending();pt(this.runtime,l,performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),n?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.winningCells=q(this.grid),this.initialHighlightRequestedAt=performance.now())}static ZERO_SWAY=[0,0,0];render(i){this.initialHighlightRequestedAt>0&&i-this.initialHighlightRequestedAt>=it&&(U(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0,this.button&&(this.button.disabled=!1));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;B({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;Ot({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=A(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=W:this.quality==="balanced"?this.particlesPerCell=y:this.quality==="low"?this.particlesPerCell=D:this.particlesPerCell=this.dpr>=2?y:W,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,D),u=(o,h,a,c,d)=>{this.drawSpriteCellToCtx(e,o,h,a,c,d)};for(let o=0;o<2;o+=1)B({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))}}w.CascadingReel=g,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(m,E){typeof exports=="object"&&typeof module<"u"?E(exports):typeof define=="function"&&define.amd?define(["exports"],E):(m=typeof globalThis<"u"?globalThis:m||self,E(m.CascadingReel={}))})(this,(function(m){"use strict";const q=[.04,0,-.04],X=34,k=.05,Y=5e3,x=1800,z=.1,P=720,F=34,$=[255,235,110],K=190,w=520,Q=200;function O(e,t,i){return e<t?t:e>i?i:e}function V(e){return Math.floor(Math.random()*e)}function A(e,t){return(e%t+t)%t}function v(e){return O(Math.round(e),0,255)}function H(e){const t=[];for(let i=0;i<3;i+=1){const s=[];for(let r=0;r<3;r+=1)s.push(V(e));t.push(s)}return t}function D(e){const t=new Map;for(let n=0;n<3;n+=1)for(let o=0;o<3;o+=1){const h=e[n][o];t.set(h,(t.get(h)??0)+1)}let i=e[0][0],s=-1;for(const[n,o]of t.entries())o>s&&(s=o,i=n);const r=[];for(let n=0;n<3;n+=1)for(let o=0;o<3;o+=1)e[n][o]===i&&r.push({col:n,row:o});return r}function N(){return Array.from({length:3},()=>Array.from({length:3},()=>0))}function I(e,t){for(let i=0;i<3;i+=1)for(let s=0;s<3;s+=1)e[i][s]=t}class j{rafId=null;step=null;start(t){this.rafId===null&&(this.step=t,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=t=>{if(!this.step){this.stop();return}if(this.step(t)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}}function U(e){const t=O(e,0,1);return t*t*(3-2*t)}function Z(e,t,i){const s=[0,0,0];let r=0;for(let n=2;n>=0;n-=1){if(e[n]===0){s[n]=0;continue}s[n]=r;const o=Math.floor(t*k);r+=o+i}return s}function y(e){const i=e.height-e.boardY+e.cellH+2,s=[i,i,i];return{outgoingDistance:i,incomingFromOffsets:[-e.cellH,-e.cellH*2,-e.cellH*3],rowStartDelays:Z(s,w,X),incomingStartShift:w}}function J(e){let t=!0,i=!0;for(let s=0;s<e.scriptedOutgoingOffsets.length;s+=1){const r=e.now-(e.scriptedOutroStartedAt+s*K);for(let n=0;n<3;n+=1){const o=r-e.motionPlan.rowStartDelays[n];if(o<=0){e.scriptedOutgoingOffsets[s][n]=0,e.scriptedIncomingOffsets[s][n]=Number.NaN,t=!1,i=!1;continue}const h=O(o/w,0,1),l=U(h);e.scriptedOutgoingOffsets[s][n]=e.motionPlan.outgoingDistance*l,h<1&&(t=!1);const c=o-e.motionPlan.incomingStartShift;if(c<=0){e.scriptedIncomingOffsets[s][n]=Number.NaN,i=!1;continue}const a=O(c/w,0,1),d=U(a);e.scriptedIncomingOffsets[s][n]=e.motionPlan.incomingFromOffsets[n]*(1-d),a<1&&(i=!1)}}return{allOutgoingDone:t,allIncomingDone:i}}function tt(e){return e?[v(e[0]),v(e[1]),v(e[2])]:$}function it(e){return e==="rainbow"?"rainbow":"solid"}function W(e){if(e.length!==3)throw new Error("rows must contain 3 rows");for(let t=0;t<3;t+=1)if(!Array.isArray(e[t])||e[t].length!==3)throw new Error(`rows[${t}] must contain 3 columns`);return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function G(e,t){if(e.length!==3)throw new Error("stopGrid must contain 3 columns");const i=[];for(let s=0;s<3;s+=1){const r=e[s];if(!Array.isArray(r)||r.length!==3)throw new Error(`stopGrid[${s}] must contain 3 rows`);i[s]=[A(r[0],t),A(r[1],t),A(r[2],t)]}return i}function et(e,t){return G(W(e),t)}function st(e){return{stopGrid:e.stopGrid?.map(t=>[...t]),stopRows:e.stopRows?.map(t=>[...t]),finaleSequence:e.finaleSequence?.map(t=>t.map(i=>[...i])),finaleSequenceRows:e.finaleSequenceRows?.map(t=>t.map(i=>[...i])),highlightWin:e.highlightWin,callback:e.callback}}class nt{queue;constructor(t){this.queue=(t??[]).map(i=>st(i))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}}function rt(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,winEffectsEnvelope:1}}function ot(e,t){e.hasStartedFirstSpin=!0,e.isSpinning=!0,e.phase="outro",e.outroStartedAt=t.startedAt,e.activeSpinState=t.activeSpinState,e.shouldHighlightCurrentSpin=t.shouldHighlightCurrentSpin}function ht(e,t,i){e.phase="idle",e.idleStartedAt=i,e.isSpinning=!1,e.shouldHighlightCurrentSpin=!1,e.queueFinished=!t,e.activeSpinState=null}function B(e,t){e.winFlashStartedAt=t,e.phase="winFlash"}function lt(e){e.isSpinning=!1,e.queueFinished=!0}const at=`
|
|
2
|
+
attribute vec2 a_pos;
|
|
3
|
+
uniform vec2 u_resolution;
|
|
4
|
+
uniform vec4 u_destRect;
|
|
5
|
+
varying vec2 v_uv;
|
|
6
|
+
|
|
7
|
+
void main() {
|
|
8
|
+
vec2 local = a_pos;
|
|
9
|
+
vec2 pixel = vec2(
|
|
10
|
+
u_destRect.x + local.x * u_destRect.z,
|
|
11
|
+
u_destRect.y + local.y * u_destRect.w
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
vec2 zeroToOne = pixel / u_resolution;
|
|
15
|
+
vec2 clip = vec2(
|
|
16
|
+
zeroToOne.x * 2.0 - 1.0,
|
|
17
|
+
1.0 - zeroToOne.y * 2.0
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
gl_Position = vec4(clip, 0.0, 1.0);
|
|
21
|
+
v_uv = local;
|
|
22
|
+
}
|
|
23
|
+
`,ut=`
|
|
24
|
+
precision mediump float;
|
|
25
|
+
|
|
26
|
+
uniform sampler2D u_texture;
|
|
27
|
+
uniform vec4 u_srcRect;
|
|
28
|
+
uniform vec4 u_color;
|
|
29
|
+
uniform float u_useTexture;
|
|
30
|
+
uniform float u_shapeMode;
|
|
31
|
+
varying vec2 v_uv;
|
|
32
|
+
|
|
33
|
+
void main() {
|
|
34
|
+
if (u_shapeMode > 0.5) {
|
|
35
|
+
vec2 centered = v_uv - vec2(0.5, 0.5);
|
|
36
|
+
float dist = length(centered) * 2.0;
|
|
37
|
+
if (dist > 1.0) {
|
|
38
|
+
discard;
|
|
39
|
+
}
|
|
40
|
+
float feather = smoothstep(1.0, 0.72, dist);
|
|
41
|
+
gl_FragColor = vec4(u_color.rgb, u_color.a * feather);
|
|
42
|
+
} else if (u_useTexture > 0.5) {
|
|
43
|
+
vec2 uv = vec2(
|
|
44
|
+
mix(u_srcRect.x, u_srcRect.z, v_uv.x),
|
|
45
|
+
mix(u_srcRect.y, u_srcRect.w, v_uv.y)
|
|
46
|
+
);
|
|
47
|
+
vec4 tex = texture2D(u_texture, uv);
|
|
48
|
+
gl_FragColor = tex * u_color;
|
|
49
|
+
} else {
|
|
50
|
+
gl_FragColor = u_color;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;class ct{canvas;spriteImage;spriteElementsCount;gl;program;uniforms;quadBuffer;texture;viewportW=1;viewportH=1;spriteWidth;spriteHeight;spriteSegmentHeight;constructor(t){this.canvas=t.canvas,this.spriteImage=t.spriteImage,this.spriteElementsCount=Math.max(1,t.spriteElementsCount),this.spriteWidth=this.spriteImage.width,this.spriteHeight=this.spriteImage.height,this.spriteSegmentHeight=this.spriteHeight/this.spriteElementsCount;const i=this.canvas.getContext("webgl2",{alpha:!0,antialias:!1})??this.canvas.getContext("webgl",{alpha:!0,antialias:!1});if(!i)throw new Error("WebGL context is not available");this.gl=i;const s=this.createShader(this.gl.VERTEX_SHADER,at),r=this.createShader(this.gl.FRAGMENT_SHADER,ut);this.program=this.createProgram(s,r),this.gl.deleteShader(s),this.gl.deleteShader(r);const n=this.gl.createBuffer();if(!n)throw new Error("Failed to create WebGL quad buffer");this.quadBuffer=n,this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),this.gl.STATIC_DRAW);const o=this.gl.createTexture();if(!o)throw new Error("Failed to create WebGL texture");this.texture=o,this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,0),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,1),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,this.spriteImage),this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,0),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.useProgram(this.program);const h=this.gl.getAttribLocation(this.program,"a_pos");this.gl.enableVertexAttribArray(h),this.gl.vertexAttribPointer(h,2,this.gl.FLOAT,!1,8,0),this.uniforms={resolution:this.gl.getUniformLocation(this.program,"u_resolution"),destRect:this.gl.getUniformLocation(this.program,"u_destRect"),srcRect:this.gl.getUniformLocation(this.program,"u_srcRect"),color:this.gl.getUniformLocation(this.program,"u_color"),useTexture:this.gl.getUniformLocation(this.program,"u_useTexture"),shapeMode:this.gl.getUniformLocation(this.program,"u_shapeMode"),texture:this.gl.getUniformLocation(this.program,"u_texture")},this.gl.uniform1i(this.uniforms.texture,0),this.gl.clearColor(0,0,0,0),this.gl.enable(this.gl.BLEND),this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}resize(t,i){this.viewportW=Math.max(1,Math.floor(t)),this.viewportH=Math.max(1,Math.floor(i)),this.gl.viewport(0,0,this.viewportW,this.viewportH)}beginFrame(){this.gl.useProgram(this.program),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.uniform2f(this.uniforms.resolution,this.viewportW,this.viewportH),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT)}drawSprite(t,i,s,r,n,o=1){const l=A(t,this.spriteElementsCount)*this.spriteSegmentHeight,c=l+this.spriteSegmentHeight,a=.5,d=a/this.spriteWidth,g=1-a/this.spriteWidth,p=1-(c-a)/this.spriteHeight,S=1-(l+a)/this.spriteHeight;this.gl.uniform4f(this.uniforms.destRect,i,s,r,n),this.gl.uniform4f(this.uniforms.srcRect,d,p,g,S),this.gl.uniform4f(this.uniforms.color,1,1,1,o),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSolidRect(t,i,s,r,n){this.gl.uniform4f(this.uniforms.destRect,t,i,s,r),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,n[0],n[1],n[2],n[3]),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSoftCircle(t,i,s,r){const n=s*2;this.gl.uniform4f(this.uniforms.destRect,t-s,i-s,n,n),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,r[0],r[1],r[2],r[3]),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.uniform1f(this.uniforms.shapeMode,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6),this.gl.uniform1f(this.uniforms.shapeMode,0)}beginAdditiveBlend(){this.gl.blendFunc(this.gl.SRC_ALPHA,this.gl.ONE)}endAdditiveBlend(){this.gl.blendFunc(this.gl.ONE,this.gl.ONE_MINUS_SRC_ALPHA)}dispose(){this.gl.deleteTexture(this.texture),this.gl.deleteBuffer(this.quadBuffer),this.gl.deleteProgram(this.program)}createShader(t,i){const s=this.gl.createShader(t);if(!s)throw new Error("Failed to create WebGL shader");if(this.gl.shaderSource(s,i),this.gl.compileShader(s),!this.gl.getShaderParameter(s,this.gl.COMPILE_STATUS)){const r=this.gl.getShaderInfoLog(s)??"unknown error";throw this.gl.deleteShader(s),new Error(`WebGL shader compile failed: ${r}`)}return s}createProgram(t,i){const s=this.gl.createProgram();if(!s)throw new Error("Failed to create WebGL program");if(this.gl.attachShader(s,t),this.gl.attachShader(s,i),this.gl.linkProgram(s),!this.gl.getProgramParameter(s,this.gl.LINK_STATUS)){const r=this.gl.getProgramInfoLog(s)??"unknown error";throw this.gl.deleteProgram(s),new Error(`WebGL program link failed: ${r}`)}return s}}class u{static RAINBOW_HUE_BUCKETS=24;static PARTICLE_GLOBAL_ALPHA=.9;static PARTICLE_MAX_DISTANCE=.72;static PARTICLE_BASE_RADIUS=.028;canvas;container;button;spinQueueController;spriteUrl;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;spriteImage=null;webglRenderer=null;rafLoop=new j;runtime=rt();dpr=1;width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutroVirtualElapsedMs=0;scriptedOutroLastNow=0;outroMotionPlan=null;scriptedOutgoingOffsets=N();scriptedIncomingOffsets=N();winningCells=[];winningCellKeys=new Set;grid;particlesPerCell=F;lastRafTime=0;initialHighlightRequestedAt=0;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;static OUTRO_MAX_STEP_MS=20;static WIN_BORDER_ALPHA=.72;static WIN_BORDER_INSET_RATIO=.06;static particleSeedsCache=new Map;constructor(t){this.canvas=t.canvas,this.container=t.container,this.button=t.button,this.spriteUrl=t.sprite,this.spriteElementsCount=Math.max(1,t.spriteElementsCount??6),this.highlightInitialWinningCells=t.highlightInitialWinningCells!==!1,this.spinQueueController=new nt(t.queuedSpinStates),this.particleColorRgb=tt(t.particleColorRgb),this.particleColorMode=it(t.particleColorMode),this.grid=t.initialSegments?et(t.initialSegments,this.spriteElementsCount):H(this.spriteElementsCount)}async init(){if(this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),!this.spriteImage)throw new Error("sprite is required for WebGL renderer");this.webglRenderer=new ct({canvas:this.canvas,spriteImage:this.spriteImage,spriteElementsCount:this.spriteElementsCount}),this.webglRenderer.resize(this.width,this.height),this.applyInitialHighlightIfNeeded(),requestAnimationFrame(t=>{this.render(t),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),lt(this.runtime),this.webglRenderer?.dispose(),this.webglRenderer=null,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 t=this.spinQueueController.consume(),i=t?.highlightWin===!0;this.runtime.isSpinning=!0,this.runtime.phase="preSpin",this.runtime.preSpinStartedAt=performance.now(),this.runtime.activeSpinState=t,this.runtime.shouldHighlightCurrentSpin=i,this.runtime.hasStartedFirstSpin=!0,this.button&&(this.button.disabled=!0),this.startLoop()}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=()=>{if(this.runtime.phase==="winFlash"&&(this.runtime.shouldHighlightCurrentSpin||!this.runtime.hasStartedFirstSpin)){this.finishSpinWithUi(!0),this.spinQueueController.hasPending()&&this.spin();return}this.runtime.isSpinning||this.spin()};getNextGrid(t){return t?G(t,this.spriteElementsCount):H(this.spriteElementsCount)}update(t){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(t-this.runtime.preSpinStartedAt<u.PRE_SPIN_MS)return;ot(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:t}),this.runtime.preSpinStartedAt=0;const i=this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(n=>W(n)):this.runtime.activeSpinState?.finaleSequence??[];this.scriptedCascadeQueue=i.map(n=>n.map(o=>[...o])),this.clearWinningCells();const s=this.runtime.activeSpinState?.stopRows?W(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,r=this.getNextGrid(s);this.startOutroTransition(r,t)}if(this.runtime.phase==="outro"){this.updateScriptedOutro(t);return}if(this.runtime.phase==="winFlash"){!this.runtime.shouldHighlightCurrentSpin&&t-this.runtime.winFlashStartedAt>=Y&&this.finishSpinWithUi();return}}}updateScriptedOutro(t){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}this.outroMotionPlan||(this.outroMotionPlan=y({height:this.height,boardY:this.boardY,cellH:this.cellH}));const i=this.getSmoothedOutroNow(t),{allOutgoingDone:s,allIncomingDone:r}=J({now:i,scriptedOutroStartedAt:this.scriptedOutroStartedAt,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets,motionPlan:this.outroMotionPlan});if(!(!s||!r)){if(!this.scriptedPendingGrid){this.finishSpinWithUi();return}if(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.outroMotionPlan=null,this.clearWinningCells(),I(this.scriptedOutgoingOffsets,0),I(this.scriptedIncomingOffsets,0),!this.tryStartScriptedCascade(t)){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.setWinningCells(D(this.grid)),B(this.runtime,t),this.runtime.activeSpinState?.callback?.(),this.button&&this.runtime.shouldHighlightCurrentSpin&&this.spinQueueController.hasPending()&&(this.button.disabled=!1)}}}tryStartScriptedCascade(t){if(this.scriptedCascadeQueue.length===0)return!1;const i=this.scriptedCascadeQueue.shift();return i?(this.startOutroTransition(G(i,this.spriteElementsCount),t),!0):!1}startOutroTransition(t,i){this.scriptedOutgoingGrid=this.grid.map(s=>[...s]),this.scriptedPendingGrid=t.map(s=>[...s]),this.outroMotionPlan=y({height:this.height,boardY:this.boardY,cellH:this.cellH}),I(this.scriptedIncomingOffsets,Number.NaN),I(this.scriptedOutgoingOffsets,0),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=i,this.scriptedOutroVirtualElapsedMs=0,this.scriptedOutroLastNow=i}getSmoothedOutroNow(t){const i=Math.max(0,t-this.scriptedOutroLastNow);this.scriptedOutroLastNow=t;const s=Math.min(i,u.OUTRO_MAX_STEP_MS);return this.scriptedOutroVirtualElapsedMs+=s,this.scriptedOutroStartedAt+this.scriptedOutroVirtualElapsedMs}finishSpinWithUi(t=!1){const i=this.runtime.activeSpinState,s=t?void 0:i?.callback,r=this.spinQueueController.hasPending();ht(this.runtime,r,performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),s?.()}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.setWinningCells(D(this.grid)),this.initialHighlightRequestedAt=performance.now())}render(t){if(!this.webglRenderer)return;this.initialHighlightRequestedAt>0&&t-this.initialHighlightRequestedAt>=Q&&(B(this.runtime,this.initialHighlightRequestedAt),this.runtime.winEffectsEnvelope=1,this.initialHighlightRequestedAt=0,this.button&&(this.button.disabled=!1));const i=this.runtime.phase==="preSpin"?0:this.runtime.phase==="winFlash"?1:0;if(this.lastRafTime>0){const h=Math.min(t-this.lastRafTime,50),l=1-Math.exp(-h/u.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(i-this.runtime.winEffectsEnvelope)*l}this.lastRafTime=t,this.webglRenderer.beginFrame();const s=this.runtime.phase==="winFlash"||this.runtime.phase==="preSpin"||this.initialHighlightRequestedAt>0&&this.winningCells.length>0;this.runtime.phase==="outro"&&this.scriptedOutgoingGrid&&this.scriptedPendingGrid?(this.drawGrid(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsets,s),this.drawGrid(this.scriptedPendingGrid,this.scriptedIncomingOffsets,s)):this.drawGrid(this.grid,null,s);const r=this.initialHighlightRequestedAt>0?"winFlash":this.runtime.phase,n=this.initialHighlightRequestedAt>0?this.initialHighlightRequestedAt:this.runtime.winFlashStartedAt,o=this.initialHighlightRequestedAt>0?1:this.runtime.winEffectsEnvelope;this.drawWinningEffects({now:t,phase:r,winFlashStartedAt:n,winEffectsEnvelope:o})}isWinningCell=(t,i)=>this.winningCellKeys.has(`${t}:${i}`);getRowCompactOffset(t){return(q[t]??0)*this.cellH}drawGrid(t,i,s){const r=this.webglRenderer;if(r)for(let n=0;n<3;n+=1){const o=this.boardX+n*this.cellW;for(let h=0;h<3;h+=1){if(s&&this.isWinningCell(n,h))continue;const l=i?i[n][h]:0;if(!Number.isFinite(l))continue;const c=this.boardY+h*this.cellH+l+this.getRowCompactOffset(h);c>this.height||c+this.cellH<0||r.drawSprite(t[n][h],o,c,this.cellW,this.cellH,1)}}}drawWinningEffects(t){const i=this.webglRenderer;if(!i||this.winningCells.length===0||t.phase!=="winFlash"&&t.phase!=="preSpin")return;const s=Math.max(0,Math.min(1,t.winEffectsEnvelope)),r=Math.max(0,t.now-t.winFlashStartedAt),n=r%x/x,o=1+Math.sin(n*Math.PI*2)*z*s,h=Math.max(1,this.cellW*u.WIN_BORDER_INSET_RATIO),l=Math.max(1,this.cellW*.03),c=this.runtime.phase==="winFlash"&&this.runtime.shouldHighlightCurrentSpin&&this.runtime.hasStartedFirstSpin;for(const a of this.winningCells){const d=this.boardX+a.col*this.cellW,g=this.boardY+a.row*this.cellH+this.getRowCompactOffset(a.row),p=this.grid[a.col][a.row],S=u.WIN_BORDER_ALPHA*s,f=this.particleColorMode==="rainbow"?u.hslToRgb01((r*.2+a.col*36+a.row*22)%360,.96,.64):[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255],C=this.cellW*o,R=this.cellH*o,_=(this.cellW-C)*.5,dt=(this.cellH-R)*.5;i.drawSprite(p,d+_,g+dt,C,R,1);const b=d+h,L=g+h,T=this.cellW-h*2,M=this.cellH-h*2;T<=l*2||M<=l*2||(i.drawSolidRect(b,L,T,l,[f[0],f[1],f[2],S]),i.drawSolidRect(b,L+M-l,T,l,[f[0],f[1],f[2],S]),i.drawSolidRect(b,L,l,M,[f[0],f[1],f[2],S]),i.drawSolidRect(b+T-l,L,l,M,[f[0],f[1],f[2],S]),c&&this.drawCellParticleBurst({renderer:i,cell:a,centerX:d+this.cellW*.5,centerY:g+this.cellH*.5,elapsed:r,envelope:s}))}}drawCellParticleBurst(t){const i=Math.min(this.cellW,this.cellH)*u.PARTICLE_MAX_DISTANCE,s=Math.min(this.cellW,this.cellH)*u.PARTICLE_BASE_RADIUS,r=u.getParticleSeeds(t.cell.col,t.cell.row),n=[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255];t.renderer.beginAdditiveBlend();for(let o=0;o<this.particlesPerCell;o+=1){const h=r[o],l=h.phaseOffset*P,c=t.elapsed-l;if(c<0)continue;const a=c%P/P,d=h.seedA*Math.PI*2,g=i*a*(.35+h.seedB*.65),p=t.centerX+Math.cos(d)*g,S=t.centerY+Math.sin(d)*g,f=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+h.twinkleSeed*2)*Math.PI*2)),C=Math.max(1,s*(.55+h.seedC*.6)*(1-a*.5)),R=Math.max(0,Math.min(1,(.9+f*.2)*u.PARTICLE_GLOBAL_ALPHA*t.envelope));if(R<=0)continue;const _=this.particleColorMode==="rainbow"?u.getRainbowParticleColor(t.elapsed,t.cell.col,t.cell.row,h.seedA):n;t.renderer.drawSoftCircle(p,S,C,[_[0],_[1],_[2],R])}t.renderer.endAdditiveBlend()}static hslToRgb01(t,i,s){const r=(t%360+360)%360,n=Math.max(0,Math.min(1,i)),o=Math.max(0,Math.min(1,s)),h=(1-Math.abs(2*o-1))*n,l=r/60,c=h*(1-Math.abs(l%2-1));let a=0,d=0,g=0;l>=0&&l<1?(a=h,d=c):l<2?(a=c,d=h):l<3?(d=h,g=c):l<4?(d=c,g=h):l<5?(a=c,g=h):(a=h,g=c);const p=o-h*.5;return[a+p,d+p,g+p]}static getRainbowParticleColor(t,i,s,r){const n=(r*360+t*.24+i*38+s*22)%360,o=360/u.RAINBOW_HUE_BUCKETS,h=Math.floor(n/o)*o;return u.hslToRgb01(h,.98,.64)}static hash01(t,i,s,r){const n=Math.sin(t*127.1+i*311.7+s*74.7+r*19.3)*43758.5453;return n-Math.floor(n)}static getParticleSeeds(t,i){const s=`${t},${i}`,r=u.particleSeedsCache.get(s);if(r)return r;const n=[];for(let o=0;o<F;o+=1)n.push({seedA:u.hash01(t,i,o,1),seedB:u.hash01(t,i,o,2),seedC:u.hash01(t,i,o,3),phaseOffset:u.hash01(t,i,o,4),twinkleSeed:u.hash01(t,i,o,5)});return u.particleSeedsCache.set(s,n),n}clearWinningCells(){this.winningCells=[],this.winningCellKeys.clear()}setWinningCells(t){this.winningCells=t,this.winningCellKeys.clear();for(const i of t)this.winningCellKeys.add(`${i.col}:${i.row}`)}resize=()=>{const t=this.container.getBoundingClientRect();this.dpr=Math.max(1,Math.min(window.devicePixelRatio||1,2));const i=Math.max(300,Math.floor(t.width*this.dpr));this.width=i,this.height=i;const s=Math.floor(Math.min(this.width/3,this.height/3));this.cellW=s,this.cellH=s,this.boardX=Math.floor((this.width-this.cellW*3)/2),this.boardY=Math.floor((this.height-this.cellH*3)/2),this.canvas.width=this.width,this.canvas.height=this.height,this.webglRenderer?.resize(this.width,this.height)};async loadSpriteIfProvided(){if(!this.spriteUrl)return;const t=new Image;t.decoding="async",t.src=this.spriteUrl;try{await t.decode(),this.spriteImage=t}catch{this.spriteImage=null}}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(t=>(this.update(t),this.render(t),this.shouldKeepAnimating()))}shouldKeepAnimating(){return this.runtime.isSpinning||this.initialHighlightRequestedAt>0?!0:this.runtime.winEffectsEnvelope>.001}}m.CascadingReel=u,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
|
2
54
|
//# sourceMappingURL=index.umd.js.map
|