cascading-reel 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -169
- package/dist/index.cjs +1512 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +168 -0
- package/dist/index.d.ts +168 -169
- package/dist/index.js +1510 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -27
- package/dist/index.cjs.js +0 -131
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -1069
- package/dist/index.es.js.map +0 -1
- package/dist/index.umd.js +0 -131
- package/dist/index.umd.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/utils/math.ts","../src/core/grid.ts","../src/core/loop.ts","../src/core/motionTimeline.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 type MotionProfile = {\n columnStaggerMs: number;\n fallMs: number;\n outroOverlapMs: number;\n outroRowGapMs: number;\n rowBaseSpacingRatio: number;\n incomingAlphaRampMs: number;\n fixedStepMs: number;\n maxCatchUpStepsPerFrame: number;\n};\nexport const DEFAULT_MOTION_PROFILE: MotionProfile = {\n columnStaggerMs: 76,\n fallMs: 800,\n outroOverlapMs: 620,\n outroRowGapMs: 14,\n rowBaseSpacingRatio: 0.05,\n incomingAlphaRampMs: 34,\n fixedStepMs: 1000 / 120,\n maxCatchUpStepsPerFrame: 6,\n};\nexport const FLOW_OUTRO_ROW_GAP_MS = DEFAULT_MOTION_PROFILE.outroRowGapMs;\nexport const FLOW_ROW_BASE_SPACING_RATIO = DEFAULT_MOTION_PROFILE.rowBaseSpacingRatio;\nexport const FLOW_WIN_PULSE_PERIOD_MS = 1800;\nexport const FLOW_WIN_PULSE_AMPLITUDE = 0.15;\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 = DEFAULT_MOTION_PROFILE.columnStaggerMs;\nexport const FLOW_FALL_MS = DEFAULT_MOTION_PROFILE.fallMs;\nexport const FLOW_OUTRO_OVERLAP_MS = DEFAULT_MOTION_PROFILE.outroOverlapMs;\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 { clamp } from '../utils/math';\n\nexport type MotionSegment = {\n startMs: number;\n endMs: number;\n from: number;\n to: number;\n};\n\nexport function smootherStep(t: number): number {\n const x = clamp(t, 0, 1);\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\n\nexport function sampleSegment(\n segment: MotionSegment,\n nowMs: number,\n): {\n value: number;\n t: number;\n done: boolean;\n} {\n if (segment.endMs <= segment.startMs) {\n return { value: segment.to, t: 1, done: true };\n }\n const t = clamp((nowMs - segment.startMs) / (segment.endMs - segment.startMs), 0, 1);\n const eased = smootherStep(t);\n return {\n value: segment.from + (segment.to - segment.from) * eased,\n t,\n done: t >= 1,\n };\n}\n","import type { MotionProfile } from '../constants';\nimport { GRID_ROWS } from '../constants';\nimport { clamp } from '../utils/math';\nimport { sampleSegment } from './motionTimeline';\n\nexport type CellVisibility = 'hidden' | 'entering' | 'active' | 'exiting';\n\nexport type OutroMotionPlan = {\n columnStaggerMs: number;\n fallMs: number;\n incomingAlphaRampMs: number;\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 durationMs: number,\n gapMs: number,\n rowBaseSpacingRatio: 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(durationMs * rowBaseSpacingRatio);\n nextDelay += baseSpacing + gapMs;\n }\n return delays;\n}\n\nexport function buildOutroMotionPlan(params: {\n height: number;\n boardY: number;\n cellH: number;\n motionProfile: MotionProfile;\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 columnStaggerMs: params.motionProfile.columnStaggerMs,\n fallMs: params.motionProfile.fallMs,\n incomingAlphaRampMs: params.motionProfile.incomingAlphaRampMs,\n outgoingDistance,\n incomingFromOffsets: [-params.cellH, -params.cellH * 2, -params.cellH * 3],\n rowStartDelays: buildSequentialRowStartDelays(\n outgoingOffsetsForOrder,\n params.motionProfile.fallMs,\n params.motionProfile.outroRowGapMs,\n params.motionProfile.rowBaseSpacingRatio,\n ),\n incomingStartShift: Math.max(\n 0,\n params.motionProfile.fallMs - params.motionProfile.outroOverlapMs,\n ),\n };\n}\n\nexport function updateOutroOffsets(params: {\n elapsedMs: number;\n scriptedOutgoingOffsets: number[][];\n scriptedIncomingOffsets: number[][];\n scriptedIncomingAlpha: number[][];\n scriptedIncomingVisibility: CellVisibility[][];\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 = params.elapsedMs - col * params.motionPlan.columnStaggerMs;\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] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allOutgoingDone = false;\n allIncomingDone = false;\n continue;\n }\n\n const outgoing = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: 0,\n to: params.motionPlan.outgoingDistance,\n },\n rowElapsed,\n );\n params.scriptedOutgoingOffsets[col][row] = outgoing.value;\n params.scriptedIncomingVisibility[col][row] = outgoing.done ? 'entering' : 'exiting';\n if (!outgoing.done) allOutgoingDone = false;\n\n const incomingElapsed = rowElapsed - params.motionPlan.incomingStartShift;\n if (incomingElapsed <= 0) {\n params.scriptedIncomingOffsets[col][row] = params.motionPlan.incomingFromOffsets[row];\n params.scriptedIncomingAlpha[col][row] = 0;\n params.scriptedIncomingVisibility[col][row] = 'hidden';\n allIncomingDone = false;\n continue;\n }\n\n const incoming = sampleSegment(\n {\n startMs: 0,\n endMs: params.motionPlan.fallMs,\n from: params.motionPlan.incomingFromOffsets[row],\n to: 0,\n },\n incomingElapsed,\n );\n params.scriptedIncomingOffsets[col][row] = incoming.value;\n params.scriptedIncomingAlpha[col][row] = clamp(\n incomingElapsed / Math.max(1, params.motionPlan.incomingAlphaRampMs),\n 0,\n 1,\n );\n params.scriptedIncomingVisibility[col][row] = incoming.done ? 'active' : 'entering';\n if (!incoming.done) allIncomingDone = false;\n }\n }\n\n return { allOutgoingDone, allIncomingDone };\n}\n","import { DEFAULT_PARTICLE_COLOR_RGB, GRID_COLS, GRID_ROWS } from './constants';\nimport type { ParticleColor, SpinState, SymbolId } from './types';\nimport { clamp, normalizeRgbChannel, normalizeSegment } from './utils/math';\n\nexport function normalizeParticleColor(color?: ParticleColor): {\n mode: 'solid' | 'rainbow';\n rgb: [number, number, number];\n} {\n if (color === 'rainbow') {\n return { mode: 'rainbow', rgb: DEFAULT_PARTICLE_COLOR_RGB };\n }\n const rgb = color ?? DEFAULT_PARTICLE_COLOR_RGB;\n return {\n mode: 'solid',\n rgb: [normalizeRgbChannel(rgb[0]), normalizeRgbChannel(rgb[1]), normalizeRgbChannel(rgb[2])],\n };\n}\n\nexport function normalizeSymbolScale(value: number | undefined, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n return clamp(value, 0.5, 1.2);\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 time: WebGLUniformLocation | null;\n borderPx: WebGLUniformLocation | null;\n borderInsetPx: WebGLUniformLocation | null;\n cornerRadiusPx: WebGLUniformLocation | null;\n noiseAmp: WebGLUniformLocation | null;\n pulseStrength: WebGLUniformLocation | null;\n};\n\nconst VERTEX_SHADER_SOURCE = `\nattribute vec2 a_pos;\nuniform vec2 u_resolution;\nuniform mediump 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 mediump vec4 u_destRect;\nuniform vec4 u_srcRect;\nuniform vec4 u_color;\nuniform float u_useTexture;\nuniform float u_shapeMode;\nuniform float u_time;\nuniform float u_borderPx;\nuniform float u_borderInsetPx;\nuniform float u_cornerRadiusPx;\nuniform float u_noiseAmp;\nuniform float u_pulseStrength;\nvarying vec2 v_uv;\n\nfloat hash1(float n) {\n return fract(sin(n) * 43758.5453);\n}\n\nvoid main() {\n if (u_shapeMode > 1.5) {\n vec2 sizePx = max(vec2(1.0), u_destRect.zw);\n vec2 px = v_uv * sizePx;\n float cornerRadius = clamp(u_cornerRadiusPx, 0.0, max(0.0, min(sizePx.x, sizePx.y) * 0.5 - 0.01));\n\n vec2 halfSize = sizePx * 0.5;\n vec2 p = px - halfSize;\n vec2 q = abs(p) - (halfSize - vec2(cornerRadius));\n float outside = length(max(q, vec2(0.0)));\n float inside = min(max(q.x, q.y), 0.0);\n float d = -(outside + inside - cornerRadius);\n\n float left = px.x;\n float right = sizePx.x - px.x;\n float top = px.y;\n float bottom = sizePx.y - px.y;\n\n float side = 0.0;\n float s = 0.0;\n float perimeter = max(1.0, (sizePx.x + sizePx.y) * 2.0);\n if (top <= left && top <= right && top <= bottom) {\n side = 0.0;\n s = px.x;\n } else if (right <= left && right <= top && right <= bottom) {\n side = 1.0;\n s = sizePx.x + px.y;\n } else if (bottom <= left && bottom <= right && bottom <= top) {\n side = 2.0;\n s = sizePx.x + sizePx.y + (sizePx.x - px.x);\n } else {\n side = 3.0;\n s = sizePx.x + sizePx.y + sizePx.x + (sizePx.y - px.y);\n }\n\n float sideSeed = hash1(side * 17.31 + sizePx.x * 0.013 + sizePx.y * 0.007);\n float phase = s * (0.12 + sideSeed * 0.06) + u_time * (6.0 + sideSeed * 4.0);\n float n1 = sin(phase);\n float n2 = sin(phase * 1.87 + 1.6 + sideSeed * 5.1);\n float n3 = sin(phase * 2.53 + 0.73);\n float waviness = (n1 * 0.58 + n2 * 0.3 + n3 * 0.12) * u_noiseAmp;\n\n float borderCenter = max(0.5, u_borderInsetPx + waviness);\n float borderDist = d - borderCenter;\n float core = exp(-pow(borderDist / max(1.0, u_borderPx * 0.72), 2.0));\n float glow = exp(-pow(borderDist / max(1.0, u_borderPx * 2.1), 2.0)) * 0.55;\n\n float pulseA = fract(u_time * 0.11 + 0.07);\n float pulseB = fract(u_time * 0.11 + 0.41);\n float pulseC = fract(u_time * 0.11 + 0.78);\n float posA = pulseA * perimeter;\n float posB = pulseB * perimeter;\n float posC = pulseC * perimeter;\n float dsA = min(abs(s - posA), perimeter - abs(s - posA));\n float dsB = min(abs(s - posB), perimeter - abs(s - posB));\n float dsC = min(abs(s - posC), perimeter - abs(s - posC));\n float pulseBand = exp(-pow(dsA / 18.0, 2.0)) + exp(-pow(dsB / 22.0, 2.0)) + exp(-pow(dsC / 16.0, 2.0));\n pulseBand *= exp(-pow(borderDist / max(1.0, u_borderPx * 1.25), 2.0));\n float outerFade = smoothstep(0.0, max(0.75, u_borderInsetPx * 0.95), d);\n\n float flicker = 0.84 + 0.16 * sin(u_time * 21.0 + s * 0.2 + side * 3.1);\n float alpha = (core + glow + pulseBand * u_pulseStrength) * u_color.a * flicker * outerFade;\n if (alpha <= 0.003) {\n discard;\n }\n gl_FragColor = vec4(u_color.rgb * (0.9 + pulseBand * 0.24), alpha);\n } else 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 time: this.gl.getUniformLocation(this.program, 'u_time'),\n borderPx: this.gl.getUniformLocation(this.program, 'u_borderPx'),\n borderInsetPx: this.gl.getUniformLocation(this.program, 'u_borderInsetPx'),\n cornerRadiusPx: this.gl.getUniformLocation(this.program, 'u_cornerRadiusPx'),\n noiseAmp: this.gl.getUniformLocation(this.program, 'u_noiseAmp'),\n pulseStrength: this.gl.getUniformLocation(this.program, 'u_pulseStrength'),\n };\n\n this.gl.uniform1i(this.uniforms.texture, 0);\n this.gl.uniform1f(this.uniforms.time, 0);\n this.gl.uniform1f(this.uniforms.borderPx, 1);\n this.gl.uniform1f(this.uniforms.borderInsetPx, 0);\n this.gl.uniform1f(this.uniforms.cornerRadiusPx, 0);\n this.gl.uniform1f(this.uniforms.noiseAmp, 0);\n this.gl.uniform1f(this.uniforms.pulseStrength, 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 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 drawElectricBorder(params: {\n x: number;\n y: number;\n width: number;\n height: number;\n rgba: [number, number, number, number];\n timeMs: number;\n borderThicknessPx: number;\n borderInsetPx: number;\n cornerRadiusPx: number;\n noiseAmplitudePx: number;\n pulseStrength: number;\n }): void {\n this.gl.uniform4f(this.uniforms.destRect, params.x, params.y, params.width, params.height);\n this.gl.uniform4f(this.uniforms.srcRect, 0, 0, 1, 1);\n this.gl.uniform4f(\n this.uniforms.color,\n params.rgba[0],\n params.rgba[1],\n params.rgba[2],\n params.rgba[3],\n );\n this.gl.uniform1f(this.uniforms.useTexture, 0);\n this.gl.uniform1f(this.uniforms.shapeMode, 2);\n this.gl.uniform1f(this.uniforms.time, params.timeMs * 0.001);\n this.gl.uniform1f(this.uniforms.borderPx, Math.max(0.5, params.borderThicknessPx));\n this.gl.uniform1f(this.uniforms.borderInsetPx, Math.max(0, params.borderInsetPx));\n this.gl.uniform1f(this.uniforms.cornerRadiusPx, Math.max(0, params.cornerRadiusPx));\n this.gl.uniform1f(this.uniforms.noiseAmp, Math.max(0, params.noiseAmplitudePx));\n this.gl.uniform1f(this.uniforms.pulseStrength, Math.max(0, params.pulseStrength));\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_MOTION_PROFILE,\n DEFAULT_SPRITE_ELEMENTS_COUNT,\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 type MotionProfile,\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 { CellVisibility, 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 normalizeStopGrid,\n normalizeSymbolScale,\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\ntype GridRenderSampler = {\n grid: SymbolId[][];\n skipWinningCells: boolean;\n sampleOffsetY: (col: number, row: number) => number;\n sampleAlpha: (col: number, row: number) => number;\n isVisible: (col: number, row: number) => boolean;\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 private static readonly DEFAULT_SYMBOL_SCALE = 0.9;\n\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n private readonly button?: HTMLButtonElement;\n private readonly spinQueueController: SpinQueueController;\n private readonly spriteSource?: CascadingReelConfig['sprite'];\n private readonly spriteCrossOrigin: NonNullable<CascadingReelConfig['spriteCrossOrigin']>;\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 symbolScale: number;\n private readonly motionProfile: MotionProfile;\n private readonly isCoarsePointerDevice: boolean;\n\n private spriteImage: HTMLImageElement | null = null;\n private spriteLoadError: string | null = null;\n private webglRenderer: WebGLRenderer | null = null;\n private readonly rafLoop = new RafLoop();\n private readonly runtime = createRuntimeState();\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 scriptedOutroElapsedMs = 0;\n private outroMotionPlan: OutroMotionPlan | null = null;\n private scriptedOutgoingOffsets: number[][] = createZeroOffsets();\n private scriptedOutgoingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingOffsets: number[][] = createZeroOffsets();\n private scriptedIncomingOffsetsPrev: number[][] = createZeroOffsets();\n private scriptedIncomingAlpha: number[][] = createZeroOffsets();\n private scriptedIncomingAlphaPrev: number[][] = createZeroOffsets();\n private scriptedIncomingVisibility: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\n private scriptedIncomingVisibilityPrev: CellVisibility[][] =\n CascadingReel.createVisibilityGrid('hidden');\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 simulationLastNow = 0;\n private simulationAccumulatorMs = 0;\n private outroInterpolationAlpha = 1;\n private isOutroPipelineWarmedUp = false;\n private isGpuPipelineWarmedUp = false;\n private perfWindowStartedAt = 0;\n private perfFrameCount = 0;\n private perfOver20MsCount = 0;\n private readonly perfDtSamples: number[] = [];\n private mobileDprCap = 2;\n private mobilePerfGoodWindows = 0;\n private mobilePerfBadWindows = 0;\n private static readonly PERF_WINDOW_MS = 1500;\n private static readonly PERF_BAD_P95_DT_MS = 19;\n private static readonly PERF_GOOD_P95_DT_MS = 17.5;\n private static readonly PERF_BAD_SLOW_RATIO = 0.03;\n private static readonly MOBILE_BAD_WINDOWS_TO_DECREASE_DPR = 2;\n private static readonly MOBILE_GOOD_WINDOWS_TO_INCREASE_DPR = 4;\n private static readonly MOBILE_DPR_CAP_MIN = 1.25;\n private static readonly MOBILE_DPR_CAP_MAX = 1.5;\n private static readonly MOBILE_DPR_STEP_DOWN = 0.1;\n private static readonly MOBILE_DPR_STEP_UP = 0.05;\n private static readonly DPR_QUANT_STEP = 0.25;\n private static readonly MAX_CANVAS_AREA_PX_COARSE = 900 * 900;\n private static readonly MAX_CANVAS_AREA_PX_FINE = 1400 * 1400;\n private static readonly PRE_SPIN_MS = 150;\n private static readonly WIN_EFFECTS_ENVELOPE_TAU_MS = 120;\n private static readonly MAX_FRAME_DELTA_MS = 100;\n private static readonly WIN_BORDER_ALPHA = 0.72;\n private static readonly WIN_BORDER_INSET_RATIO = 0.08;\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.spriteSource = config.sprite;\n this.spriteCrossOrigin = config.spriteCrossOrigin ?? 'anonymous';\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 const particleColor = normalizeParticleColor(config.particleColor);\n this.particleColorRgb = particleColor.rgb;\n this.particleColorMode = particleColor.mode;\n this.symbolScale = normalizeSymbolScale(config.symbolScale, CascadingReel.DEFAULT_SYMBOL_SCALE);\n this.motionProfile = DEFAULT_MOTION_PROFILE;\n this.isCoarsePointerDevice = CascadingReel.detectCoarsePointerDevice();\n this.mobileDprCap = this.isCoarsePointerDevice ? CascadingReel.MOBILE_DPR_CAP_MAX : 2;\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 const reason = this.spriteLoadError ? ` (${this.spriteLoadError})` : '';\n throw new Error(`sprite is required for WebGL renderer${reason}`);\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.warmUpOutroPipeline();\n this.warmUpGpuPipeline();\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 this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n destroyState(this.runtime);\n this.webglRenderer?.dispose();\n this.webglRenderer = null;\n this.clearWinningCells();\n }\n\n public spin(): void {\n this.dismissHighlightIfActive();\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 this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\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 document.addEventListener('visibilitychange', this.onVisibilityChange);\n this.button?.addEventListener('click', this.onSpinClick);\n }\n\n private unbindEvents(): void {\n window.removeEventListener('resize', this.resize);\n document.removeEventListener('visibilitychange', this.onVisibilityChange);\n this.button?.removeEventListener('click', this.onSpinClick);\n }\n\n private readonly onVisibilityChange = (): void => {\n this.lastRafTime = 0;\n this.simulationLastNow = 0;\n this.simulationAccumulatorMs = 0;\n this.resetPerfWindow();\n };\n\n private readonly onSpinClick = (): void => {\n if (this.runtime.isSpinning && this.runtime.phase !== 'winFlash') 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 return;\n }\n }\n\n private stepScriptedOutro(stepMs: number): void {\n if (!this.scriptedOutgoingGrid || !this.scriptedPendingGrid) {\n this.finishSpinWithUi();\n return;\n }\n if (!this.outroMotionPlan) {\n this.outroMotionPlan = this.createOutroMotionPlan();\n }\n this.scriptedOutroElapsedMs += stepMs;\n const { allOutgoingDone, allIncomingDone } = updateOutroOffsets({\n elapsedMs: this.scriptedOutroElapsedMs,\n scriptedOutgoingOffsets: this.scriptedOutgoingOffsets,\n scriptedIncomingOffsets: this.scriptedIncomingOffsets,\n scriptedIncomingAlpha: this.scriptedIncomingAlpha,\n scriptedIncomingVisibility: this.scriptedIncomingVisibility,\n motionPlan: this.outroMotionPlan,\n });\n\n if (!allOutgoingDone || !allIncomingDone) return;\n\n this.grid = this.scriptedPendingGrid;\n this.scriptedOutgoingGrid = null;\n this.scriptedPendingGrid = null;\n this.outroMotionPlan = null;\n this.clearWinningCells();\n this.resetOutroBuffers(1, 'active');\n\n if (this.tryStartScriptedCascade(this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs))\n return;\n if (!this.runtime.shouldHighlightCurrentSpin) {\n this.finishSpinWithUi();\n return;\n }\n\n this.setWinningCells(findMostFrequentCells(this.grid));\n startWinFlash(this.runtime, this.scriptedOutroStartedAt + this.scriptedOutroElapsedMs);\n this.runtime.activeSpinState?.callback?.();\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 = this.createOutroMotionPlan();\n this.resetOutroBuffers(0, 'hidden');\n this.clearWinningCells();\n this.runtime.phase = 'outro';\n this.scriptedOutroStartedAt = now;\n this.scriptedOutroElapsedMs = 0;\n this.outroInterpolationAlpha = 1;\n }\n\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 dismissHighlightIfActive(): void {\n if (this.runtime.phase !== 'winFlash') return;\n this.finishSpinWithUi(true);\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 warmUpOutroPipeline(): void {\n if (this.isOutroPipelineWarmedUp) return;\n const motionPlan = this.createOutroMotionPlan();\n const outgoingOffsets = createZeroOffsets();\n const incomingOffsets = createZeroOffsets();\n const incomingAlpha = createZeroOffsets();\n const incomingVisibility = CascadingReel.createVisibilityGrid('hidden');\n const maxRowDelay = Math.max(...motionPlan.rowStartDelays);\n const maxColumnDelay = (GRID_COLS - 1) * motionPlan.columnStaggerMs;\n const warmupMoments = [\n 0,\n this.motionProfile.fixedStepMs,\n motionPlan.incomingStartShift + this.motionProfile.fixedStepMs,\n motionPlan.fallMs + maxRowDelay + maxColumnDelay + this.motionProfile.fixedStepMs,\n ];\n for (const elapsedMs of warmupMoments) {\n updateOutroOffsets({\n elapsedMs,\n scriptedOutgoingOffsets: outgoingOffsets,\n scriptedIncomingOffsets: incomingOffsets,\n scriptedIncomingAlpha: incomingAlpha,\n scriptedIncomingVisibility: incomingVisibility,\n motionPlan,\n });\n }\n this.isOutroPipelineWarmedUp = true;\n }\n\n private createOutroMotionPlan(): OutroMotionPlan {\n const plan = buildOutroMotionPlan({\n height: this.height,\n boardY: this.boardY,\n cellH: this.cellH,\n motionProfile: this.motionProfile,\n });\n if (!this.isCoarsePointerDevice) return plan;\n const frameMs = 1000 / 60;\n return {\n ...plan,\n columnStaggerMs: this.quantizeMs(plan.columnStaggerMs, frameMs),\n incomingStartShift: this.quantizeMs(plan.incomingStartShift, frameMs),\n rowStartDelays: [\n this.quantizeMs(plan.rowStartDelays[0], frameMs),\n this.quantizeMs(plan.rowStartDelays[1], frameMs),\n this.quantizeMs(plan.rowStartDelays[2], frameMs),\n ],\n };\n }\n\n private quantizeMs(valueMs: number, frameMs: number): number {\n if (valueMs <= 0) return 0;\n const frames = Math.max(1, Math.round(valueMs / frameMs));\n return frames * frameMs;\n }\n\n private warmUpGpuPipeline(): void {\n if (this.isGpuPipelineWarmedUp) return;\n const renderer = this.webglRenderer;\n if (!renderer) return;\n const warmW = Math.max(8, this.cellW * 0.2);\n const warmH = Math.max(8, this.cellH * 0.2);\n const warmX = this.boardX + 2;\n const warmY = this.boardY + 2;\n\n renderer.beginFrame();\n renderer.drawSprite(0, warmX, warmY, warmW, warmH, 1);\n renderer.drawSolidRect(warmX + warmW + 2, warmY, warmW, warmH, [1, 1, 1, 0.35]);\n renderer.beginAdditiveBlend();\n renderer.drawSoftCircle(\n warmX + warmW * 0.5,\n warmY + warmH * 0.5,\n Math.max(2, warmW * 0.25),\n [1, 0.9, 0.4, 0.4],\n );\n renderer.endAdditiveBlend();\n\n this.isGpuPipelineWarmedUp = true;\n }\n\n private trackOutroPerf(dt: number, now: number): void {\n if (this.runtime.phase !== 'outro') {\n this.resetPerfWindow();\n return;\n }\n if (this.perfWindowStartedAt <= 0) this.perfWindowStartedAt = now;\n this.perfFrameCount += 1;\n if (dt > 20) this.perfOver20MsCount += 1;\n this.perfDtSamples.push(dt);\n if (now - this.perfWindowStartedAt < CascadingReel.PERF_WINDOW_MS) return;\n this.flushPerfWindow();\n this.perfWindowStartedAt = now;\n }\n\n private resetPerfWindow(): void {\n this.perfWindowStartedAt = 0;\n this.perfFrameCount = 0;\n this.perfOver20MsCount = 0;\n this.perfDtSamples.length = 0;\n }\n\n private flushPerfWindow(): void {\n if (this.perfFrameCount === 0) return;\n const sorted = [...this.perfDtSamples].sort((a, b) => a - b);\n const p95Index = Math.max(0, Math.min(sorted.length - 1, Math.floor(sorted.length * 0.95)));\n const p95Dt = sorted[p95Index];\n this.adjustMobileDprCap(p95Dt, this.perfOver20MsCount, this.perfFrameCount);\n this.perfFrameCount = 0;\n this.perfOver20MsCount = 0;\n this.perfDtSamples.length = 0;\n }\n\n private adjustMobileDprCap(p95Dt: number, slowFrames: number, totalFrames: number): void {\n if (!this.isCoarsePointerDevice) return;\n if (totalFrames <= 0) return;\n\n const slowRatio = slowFrames / totalFrames;\n const isBadWindow =\n p95Dt > CascadingReel.PERF_BAD_P95_DT_MS || slowRatio > CascadingReel.PERF_BAD_SLOW_RATIO;\n const isGoodWindow = p95Dt <= CascadingReel.PERF_GOOD_P95_DT_MS && slowFrames === 0;\n let nextCap = this.mobileDprCap;\n\n if (isBadWindow) {\n this.mobilePerfBadWindows += 1;\n this.mobilePerfGoodWindows = 0;\n if (this.mobilePerfBadWindows >= CascadingReel.MOBILE_BAD_WINDOWS_TO_DECREASE_DPR) {\n nextCap = Math.max(\n CascadingReel.MOBILE_DPR_CAP_MIN,\n this.mobileDprCap - CascadingReel.MOBILE_DPR_STEP_DOWN,\n );\n this.mobilePerfBadWindows = 0;\n }\n } else if (isGoodWindow) {\n this.mobilePerfGoodWindows += 1;\n this.mobilePerfBadWindows = 0;\n if (this.mobilePerfGoodWindows >= CascadingReel.MOBILE_GOOD_WINDOWS_TO_INCREASE_DPR) {\n nextCap = Math.min(\n CascadingReel.MOBILE_DPR_CAP_MAX,\n this.mobileDprCap + CascadingReel.MOBILE_DPR_STEP_UP,\n );\n this.mobilePerfGoodWindows = 0;\n }\n } else {\n this.mobilePerfGoodWindows = 0;\n this.mobilePerfBadWindows = 0;\n }\n\n if (Math.abs(nextCap - this.mobileDprCap) < 0.001) return;\n this.mobileDprCap = nextCap;\n this.resize();\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 this.trackOutroPerf(dt, now);\n const kWin = 1 - Math.exp(-dt / CascadingReel.WIN_EFFECTS_ENVELOPE_TAU_MS);\n this.runtime.winEffectsEnvelope +=\n (winEffectsTarget - this.runtime.winEffectsEnvelope) * kWin;\n } else {\n this.resetPerfWindow();\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.drawGridInterpolated(\n this.scriptedOutgoingGrid,\n this.scriptedOutgoingOffsetsPrev,\n this.scriptedOutgoingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n );\n this.drawGridInterpolated(\n this.scriptedPendingGrid,\n this.scriptedIncomingOffsetsPrev,\n this.scriptedIncomingOffsets,\n this.outroInterpolationAlpha,\n skipWinningCells,\n this.scriptedIncomingAlphaPrev,\n this.scriptedIncomingAlpha,\n this.scriptedIncomingVisibility,\n );\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 applyPixelSnapY(y: number): number {\n if (this.isCoarsePointerDevice) return Math.round(y * 2) / 2;\n return y;\n }\n\n private drawGrid(\n grid: SymbolId[][],\n offsets: number[][] | null,\n skipWinningCells: boolean,\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) => (offsets ? offsets[col][row] : 0),\n sampleAlpha: () => 1,\n isVisible: () => true,\n });\n }\n\n private drawGridInterpolated(\n grid: SymbolId[][],\n prevOffsets: number[][],\n currOffsets: number[][],\n alpha: number,\n skipWinningCells: boolean,\n prevOpacity?: number[][],\n currOpacity?: number[][],\n visibility?: CellVisibility[][],\n ): void {\n this.drawGridWithSampler({\n grid,\n skipWinningCells,\n sampleOffsetY: (col, row) =>\n prevOffsets[col][row] + (currOffsets[col][row] - prevOffsets[col][row]) * alpha,\n sampleAlpha: (col, row) =>\n prevOpacity && currOpacity\n ? prevOpacity[col][row] + (currOpacity[col][row] - prevOpacity[col][row]) * alpha\n : 1,\n isVisible: (col, row) => !visibility || visibility[col][row] !== 'hidden',\n });\n }\n\n private drawGridWithSampler(sampler: GridRenderSampler): 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 (sampler.skipWinningCells && this.isWinningCell(col, row)) continue;\n if (!sampler.isVisible(col, row)) continue;\n const offsetY = sampler.sampleOffsetY(col, row);\n const y = this.applyPixelSnapY(\n this.boardY + row * this.cellH + offsetY + this.getRowCompactOffset(row),\n );\n if (y > this.height || y + this.cellH < 0) continue;\n const spriteAlpha = sampler.sampleAlpha(col, row);\n if (spriteAlpha <= 0) continue;\n const symbolW = this.cellW * this.symbolScale;\n const symbolH = this.cellH * this.symbolScale;\n const symbolOffsetX = (this.cellW - symbolW) * 0.5;\n const symbolOffsetY = (this.cellH - symbolH) * 0.5;\n renderer.drawSprite(\n sampler.grid[col][row],\n x + symbolOffsetX,\n y + symbolOffsetY,\n symbolW,\n symbolH,\n spriteAlpha,\n );\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.022);\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: [number, number, number] =\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 * this.symbolScale * pulse;\n const scaledH = this.cellH * this.symbolScale * 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 this.drawElectricBorder({\n renderer,\n cell,\n x: innerX,\n y: innerY,\n w: innerW,\n h: innerH,\n borderThickness,\n borderColor,\n alpha,\n elapsed,\n envelope,\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 drawElectricBorder(params: {\n renderer: WebGLRenderer;\n cell: CellPosition;\n x: number;\n y: number;\n w: number;\n h: number;\n borderThickness: number;\n borderColor: [number, number, number];\n alpha: number;\n elapsed: number;\n envelope: number;\n }): void {\n const baseA = Math.max(0, Math.min(1, params.alpha * params.envelope));\n const expand = params.borderThickness * 1.8;\n const seedTime = params.elapsed + params.cell.col * 170 + params.cell.row * 290;\n params.renderer.beginAdditiveBlend();\n params.renderer.drawElectricBorder({\n x: params.x - expand,\n y: params.y - expand,\n width: params.w + expand * 2,\n height: params.h + expand * 2,\n rgba: [params.borderColor[0], params.borderColor[1], params.borderColor[2], baseA * 0.55],\n timeMs: seedTime,\n borderThicknessPx: Math.max(0.8, params.borderThickness * 1.1),\n borderInsetPx: expand * 0.85,\n cornerRadiusPx: Math.max(2, params.borderThickness * 2.5),\n noiseAmplitudePx: Math.max(0.15, params.borderThickness * 0.6),\n pulseStrength: 0.9,\n });\n params.renderer.drawElectricBorder({\n x: params.x - expand * 0.5,\n y: params.y - expand * 0.5,\n width: params.w + expand,\n height: params.h + expand,\n rgba: [params.borderColor[0], params.borderColor[1], params.borderColor[2], baseA * 0.9],\n timeMs: seedTime * 1.03,\n borderThicknessPx: Math.max(0.7, params.borderThickness * 0.8),\n borderInsetPx: expand * 0.5,\n cornerRadiusPx: Math.max(1.5, params.borderThickness * 1.7),\n noiseAmplitudePx: Math.max(0.12, params.borderThickness * 0.42),\n pulseStrength: 1.25,\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 detectCoarsePointerDevice(): boolean {\n if (typeof window === 'undefined') return false;\n const coarseMatch =\n typeof window.matchMedia === 'function' && window.matchMedia('(pointer: coarse)').matches;\n const touchPoints =\n typeof navigator !== 'undefined' && typeof navigator.maxTouchPoints === 'number'\n ? navigator.maxTouchPoints\n : 0;\n return coarseMatch || touchPoints > 0;\n }\n\n private quantizeDprCap(value: number): number {\n const step = CascadingReel.DPR_QUANT_STEP;\n return Math.max(1, Math.round(value / step) * step);\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 const cssSide = Math.max(300, Math.floor(bounds.width));\n const cssArea = Math.max(1, cssSide * cssSide);\n const maxCanvasArea = this.isCoarsePointerDevice\n ? CascadingReel.MAX_CANVAS_AREA_PX_COARSE\n : CascadingReel.MAX_CANVAS_AREA_PX_FINE;\n const dprAreaCap = Math.max(1, Math.sqrt(maxCanvasArea / cssArea));\n const requestedCap = this.isCoarsePointerDevice ? this.mobileDprCap : 2;\n const dprCap = this.quantizeDprCap(Math.min(requestedCap, dprAreaCap));\n const dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, dprCap));\n const side = Math.max(300, Math.floor(cssSide * 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.spriteSource) {\n this.spriteImage = null;\n this.spriteLoadError = null;\n return;\n }\n\n if (typeof this.spriteSource !== 'string') {\n try {\n await this.decodeSpriteImage(this.spriteSource);\n this.spriteImage = this.spriteSource;\n this.spriteLoadError = null;\n } catch (error) {\n this.spriteImage = null;\n this.spriteLoadError = this.toSpriteLoadErrorMessage(error, '[HTMLImageElement]');\n }\n return;\n }\n\n const image = new Image();\n image.decoding = 'async';\n image.crossOrigin = this.spriteCrossOrigin;\n image.src = this.spriteSource;\n try {\n await this.decodeSpriteImage(image);\n this.spriteImage = image;\n this.spriteLoadError = null;\n } catch (error) {\n this.spriteImage = null;\n this.spriteLoadError = this.toSpriteLoadErrorMessage(error, this.spriteSource);\n }\n }\n\n private async decodeSpriteImage(image: HTMLImageElement): Promise<void> {\n if (image.complete && image.naturalWidth > 0 && image.naturalHeight > 0) return;\n await image.decode();\n }\n\n private toSpriteLoadErrorMessage(error: unknown, spriteSourceLabel: string): string {\n const errorText = error instanceof Error ? `${error.name}: ${error.message}` : String(error);\n return `failed to load sprite \"${spriteSourceLabel}\" - ${errorText}`;\n }\n\n private advanceSimulation(now: number): void {\n if (this.simulationLastNow <= 0) {\n this.simulationLastNow = now;\n this.update(now);\n return;\n }\n const frameDt = Math.max(\n 0,\n Math.min(now - this.simulationLastNow, CascadingReel.MAX_FRAME_DELTA_MS),\n );\n this.simulationLastNow = now;\n this.update(now);\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n this.advanceOutroFixedStep(frameDt);\n }\n\n private advanceOutroFixedStep(frameDt: number): void {\n this.simulationAccumulatorMs += frameDt;\n const stepMs = this.motionProfile.fixedStepMs;\n let steps = 0;\n while (\n this.simulationAccumulatorMs >= stepMs &&\n steps < this.motionProfile.maxCatchUpStepsPerFrame &&\n this.runtime.phase === 'outro'\n ) {\n this.snapshotOutroState();\n this.stepScriptedOutro(stepMs);\n this.simulationAccumulatorMs -= stepMs;\n steps += 1;\n }\n if (this.runtime.phase !== 'outro') {\n this.outroInterpolationAlpha = 1;\n return;\n }\n this.outroInterpolationAlpha = Math.max(0, Math.min(1, this.simulationAccumulatorMs / stepMs));\n }\n\n private snapshotOutroState(): void {\n this.copyOffsets(this.scriptedOutgoingOffsetsPrev, this.scriptedOutgoingOffsets);\n this.copyOffsets(this.scriptedIncomingOffsetsPrev, this.scriptedIncomingOffsets);\n this.copyOffsets(this.scriptedIncomingAlphaPrev, this.scriptedIncomingAlpha);\n this.copyVisibility(this.scriptedIncomingVisibilityPrev, this.scriptedIncomingVisibility);\n }\n\n private copyOffsets(target: number[][], source: number[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private copyVisibility(target: CellVisibility[][], source: CellVisibility[][]): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n target[col][row] = source[col][row];\n }\n }\n }\n\n private resetOutroBuffers(incomingAlpha: number, incomingVisibility: CellVisibility): void {\n fillOffsets(this.scriptedOutgoingOffsets, 0);\n fillOffsets(this.scriptedIncomingOffsets, 0);\n fillOffsets(this.scriptedOutgoingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingOffsetsPrev, 0);\n fillOffsets(this.scriptedIncomingAlpha, incomingAlpha);\n fillOffsets(this.scriptedIncomingAlphaPrev, incomingAlpha);\n this.fillVisibilityGrid(this.scriptedIncomingVisibility, incomingVisibility);\n this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev, incomingVisibility);\n }\n\n private static createVisibilityGrid(value: CellVisibility): CellVisibility[][] {\n return Array.from({ length: GRID_COLS }, () => Array.from({ length: GRID_ROWS }, () => value));\n }\n\n private fillVisibilityGrid(grid: CellVisibility[][], value: CellVisibility): void {\n for (let col = 0; col < GRID_COLS; col += 1) {\n for (let row = 0; row < GRID_ROWS; row += 1) {\n grid[col][row] = value;\n }\n }\n }\n\n private startLoop(): void {\n if (this.rafLoop.isRunning()) return;\n this.rafLoop.start((time: number): boolean => {\n this.advanceSimulation(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"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cascading-reel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A high-performance WebGL cascading reel animator for slot-style UIs on any HTMLCanvasElement",
|
|
5
5
|
"author": "ux-ui.pro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,49 +14,57 @@
|
|
|
14
14
|
"homepage": "https://github.com/ux-ui-pro/cascading-reel",
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"type": "module",
|
|
17
|
-
"packageManager": "
|
|
17
|
+
"packageManager": "npm@10.9.3",
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
19
|
+
"node": ">=20.19.0"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"clean": "rimraf dist",
|
|
23
|
-
"build": "
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"lint": "biome check src",
|
|
27
|
-
"
|
|
28
|
-
"format": "biome format --write src",
|
|
23
|
+
"build": "tsdown",
|
|
24
|
+
"verify": "npm run lint && npm run typecheck && npm run build && npm run test:smoke && npm run pack:check",
|
|
25
|
+
"lint": "biome check src tests tsdown.config.ts",
|
|
26
|
+
"lint:fix": "biome check --write src tests tsdown.config.ts",
|
|
27
|
+
"format": "biome format --write src tests tsdown.config.ts",
|
|
29
28
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
-
"test:smoke": "node --test tests
|
|
31
|
-
"
|
|
29
|
+
"test:smoke": "node --test \"tests/**/*.js\"",
|
|
30
|
+
"pack:check": "npm pack --dry-run && publint && attw --pack . --profile node16",
|
|
31
|
+
"prepublishOnly": "npm run clean && npm run verify"
|
|
32
32
|
},
|
|
33
33
|
"source": "src/index.ts",
|
|
34
|
-
"main": "dist/index.cjs
|
|
35
|
-
"module": "dist/index.
|
|
36
|
-
"
|
|
37
|
-
"types": "dist/index.d.ts",
|
|
34
|
+
"main": "./dist/index.cjs",
|
|
35
|
+
"module": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
38
37
|
"exports": {
|
|
39
38
|
".": {
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
"import": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
},
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./dist/index.d.cts",
|
|
45
|
+
"default": "./dist/index.cjs"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
46
48
|
},
|
|
47
49
|
"files": [
|
|
48
50
|
"dist",
|
|
49
51
|
"README.md",
|
|
50
52
|
"LICENSE"
|
|
51
53
|
],
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
52
57
|
"devDependencies": {
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
58
|
+
"@arethetypeswrong/cli": "^0.18.3",
|
|
59
|
+
"@biomejs/biome": "2.4.16",
|
|
60
|
+
"@types/node": "25.9.2",
|
|
61
|
+
"@ux-ui/biome-config": "^0.1.0",
|
|
62
|
+
"@ux-ui/tsconfig-base": "^0.1.0",
|
|
63
|
+
"@ux-ui/tsdown-config": "^0.1.0",
|
|
64
|
+
"publint": "^0.3.21",
|
|
56
65
|
"rimraf": "6.1.3",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"vite-plugin-dts": "4.5.4"
|
|
66
|
+
"tsdown": "0.22.3",
|
|
67
|
+
"typescript": "6.0.3"
|
|
60
68
|
},
|
|
61
69
|
"keywords": [
|
|
62
70
|
"typescript",
|
package/dist/index.cjs.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var U=[.04,0,-.04],S={columnStaggerMs:76,fallMs:800,outroOverlapMs:88,outroRowGapMs:14,rowBaseSpacingRatio:.05,incomingAlphaRampMs:34,fixedStepMs:1e3/120,maxCatchUpStepsPerFrame:6},ot=S.outroRowGapMs,lt=S.rowBaseSpacingRatio,v=1800,N=.15,T=[255,235,110],ht=S.columnStaggerMs,at=S.fallMs,ct=S.outroOverlapMs;function M(e,t,i){return e<t?t:e>i?i:e}function q(e){return Math.floor(Math.random()*e)}function b(e,t){return(e%t+t)%t}function R(e){return M(Math.round(e),0,255)}function L(e){const t=[];for(let i=0;i<3;i+=1){const s=[];for(let r=0;r<3;r+=1)s.push(q(e));t.push(s)}return t}function W(e){const t=new Map;for(let n=0;n<3;n+=1)for(let o=0;o<3;o+=1){const l=e[n][o];t.set(l,(t.get(l)??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 g(){return Array.from({length:3},()=>Array.from({length:3},()=>0))}function m(e,t){for(let i=0;i<3;i+=1)for(let s=0;s<3;s+=1)e[i][s]=t}var z=class{rafId=null;step=null;start(e){this.rafId===null&&(this.step=e,this.rafId=requestAnimationFrame(this.tick))}stop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.step=null}isRunning(){return this.rafId!==null}tick=e=>{if(!this.step){this.stop();return}if(this.step(e)===!1){this.stop();return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.tick))}};function X(e){const t=M(e,0,1);return t*t*t*(t*(t*6-15)+10)}function y(e,t){if(e.endMs<=e.startMs)return{value:e.to,t:1,done:!0};const i=M((t-e.startMs)/(e.endMs-e.startMs),0,1),s=X(i);return{value:e.from+(e.to-e.from)*s,t:i,done:i>=1}}function k(e,t,i,s){const r=[0,0,0];let n=0;for(let o=2;o>=0;o-=1){if(e[o]===0){r[o]=0;continue}r[o]=n;const l=Math.floor(t*s);n+=l+i}return r}function V(e){const t=e.height-e.boardY+e.cellH+2,i=[t,t,t];return{columnStaggerMs:e.motionProfile.columnStaggerMs,fallMs:e.motionProfile.fallMs,incomingAlphaRampMs:e.motionProfile.incomingAlphaRampMs,outgoingDistance:t,incomingFromOffsets:[-e.cellH,-e.cellH*2,-e.cellH*3],rowStartDelays:k(i,e.motionProfile.fallMs,e.motionProfile.outroRowGapMs,e.motionProfile.rowBaseSpacingRatio),incomingStartShift:Math.max(0,e.motionProfile.fallMs-e.motionProfile.outroOverlapMs)}}function D(e){let t=!0,i=!0;for(let s=0;s<e.scriptedOutgoingOffsets.length;s+=1){const r=e.elapsedMs-s*e.motionPlan.columnStaggerMs;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]=e.motionPlan.incomingFromOffsets[n],e.scriptedIncomingAlpha[s][n]=0,e.scriptedIncomingVisibility[s][n]="hidden",t=!1,i=!1;continue}const l=y({startMs:0,endMs:e.motionPlan.fallMs,from:0,to:e.motionPlan.outgoingDistance},o);e.scriptedOutgoingOffsets[s][n]=l.value,e.scriptedIncomingVisibility[s][n]=l.done?"entering":"exiting",l.done||(t=!1);const u=o-e.motionPlan.incomingStartShift;if(u<=0){e.scriptedIncomingOffsets[s][n]=e.motionPlan.incomingFromOffsets[n],e.scriptedIncomingAlpha[s][n]=0,e.scriptedIncomingVisibility[s][n]="hidden",i=!1;continue}const c=y({startMs:0,endMs:e.motionPlan.fallMs,from:e.motionPlan.incomingFromOffsets[n],to:0},u);e.scriptedIncomingOffsets[s][n]=c.value,e.scriptedIncomingAlpha[s][n]=M(u/Math.max(1,e.motionPlan.incomingAlphaRampMs),0,1),e.scriptedIncomingVisibility[s][n]=c.done?"active":"entering",c.done||(i=!1)}}return{allOutgoingDone:t,allIncomingDone:i}}function Y(e){if(e==="rainbow")return{mode:"rainbow",rgb:T};const t=e??T;return{mode:"solid",rgb:[R(t[0]),R(t[1]),R(t[2])]}}function $(e,t){return typeof e!="number"||!Number.isFinite(e)?t:M(e,.5,1.2)}function C(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 x(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]=[b(r[0],t),b(r[1],t),b(r[2],t)]}return i}function K(e,t){return x(C(e),t)}function Q(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}}var j=class{queue;constructor(e){this.queue=(e??[]).map(t=>Q(t))}hasPending(){return this.queue.length>0}consume(){return this.queue.length===0?null:this.queue.shift()??null}};function Z(){return{isSpinning:!1,hasStartedFirstSpin:!1,queueFinished:!1,shouldHighlightCurrentSpin:!1,activeSpinState:null,phase:"idle",winFlashStartedAt:0,outroStartedAt:0,idleStartedAt:0,preSpinStartedAt:0,winEffectsEnvelope:1}}function J(e,t){e.hasStartedFirstSpin=!0,e.isSpinning=!0,e.phase="outro",e.outroStartedAt=t.startedAt,e.activeSpinState=t.activeSpinState,e.shouldHighlightCurrentSpin=t.shouldHighlightCurrentSpin}function tt(e,t,i){e.phase="idle",e.idleStartedAt=i,e.isSpinning=!1,e.shouldHighlightCurrentSpin=!1,e.queueFinished=!t,e.activeSpinState=null}function F(e,t){e.winFlashStartedAt=t,e.phase="winFlash"}function it(e){e.isSpinning=!1,e.queueFinished=!0}var et=`
|
|
2
|
-
attribute vec2 a_pos;
|
|
3
|
-
uniform vec2 u_resolution;
|
|
4
|
-
uniform mediump 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
|
-
`,st=`
|
|
24
|
-
precision mediump float;
|
|
25
|
-
|
|
26
|
-
uniform sampler2D u_texture;
|
|
27
|
-
uniform mediump vec4 u_destRect;
|
|
28
|
-
uniform vec4 u_srcRect;
|
|
29
|
-
uniform vec4 u_color;
|
|
30
|
-
uniform float u_useTexture;
|
|
31
|
-
uniform float u_shapeMode;
|
|
32
|
-
uniform float u_time;
|
|
33
|
-
uniform float u_borderPx;
|
|
34
|
-
uniform float u_borderInsetPx;
|
|
35
|
-
uniform float u_cornerRadiusPx;
|
|
36
|
-
uniform float u_noiseAmp;
|
|
37
|
-
uniform float u_pulseStrength;
|
|
38
|
-
varying vec2 v_uv;
|
|
39
|
-
|
|
40
|
-
float hash1(float n) {
|
|
41
|
-
return fract(sin(n) * 43758.5453);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
void main() {
|
|
45
|
-
if (u_shapeMode > 1.5) {
|
|
46
|
-
vec2 sizePx = max(vec2(1.0), u_destRect.zw);
|
|
47
|
-
vec2 px = v_uv * sizePx;
|
|
48
|
-
float cornerRadius = clamp(u_cornerRadiusPx, 0.0, max(0.0, min(sizePx.x, sizePx.y) * 0.5 - 0.01));
|
|
49
|
-
|
|
50
|
-
vec2 halfSize = sizePx * 0.5;
|
|
51
|
-
vec2 p = px - halfSize;
|
|
52
|
-
vec2 q = abs(p) - (halfSize - vec2(cornerRadius));
|
|
53
|
-
float outside = length(max(q, vec2(0.0)));
|
|
54
|
-
float inside = min(max(q.x, q.y), 0.0);
|
|
55
|
-
float d = -(outside + inside - cornerRadius);
|
|
56
|
-
|
|
57
|
-
float left = px.x;
|
|
58
|
-
float right = sizePx.x - px.x;
|
|
59
|
-
float top = px.y;
|
|
60
|
-
float bottom = sizePx.y - px.y;
|
|
61
|
-
|
|
62
|
-
float side = 0.0;
|
|
63
|
-
float s = 0.0;
|
|
64
|
-
float perimeter = max(1.0, (sizePx.x + sizePx.y) * 2.0);
|
|
65
|
-
if (top <= left && top <= right && top <= bottom) {
|
|
66
|
-
side = 0.0;
|
|
67
|
-
s = px.x;
|
|
68
|
-
} else if (right <= left && right <= top && right <= bottom) {
|
|
69
|
-
side = 1.0;
|
|
70
|
-
s = sizePx.x + px.y;
|
|
71
|
-
} else if (bottom <= left && bottom <= right && bottom <= top) {
|
|
72
|
-
side = 2.0;
|
|
73
|
-
s = sizePx.x + sizePx.y + (sizePx.x - px.x);
|
|
74
|
-
} else {
|
|
75
|
-
side = 3.0;
|
|
76
|
-
s = sizePx.x + sizePx.y + sizePx.x + (sizePx.y - px.y);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
float sideSeed = hash1(side * 17.31 + sizePx.x * 0.013 + sizePx.y * 0.007);
|
|
80
|
-
float phase = s * (0.12 + sideSeed * 0.06) + u_time * (6.0 + sideSeed * 4.0);
|
|
81
|
-
float n1 = sin(phase);
|
|
82
|
-
float n2 = sin(phase * 1.87 + 1.6 + sideSeed * 5.1);
|
|
83
|
-
float n3 = sin(phase * 2.53 + 0.73);
|
|
84
|
-
float waviness = (n1 * 0.58 + n2 * 0.3 + n3 * 0.12) * u_noiseAmp;
|
|
85
|
-
|
|
86
|
-
float borderCenter = max(0.5, u_borderInsetPx + waviness);
|
|
87
|
-
float borderDist = d - borderCenter;
|
|
88
|
-
float core = exp(-pow(borderDist / max(1.0, u_borderPx * 0.72), 2.0));
|
|
89
|
-
float glow = exp(-pow(borderDist / max(1.0, u_borderPx * 2.1), 2.0)) * 0.55;
|
|
90
|
-
|
|
91
|
-
float pulseA = fract(u_time * 0.11 + 0.07);
|
|
92
|
-
float pulseB = fract(u_time * 0.11 + 0.41);
|
|
93
|
-
float pulseC = fract(u_time * 0.11 + 0.78);
|
|
94
|
-
float posA = pulseA * perimeter;
|
|
95
|
-
float posB = pulseB * perimeter;
|
|
96
|
-
float posC = pulseC * perimeter;
|
|
97
|
-
float dsA = min(abs(s - posA), perimeter - abs(s - posA));
|
|
98
|
-
float dsB = min(abs(s - posB), perimeter - abs(s - posB));
|
|
99
|
-
float dsC = min(abs(s - posC), perimeter - abs(s - posC));
|
|
100
|
-
float pulseBand = exp(-pow(dsA / 18.0, 2.0)) + exp(-pow(dsB / 22.0, 2.0)) + exp(-pow(dsC / 16.0, 2.0));
|
|
101
|
-
pulseBand *= exp(-pow(borderDist / max(1.0, u_borderPx * 1.25), 2.0));
|
|
102
|
-
float outerFade = smoothstep(0.0, max(0.75, u_borderInsetPx * 0.95), d);
|
|
103
|
-
|
|
104
|
-
float flicker = 0.84 + 0.16 * sin(u_time * 21.0 + s * 0.2 + side * 3.1);
|
|
105
|
-
float alpha = (core + glow + pulseBand * u_pulseStrength) * u_color.a * flicker * outerFade;
|
|
106
|
-
if (alpha <= 0.003) {
|
|
107
|
-
discard;
|
|
108
|
-
}
|
|
109
|
-
gl_FragColor = vec4(u_color.rgb * (0.9 + pulseBand * 0.24), alpha);
|
|
110
|
-
} else if (u_shapeMode > 0.5) {
|
|
111
|
-
vec2 centered = v_uv - vec2(0.5, 0.5);
|
|
112
|
-
float dist = length(centered) * 2.0;
|
|
113
|
-
if (dist > 1.0) {
|
|
114
|
-
discard;
|
|
115
|
-
}
|
|
116
|
-
float feather = smoothstep(1.0, 0.72, dist);
|
|
117
|
-
gl_FragColor = vec4(u_color.rgb, u_color.a * feather);
|
|
118
|
-
} else if (u_useTexture > 0.5) {
|
|
119
|
-
vec2 uv = vec2(
|
|
120
|
-
mix(u_srcRect.x, u_srcRect.z, v_uv.x),
|
|
121
|
-
mix(u_srcRect.y, u_srcRect.w, v_uv.y)
|
|
122
|
-
);
|
|
123
|
-
vec4 tex = texture2D(u_texture, uv);
|
|
124
|
-
gl_FragColor = tex * u_color;
|
|
125
|
-
} else {
|
|
126
|
-
gl_FragColor = u_color;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
`,rt=class{canvas;spriteImage;spriteElementsCount;gl;program;uniforms;quadBuffer;texture;viewportW=1;viewportH=1;spriteWidth;spriteHeight;spriteSegmentHeight;constructor(e){this.canvas=e.canvas,this.spriteImage=e.spriteImage,this.spriteElementsCount=Math.max(1,e.spriteElementsCount),this.spriteWidth=this.spriteImage.width,this.spriteHeight=this.spriteImage.height,this.spriteSegmentHeight=this.spriteHeight/this.spriteElementsCount;const t=this.canvas.getContext("webgl2",{alpha:!0,antialias:!1})??this.canvas.getContext("webgl",{alpha:!0,antialias:!1});if(!t)throw new Error("WebGL context is not available");this.gl=t;const i=this.createShader(this.gl.VERTEX_SHADER,et),s=this.createShader(this.gl.FRAGMENT_SHADER,st);this.program=this.createProgram(i,s),this.gl.deleteShader(i),this.gl.deleteShader(s);const r=this.gl.createBuffer();if(!r)throw new Error("Failed to create WebGL quad buffer");this.quadBuffer=r,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 n=this.gl.createTexture();if(!n)throw new Error("Failed to create WebGL texture");this.texture=n,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 o=this.gl.getAttribLocation(this.program,"a_pos");this.gl.enableVertexAttribArray(o),this.gl.vertexAttribPointer(o,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"),time:this.gl.getUniformLocation(this.program,"u_time"),borderPx:this.gl.getUniformLocation(this.program,"u_borderPx"),borderInsetPx:this.gl.getUniformLocation(this.program,"u_borderInsetPx"),cornerRadiusPx:this.gl.getUniformLocation(this.program,"u_cornerRadiusPx"),noiseAmp:this.gl.getUniformLocation(this.program,"u_noiseAmp"),pulseStrength:this.gl.getUniformLocation(this.program,"u_pulseStrength")},this.gl.uniform1i(this.uniforms.texture,0),this.gl.uniform1f(this.uniforms.time,0),this.gl.uniform1f(this.uniforms.borderPx,1),this.gl.uniform1f(this.uniforms.borderInsetPx,0),this.gl.uniform1f(this.uniforms.cornerRadiusPx,0),this.gl.uniform1f(this.uniforms.noiseAmp,0),this.gl.uniform1f(this.uniforms.pulseStrength,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(e,t){this.viewportW=Math.max(1,Math.floor(e)),this.viewportH=Math.max(1,Math.floor(t)),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(e,t,i,s,r,n=1){const o=b(e,this.spriteElementsCount)*this.spriteSegmentHeight,l=o+this.spriteSegmentHeight,u=.5,c=u/this.spriteWidth,a=1-u/this.spriteWidth,d=1-(l-u)/this.spriteHeight,f=1-(o+u)/this.spriteHeight;this.gl.uniform4f(this.uniforms.destRect,t,i,s,r),this.gl.uniform4f(this.uniforms.srcRect,c,d,a,f),this.gl.uniform4f(this.uniforms.color,1,1,1,n),this.gl.uniform1f(this.uniforms.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,1),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSolidRect(e,t,i,s,r){this.gl.uniform4f(this.uniforms.destRect,e,t,i,s),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.shapeMode,0),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}drawSoftCircle(e,t,i,s){const r=i*2;this.gl.uniform4f(this.uniforms.destRect,e-i,t-i,r,r),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,s[0],s[1],s[2],s[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)}drawElectricBorder(e){this.gl.uniform4f(this.uniforms.destRect,e.x,e.y,e.width,e.height),this.gl.uniform4f(this.uniforms.srcRect,0,0,1,1),this.gl.uniform4f(this.uniforms.color,e.rgba[0],e.rgba[1],e.rgba[2],e.rgba[3]),this.gl.uniform1f(this.uniforms.useTexture,0),this.gl.uniform1f(this.uniforms.shapeMode,2),this.gl.uniform1f(this.uniforms.time,e.timeMs*.001),this.gl.uniform1f(this.uniforms.borderPx,Math.max(.5,e.borderThicknessPx)),this.gl.uniform1f(this.uniforms.borderInsetPx,Math.max(0,e.borderInsetPx)),this.gl.uniform1f(this.uniforms.cornerRadiusPx,Math.max(0,e.cornerRadiusPx)),this.gl.uniform1f(this.uniforms.noiseAmp,Math.max(0,e.noiseAmplitudePx)),this.gl.uniform1f(this.uniforms.pulseStrength,Math.max(0,e.pulseStrength)),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(e,t){const i=this.gl.createShader(e);if(!i)throw new Error("Failed to create WebGL shader");if(this.gl.shaderSource(i,t),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS)){const s=this.gl.getShaderInfoLog(i)??"unknown error";throw this.gl.deleteShader(i),new Error(`WebGL shader compile failed: ${s}`)}return i}createProgram(e,t){const i=this.gl.createProgram();if(!i)throw new Error("Failed to create WebGL program");if(this.gl.attachShader(i,e),this.gl.attachShader(i,t),this.gl.linkProgram(i),!this.gl.getProgramParameter(i,this.gl.LINK_STATUS)){const s=this.gl.getProgramInfoLog(i)??"unknown error";throw this.gl.deleteProgram(i),new Error(`WebGL program link failed: ${s}`)}return i}},nt=class h{static RAINBOW_HUE_BUCKETS=24;static PARTICLE_GLOBAL_ALPHA=.9;static PARTICLE_MAX_DISTANCE=.72;static PARTICLE_BASE_RADIUS=.028;static DEFAULT_SYMBOL_SCALE=.9;canvas;container;button;spinQueueController;spriteSource;spriteCrossOrigin;spriteElementsCount;highlightInitialWinningCells;particleColorRgb;particleColorMode;symbolScale;motionProfile;isCoarsePointerDevice;spriteImage=null;spriteLoadError=null;webglRenderer=null;rafLoop=new z;runtime=Z();width=0;height=0;cellW=0;cellH=0;boardX=0;boardY=0;scriptedCascadeQueue=[];scriptedOutgoingGrid=null;scriptedPendingGrid=null;scriptedOutroStartedAt=0;scriptedOutroElapsedMs=0;outroMotionPlan=null;scriptedOutgoingOffsets=g();scriptedOutgoingOffsetsPrev=g();scriptedIncomingOffsets=g();scriptedIncomingOffsetsPrev=g();scriptedIncomingAlpha=g();scriptedIncomingAlphaPrev=g();scriptedIncomingVisibility=h.createVisibilityGrid("hidden");scriptedIncomingVisibilityPrev=h.createVisibilityGrid("hidden");winningCells=[];winningCellKeys=new Set;grid;particlesPerCell=34;lastRafTime=0;initialHighlightRequestedAt=0;simulationLastNow=0;simulationAccumulatorMs=0;outroInterpolationAlpha=1;isOutroPipelineWarmedUp=!1;isGpuPipelineWarmedUp=!1;perfWindowStartedAt=0;perfFrameCount=0;perfOver20MsCount=0;perfDtSamples=[];mobileDprCap=2;mobilePerfGoodWindows=0;mobilePerfBadWindows=0;static PERF_WINDOW_MS=1500;static PERF_BAD_P95_DT_MS=19;static PERF_GOOD_P95_DT_MS=17.5;static PERF_BAD_SLOW_RATIO=.03;static MOBILE_BAD_WINDOWS_TO_DECREASE_DPR=2;static MOBILE_GOOD_WINDOWS_TO_INCREASE_DPR=4;static MOBILE_DPR_CAP_MIN=1.25;static MOBILE_DPR_CAP_MAX=1.5;static MOBILE_DPR_STEP_DOWN=.1;static MOBILE_DPR_STEP_UP=.05;static DPR_QUANT_STEP=.25;static MAX_CANVAS_AREA_PX_COARSE=900*900;static MAX_CANVAS_AREA_PX_FINE=1400*1400;static PRE_SPIN_MS=150;static WIN_EFFECTS_ENVELOPE_TAU_MS=120;static MAX_FRAME_DELTA_MS=100;static WIN_BORDER_ALPHA=.72;static WIN_BORDER_INSET_RATIO=.08;static particleSeedsCache=new Map;constructor(t){this.canvas=t.canvas,this.container=t.container,this.button=t.button,this.spriteSource=t.sprite,this.spriteCrossOrigin=t.spriteCrossOrigin??"anonymous",this.spriteElementsCount=Math.max(1,t.spriteElementsCount??6),this.highlightInitialWinningCells=t.highlightInitialWinningCells!==!1,this.spinQueueController=new j(t.queuedSpinStates);const i=Y(t.particleColor);this.particleColorRgb=i.rgb,this.particleColorMode=i.mode,this.symbolScale=$(t.symbolScale,h.DEFAULT_SYMBOL_SCALE),this.motionProfile=S,this.isCoarsePointerDevice=h.detectCoarsePointerDevice(),this.mobileDprCap=this.isCoarsePointerDevice?h.MOBILE_DPR_CAP_MAX:2,this.grid=t.initialSegments?K(t.initialSegments,this.spriteElementsCount):L(this.spriteElementsCount)}async init(){if(this.bindEvents(),this.resize(),await this.loadSpriteIfProvided(),!this.spriteImage){const t=this.spriteLoadError?` (${this.spriteLoadError})`:"";throw new Error(`sprite is required for WebGL renderer${t}`)}this.webglRenderer=new rt({canvas:this.canvas,spriteImage:this.spriteImage,spriteElementsCount:this.spriteElementsCount}),this.webglRenderer.resize(this.width,this.height),this.warmUpOutroPipeline(),this.warmUpGpuPipeline(),this.applyInitialHighlightIfNeeded(),requestAnimationFrame(t=>{this.render(t),this.startLoop()})}destroy(){this.unbindEvents(),this.rafLoop.stop(),this.simulationLastNow=0,this.simulationAccumulatorMs=0,it(this.runtime),this.webglRenderer?.dispose(),this.webglRenderer=null,this.clearWinningCells()}spin(){if(this.dismissHighlightIfActive(),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.simulationLastNow=0,this.simulationAccumulatorMs=0,this.button&&(this.button.disabled=!0),this.startLoop()}bindEvents(){window.addEventListener("resize",this.resize),document.addEventListener("visibilitychange",this.onVisibilityChange),this.button?.addEventListener("click",this.onSpinClick)}unbindEvents(){window.removeEventListener("resize",this.resize),document.removeEventListener("visibilitychange",this.onVisibilityChange),this.button?.removeEventListener("click",this.onSpinClick)}onVisibilityChange=()=>{this.lastRafTime=0,this.simulationLastNow=0,this.simulationAccumulatorMs=0,this.resetPerfWindow()};onSpinClick=()=>{this.runtime.isSpinning&&this.runtime.phase!=="winFlash"||this.spin()};getNextGrid(t){return t?x(t,this.spriteElementsCount):L(this.spriteElementsCount)}update(t){if(this.runtime.isSpinning){if(this.runtime.phase==="preSpin"){if(t-this.runtime.preSpinStartedAt<h.PRE_SPIN_MS)return;J(this.runtime,{activeSpinState:this.runtime.activeSpinState,shouldHighlightCurrentSpin:this.runtime.shouldHighlightCurrentSpin,startedAt:t}),this.runtime.preSpinStartedAt=0,this.scriptedCascadeQueue=(this.runtime.activeSpinState?.finaleSequenceRows?this.runtime.activeSpinState.finaleSequenceRows.map(r=>C(r)):this.runtime.activeSpinState?.finaleSequence??[]).map(r=>r.map(n=>[...n])),this.clearWinningCells();const i=this.runtime.activeSpinState?.stopRows?C(this.runtime.activeSpinState.stopRows):this.runtime.activeSpinState?.stopGrid,s=this.getNextGrid(i);this.startOutroTransition(s,t)}this.runtime.phase}}stepScriptedOutro(t){if(!this.scriptedOutgoingGrid||!this.scriptedPendingGrid){this.finishSpinWithUi();return}this.outroMotionPlan||(this.outroMotionPlan=this.createOutroMotionPlan()),this.scriptedOutroElapsedMs+=t;const{allOutgoingDone:i,allIncomingDone:s}=D({elapsedMs:this.scriptedOutroElapsedMs,scriptedOutgoingOffsets:this.scriptedOutgoingOffsets,scriptedIncomingOffsets:this.scriptedIncomingOffsets,scriptedIncomingAlpha:this.scriptedIncomingAlpha,scriptedIncomingVisibility:this.scriptedIncomingVisibility,motionPlan:this.outroMotionPlan});if(!(!i||!s)&&(this.grid=this.scriptedPendingGrid,this.scriptedOutgoingGrid=null,this.scriptedPendingGrid=null,this.outroMotionPlan=null,this.clearWinningCells(),this.resetOutroBuffers(1,"active"),!this.tryStartScriptedCascade(this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs))){if(!this.runtime.shouldHighlightCurrentSpin){this.finishSpinWithUi();return}this.setWinningCells(W(this.grid)),F(this.runtime,this.scriptedOutroStartedAt+this.scriptedOutroElapsedMs),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(x(i,this.spriteElementsCount),t),!0):!1}startOutroTransition(t,i){this.scriptedOutgoingGrid=this.grid.map(s=>[...s]),this.scriptedPendingGrid=t.map(s=>[...s]),this.outroMotionPlan=this.createOutroMotionPlan(),this.resetOutroBuffers(0,"hidden"),this.clearWinningCells(),this.runtime.phase="outro",this.scriptedOutroStartedAt=i,this.scriptedOutroElapsedMs=0,this.outroInterpolationAlpha=1}finishSpinWithUi(t=!1){const i=this.runtime.activeSpinState,s=t?void 0:i?.callback,r=this.spinQueueController.hasPending();tt(this.runtime,r,performance.now()),this.button&&(this.button.disabled=this.runtime.queueFinished),s?.()}dismissHighlightIfActive(){this.runtime.phase==="winFlash"&&this.finishSpinWithUi(!0)}applyInitialHighlightIfNeeded(){this.highlightInitialWinningCells&&(this.setWinningCells(W(this.grid)),this.initialHighlightRequestedAt=performance.now())}warmUpOutroPipeline(){if(this.isOutroPipelineWarmedUp)return;const t=this.createOutroMotionPlan(),i=g(),s=g(),r=g(),n=h.createVisibilityGrid("hidden"),o=Math.max(...t.rowStartDelays),l=2*t.columnStaggerMs,u=[0,this.motionProfile.fixedStepMs,t.incomingStartShift+this.motionProfile.fixedStepMs,t.fallMs+o+l+this.motionProfile.fixedStepMs];for(const c of u)D({elapsedMs:c,scriptedOutgoingOffsets:i,scriptedIncomingOffsets:s,scriptedIncomingAlpha:r,scriptedIncomingVisibility:n,motionPlan:t});this.isOutroPipelineWarmedUp=!0}createOutroMotionPlan(){const t=V({height:this.height,boardY:this.boardY,cellH:this.cellH,motionProfile:this.motionProfile});if(!this.isCoarsePointerDevice)return t;const i=1e3/60;return{...t,columnStaggerMs:this.quantizeMs(t.columnStaggerMs,i),incomingStartShift:this.quantizeMs(t.incomingStartShift,i),rowStartDelays:[this.quantizeMs(t.rowStartDelays[0],i),this.quantizeMs(t.rowStartDelays[1],i),this.quantizeMs(t.rowStartDelays[2],i)]}}quantizeMs(t,i){return t<=0?0:Math.max(1,Math.round(t/i))*i}warmUpGpuPipeline(){if(this.isGpuPipelineWarmedUp)return;const t=this.webglRenderer;if(!t)return;const i=Math.max(8,this.cellW*.2),s=Math.max(8,this.cellH*.2),r=this.boardX+2,n=this.boardY+2;t.beginFrame(),t.drawSprite(0,r,n,i,s,1),t.drawSolidRect(r+i+2,n,i,s,[1,1,1,.35]),t.beginAdditiveBlend(),t.drawSoftCircle(r+i*.5,n+s*.5,Math.max(2,i*.25),[1,.9,.4,.4]),t.endAdditiveBlend(),this.isGpuPipelineWarmedUp=!0}trackOutroPerf(t,i){if(this.runtime.phase!=="outro"){this.resetPerfWindow();return}this.perfWindowStartedAt<=0&&(this.perfWindowStartedAt=i),this.perfFrameCount+=1,t>20&&(this.perfOver20MsCount+=1),this.perfDtSamples.push(t),!(i-this.perfWindowStartedAt<h.PERF_WINDOW_MS)&&(this.flushPerfWindow(),this.perfWindowStartedAt=i)}resetPerfWindow(){this.perfWindowStartedAt=0,this.perfFrameCount=0,this.perfOver20MsCount=0,this.perfDtSamples.length=0}flushPerfWindow(){if(this.perfFrameCount===0)return;const t=[...this.perfDtSamples].sort((s,r)=>s-r),i=t[Math.max(0,Math.min(t.length-1,Math.floor(t.length*.95)))];this.adjustMobileDprCap(i,this.perfOver20MsCount,this.perfFrameCount),this.perfFrameCount=0,this.perfOver20MsCount=0,this.perfDtSamples.length=0}adjustMobileDprCap(t,i,s){if(!this.isCoarsePointerDevice||s<=0)return;const r=i/s,n=t>h.PERF_BAD_P95_DT_MS||r>h.PERF_BAD_SLOW_RATIO,o=t<=h.PERF_GOOD_P95_DT_MS&&i===0;let l=this.mobileDprCap;n?(this.mobilePerfBadWindows+=1,this.mobilePerfGoodWindows=0,this.mobilePerfBadWindows>=h.MOBILE_BAD_WINDOWS_TO_DECREASE_DPR&&(l=Math.max(h.MOBILE_DPR_CAP_MIN,this.mobileDprCap-h.MOBILE_DPR_STEP_DOWN),this.mobilePerfBadWindows=0)):o?(this.mobilePerfGoodWindows+=1,this.mobilePerfBadWindows=0,this.mobilePerfGoodWindows>=h.MOBILE_GOOD_WINDOWS_TO_INCREASE_DPR&&(l=Math.min(h.MOBILE_DPR_CAP_MAX,this.mobileDprCap+h.MOBILE_DPR_STEP_UP),this.mobilePerfGoodWindows=0)):(this.mobilePerfGoodWindows=0,this.mobilePerfBadWindows=0),!(Math.abs(l-this.mobileDprCap)<.001)&&(this.mobileDprCap=l,this.resize())}render(t){if(!this.webglRenderer)return;this.initialHighlightRequestedAt>0&&t-this.initialHighlightRequestedAt>=200&&(F(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 l=Math.min(t-this.lastRafTime,50);this.trackOutroPerf(l,t);const u=1-Math.exp(-l/h.WIN_EFFECTS_ENVELOPE_TAU_MS);this.runtime.winEffectsEnvelope+=(i-this.runtime.winEffectsEnvelope)*u}else this.resetPerfWindow();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.drawGridInterpolated(this.scriptedOutgoingGrid,this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets,this.outroInterpolationAlpha,s),this.drawGridInterpolated(this.scriptedPendingGrid,this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets,this.outroInterpolationAlpha,s,this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha,this.scriptedIncomingVisibility)):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(U[t]??0)*this.cellH}applyPixelSnapY(t){return this.isCoarsePointerDevice?Math.round(t*2)/2:t}drawGrid(t,i,s){this.drawGridWithSampler({grid:t,skipWinningCells:s,sampleOffsetY:(r,n)=>i?i[r][n]:0,sampleAlpha:()=>1,isVisible:()=>!0})}drawGridInterpolated(t,i,s,r,n,o,l,u){this.drawGridWithSampler({grid:t,skipWinningCells:n,sampleOffsetY:(c,a)=>i[c][a]+(s[c][a]-i[c][a])*r,sampleAlpha:(c,a)=>o&&l?o[c][a]+(l[c][a]-o[c][a])*r:1,isVisible:(c,a)=>!u||u[c][a]!=="hidden"})}drawGridWithSampler(t){const i=this.webglRenderer;if(i)for(let s=0;s<3;s+=1){const r=this.boardX+s*this.cellW;for(let n=0;n<3;n+=1){if(t.skipWinningCells&&this.isWinningCell(s,n)||!t.isVisible(s,n))continue;const o=t.sampleOffsetY(s,n),l=this.applyPixelSnapY(this.boardY+n*this.cellH+o+this.getRowCompactOffset(n));if(l>this.height||l+this.cellH<0)continue;const u=t.sampleAlpha(s,n);if(u<=0)continue;const c=this.cellW*this.symbolScale,a=this.cellH*this.symbolScale,d=(this.cellW-c)*.5,f=(this.cellH-a)*.5;i.drawSprite(t.grid[s][n],r+d,l+f,c,a,u)}}}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%v/v,o=1+Math.sin(n*Math.PI*2)*N*s,l=Math.max(1,this.cellW*h.WIN_BORDER_INSET_RATIO),u=Math.max(1,this.cellW*.022),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,f=this.boardY+a.row*this.cellH+this.getRowCompactOffset(a.row),p=this.grid[a.col][a.row],E=h.WIN_BORDER_ALPHA*s,w=this.particleColorMode==="rainbow"?h.hslToRgb01((r*.2+a.col*36+a.row*22)%360,.96,.64):[this.particleColorRgb[0]/255,this.particleColorRgb[1]/255,this.particleColorRgb[2]/255],_=this.cellW*this.symbolScale*o,P=this.cellH*this.symbolScale*o,A=(this.cellW-_)*.5,B=(this.cellH-P)*.5;i.drawSprite(p,d+A,f+B,_,P,1);const G=d+l,H=f+l,O=this.cellW-l*2,I=this.cellH-l*2;O<=u*2||I<=u*2||(this.drawElectricBorder({renderer:i,cell:a,x:G,y:H,w:O,h:I,borderThickness:u,borderColor:w,alpha:E,elapsed:r,envelope:s}),c&&this.drawCellParticleBurst({renderer:i,cell:a,centerX:d+this.cellW*.5,centerY:f+this.cellH*.5,elapsed:r,envelope:s}))}}drawCellParticleBurst(t){const i=Math.min(this.cellW,this.cellH)*h.PARTICLE_MAX_DISTANCE,s=Math.min(this.cellW,this.cellH)*h.PARTICLE_BASE_RADIUS,r=h.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 l=r[o],u=l.phaseOffset*720,c=t.elapsed-u;if(c<0)continue;const a=c%720/720,d=l.seedA*Math.PI*2,f=i*a*(.35+l.seedB*.65),p=t.centerX+Math.cos(d)*f,E=t.centerY+Math.sin(d)*f,w=.7+.9*Math.max(0,Math.sin((t.elapsed*.012+l.twinkleSeed*2)*Math.PI*2)),_=Math.max(1,s*(.55+l.seedC*.6)*(1-a*.5)),P=Math.max(0,Math.min(1,(.9+w*.2)*h.PARTICLE_GLOBAL_ALPHA*t.envelope));if(P<=0)continue;const A=this.particleColorMode==="rainbow"?h.getRainbowParticleColor(t.elapsed,t.cell.col,t.cell.row,l.seedA):n;t.renderer.drawSoftCircle(p,E,_,[A[0],A[1],A[2],P])}t.renderer.endAdditiveBlend()}drawElectricBorder(t){const i=Math.max(0,Math.min(1,t.alpha*t.envelope)),s=t.borderThickness*1.8,r=t.elapsed+t.cell.col*170+t.cell.row*290;t.renderer.beginAdditiveBlend(),t.renderer.drawElectricBorder({x:t.x-s,y:t.y-s,width:t.w+s*2,height:t.h+s*2,rgba:[t.borderColor[0],t.borderColor[1],t.borderColor[2],i*.55],timeMs:r,borderThicknessPx:Math.max(.8,t.borderThickness*1.1),borderInsetPx:s*.85,cornerRadiusPx:Math.max(2,t.borderThickness*2.5),noiseAmplitudePx:Math.max(.15,t.borderThickness*.6),pulseStrength:.9}),t.renderer.drawElectricBorder({x:t.x-s*.5,y:t.y-s*.5,width:t.w+s,height:t.h+s,rgba:[t.borderColor[0],t.borderColor[1],t.borderColor[2],i*.9],timeMs:r*1.03,borderThicknessPx:Math.max(.7,t.borderThickness*.8),borderInsetPx:s*.5,cornerRadiusPx:Math.max(1.5,t.borderThickness*1.7),noiseAmplitudePx:Math.max(.12,t.borderThickness*.42),pulseStrength:1.25}),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)),l=(1-Math.abs(2*o-1))*n,u=r/60,c=l*(1-Math.abs(u%2-1));let a=0,d=0,f=0;u>=0&&u<1?(a=l,d=c):u<2?(a=c,d=l):u<3?(d=l,f=c):u<4?(d=c,f=l):u<5?(a=c,f=l):(a=l,f=c);const p=o-l*.5;return[a+p,d+p,f+p]}static getRainbowParticleColor(t,i,s,r){const n=(r*360+t*.24+i*38+s*22)%360,o=360/h.RAINBOW_HUE_BUCKETS,l=Math.floor(n/o)*o;return h.hslToRgb01(l,.98,.64)}static detectCoarsePointerDevice(){if(typeof window>"u")return!1;const t=typeof window.matchMedia=="function"&&window.matchMedia("(pointer: coarse)").matches,i=typeof navigator<"u"&&typeof navigator.maxTouchPoints=="number"?navigator.maxTouchPoints:0;return t||i>0}quantizeDprCap(t){const i=h.DPR_QUANT_STEP;return Math.max(1,Math.round(t/i)*i)}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=h.particleSeedsCache.get(s);if(r)return r;const n=[];for(let o=0;o<34;o+=1)n.push({seedA:h.hash01(t,i,o,1),seedB:h.hash01(t,i,o,2),seedC:h.hash01(t,i,o,3),phaseOffset:h.hash01(t,i,o,4),twinkleSeed:h.hash01(t,i,o,5)});return h.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(),i=Math.max(300,Math.floor(t.width)),s=Math.max(1,i*i),r=this.isCoarsePointerDevice?h.MAX_CANVAS_AREA_PX_COARSE:h.MAX_CANVAS_AREA_PX_FINE,n=Math.max(1,Math.sqrt(r/s)),o=this.isCoarsePointerDevice?this.mobileDprCap:2,l=this.quantizeDprCap(Math.min(o,n)),u=Math.max(1,Math.min(window.devicePixelRatio||1,l)),c=Math.max(300,Math.floor(i*u));this.width=c,this.height=c;const a=Math.floor(Math.min(this.width/3,this.height/3));this.cellW=a,this.cellH=a,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.spriteSource){this.spriteImage=null,this.spriteLoadError=null;return}if(typeof this.spriteSource!="string"){try{await this.decodeSpriteImage(this.spriteSource),this.spriteImage=this.spriteSource,this.spriteLoadError=null}catch(i){this.spriteImage=null,this.spriteLoadError=this.toSpriteLoadErrorMessage(i,"[HTMLImageElement]")}return}const t=new Image;t.decoding="async",t.crossOrigin=this.spriteCrossOrigin,t.src=this.spriteSource;try{await this.decodeSpriteImage(t),this.spriteImage=t,this.spriteLoadError=null}catch(i){this.spriteImage=null,this.spriteLoadError=this.toSpriteLoadErrorMessage(i,this.spriteSource)}}async decodeSpriteImage(t){t.complete&&t.naturalWidth>0&&t.naturalHeight>0||await t.decode()}toSpriteLoadErrorMessage(t,i){return`failed to load sprite "${i}" - ${t instanceof Error?`${t.name}: ${t.message}`:String(t)}`}advanceSimulation(t){if(this.simulationLastNow<=0){this.simulationLastNow=t,this.update(t);return}const i=Math.max(0,Math.min(t-this.simulationLastNow,h.MAX_FRAME_DELTA_MS));if(this.simulationLastNow=t,this.update(t),this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.advanceOutroFixedStep(i)}advanceOutroFixedStep(t){this.simulationAccumulatorMs+=t;const i=this.motionProfile.fixedStepMs;let s=0;for(;this.simulationAccumulatorMs>=i&&s<this.motionProfile.maxCatchUpStepsPerFrame&&this.runtime.phase==="outro";)this.snapshotOutroState(),this.stepScriptedOutro(i),this.simulationAccumulatorMs-=i,s+=1;if(this.runtime.phase!=="outro"){this.outroInterpolationAlpha=1;return}this.outroInterpolationAlpha=Math.max(0,Math.min(1,this.simulationAccumulatorMs/i))}snapshotOutroState(){this.copyOffsets(this.scriptedOutgoingOffsetsPrev,this.scriptedOutgoingOffsets),this.copyOffsets(this.scriptedIncomingOffsetsPrev,this.scriptedIncomingOffsets),this.copyOffsets(this.scriptedIncomingAlphaPrev,this.scriptedIncomingAlpha),this.copyVisibility(this.scriptedIncomingVisibilityPrev,this.scriptedIncomingVisibility)}copyOffsets(t,i){for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1)t[s][r]=i[s][r]}copyVisibility(t,i){for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1)t[s][r]=i[s][r]}resetOutroBuffers(t,i){m(this.scriptedOutgoingOffsets,0),m(this.scriptedIncomingOffsets,0),m(this.scriptedOutgoingOffsetsPrev,0),m(this.scriptedIncomingOffsetsPrev,0),m(this.scriptedIncomingAlpha,t),m(this.scriptedIncomingAlphaPrev,t),this.fillVisibilityGrid(this.scriptedIncomingVisibility,i),this.fillVisibilityGrid(this.scriptedIncomingVisibilityPrev,i)}static createVisibilityGrid(t){return Array.from({length:3},()=>Array.from({length:3},()=>t))}fillVisibilityGrid(t,i){for(let s=0;s<3;s+=1)for(let r=0;r<3;r+=1)t[s][r]=i}startLoop(){this.rafLoop.isRunning()||this.rafLoop.start(t=>(this.advanceSimulation(t),this.render(t),this.shouldKeepAnimating()))}shouldKeepAnimating(){return this.runtime.isSpinning||this.initialHighlightRequestedAt>0?!0:this.runtime.winEffectsEnvelope>.001}};exports.CascadingReel=nt;
|
|
130
|
-
|
|
131
|
-
//# sourceMappingURL=index.cjs.js.map
|