reel-deal 0.1.0-dev.0 → 0.1.0-dev.1
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 +137 -0
- package/dist/index.cjs.js +89 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +141 -0
- package/dist/index.es.js +543 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +89 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +38 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/defaults.ts","../src/utils/math.ts","../src/normalize.ts","../src/utils/dom.ts","../src/ReelDeal.ts"],"sourcesContent":["import type {\n ReelDealIdleBob,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealWebGLShading,\n} from './types';\n\nexport const defaultSpinConfig: ReelDealSpinConfig = {\n minSpins: 2,\n durationMs: 4000,\n speedPxS: 4000,\n staggerMs: 300,\n};\n\nexport const defaultWebGLShading: ReelDealWebGLShading = {\n warpAngleDeg: 90,\n edgePower: 0.5,\n};\n\nexport const defaultSpinBlur: ReelDealSpinBlur = {\n maxPx: 4,\n speedAtMax: 2200,\n};\n\nexport const defaultIdleBob: ReelDealIdleBob = {\n amplitudePx: 8,\n speedHz: 0.25,\n phaseOffsetRad: 0.9,\n delayMs: 500,\n rampMs: 800,\n};\n","const SPIN_ACCEL_END = 0.1;\nconst SPIN_DECEL_START = 0.6;\n\nexport const clampInt = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, Math.floor(value)));\n\nexport const mod = (n: number, m: number): number => ((n % m) + m) % m;\n\nexport const clamp01 = (t: number): number => Math.max(0, Math.min(1, t));\n\nexport const rampHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x ** 2 * (2 - x);\n};\n\nexport const brakeHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x + x ** 2 - x ** 3;\n};\n\nexport const reelProgress = (t: number): number => {\n const tt = clamp01(t);\n\n if (tt <= SPIN_ACCEL_END) {\n const u = tt / SPIN_ACCEL_END;\n\n return rampHermite(u) * SPIN_ACCEL_END;\n }\n\n if (tt < SPIN_DECEL_START) {\n return tt;\n }\n\n const u = (tt - SPIN_DECEL_START) / (1 - SPIN_DECEL_START);\n\n return SPIN_DECEL_START + brakeHermite(u) * (1 - SPIN_DECEL_START);\n};\n\nexport const finiteOr = (value: number | undefined, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n\nexport const nonNegative = (value: number | undefined, fallback: number): number =>\n Math.max(0, finiteOr(value, fallback));\n\nexport const nonNegativeInt = (value: number | undefined, fallback: number): number =>\n Math.max(0, Math.floor(finiteOr(value, fallback)));\n","import {\n defaultIdleBob,\n defaultSpinBlur,\n defaultSpinConfig,\n defaultWebGLShading,\n} from './defaults';\nimport type {\n ReelDealIdleBob,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealWebGLShading,\n} from './types';\nimport { finiteOr, nonNegative, nonNegativeInt } from './utils/math';\n\nconst DEFAULT_MAX_DPR = 2;\n\nexport type NormalizedSpinConfig = Required<ReelDealSpinConfig>;\n\nexport const normalizeSpinConfig = (\n overrides?: Partial<ReelDealSpinConfig>,\n): NormalizedSpinConfig => {\n const base = { ...defaultSpinConfig, ...(overrides ?? {}) };\n\n return {\n minSpins: nonNegativeInt(base.minSpins, defaultSpinConfig.minSpins),\n durationMs: nonNegativeInt(base.durationMs, defaultSpinConfig.durationMs ?? 0),\n speedPxS: nonNegativeInt(base.speedPxS, defaultSpinConfig.speedPxS ?? 0),\n staggerMs: nonNegativeInt(base.staggerMs, defaultSpinConfig.staggerMs ?? 0),\n };\n};\n\nexport const normalizeSpinBlur = (overrides?: Partial<ReelDealSpinBlur>): ReelDealSpinBlur => {\n const base = { ...defaultSpinBlur, ...(overrides ?? {}) };\n\n return {\n maxPx: nonNegative(base.maxPx, defaultSpinBlur.maxPx),\n speedAtMax: nonNegative(base.speedAtMax, defaultSpinBlur.speedAtMax),\n };\n};\n\nexport const normalizeWebGLShading = (\n overrides?: Partial<ReelDealWebGLShading>,\n): ReelDealWebGLShading => {\n const base = { ...defaultWebGLShading, ...(overrides ?? {}) };\n\n return {\n warpAngleDeg: nonNegative(base.warpAngleDeg, defaultWebGLShading.warpAngleDeg),\n edgePower: nonNegative(base.edgePower, defaultWebGLShading.edgePower),\n };\n};\n\nexport const normalizeIdleBob = (\n overrides?: Partial<ReelDealIdleBob> | false,\n): ReelDealIdleBob | null => {\n if (overrides === false) return null;\n\n const base = { ...defaultIdleBob, ...(overrides ?? {}) };\n\n return {\n amplitudePx: nonNegative(base.amplitudePx, defaultIdleBob.amplitudePx),\n speedHz: nonNegative(base.speedHz, defaultIdleBob.speedHz),\n phaseOffsetRad: finiteOr(base.phaseOffsetRad, defaultIdleBob.phaseOffsetRad),\n delayMs: nonNegativeInt(base.delayMs, defaultIdleBob.delayMs),\n rampMs: nonNegativeInt(base.rampMs, defaultIdleBob.rampMs),\n };\n};\n\nexport const normalizeMaxDpr = (value?: number): number =>\n Math.max(1, finiteOr(value, DEFAULT_MAX_DPR));\n","export const resolveElementById = <T extends HTMLElement>(id: string, kind: string): T => {\n const el = document.getElementById(id);\n\n if (!el) {\n throw new Error(`${kind} not found: ${id}`);\n }\n\n return el as T;\n};\n\nexport const resolveElement = <T extends HTMLElement>(elOrId: T | string, kind: string): T => {\n if (typeof elOrId === 'string') return resolveElementById<T>(elOrId, kind);\n\n return elOrId;\n};\n","import {\n type NormalizedSpinConfig,\n normalizeIdleBob,\n normalizeMaxDpr,\n normalizeSpinBlur,\n normalizeSpinConfig,\n normalizeWebGLShading,\n} from './normalize';\nimport type {\n ReelDealIdleBob,\n ReelDealOptions,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealSpinState,\n ReelDealWebGLShading,\n} from './types';\nimport { resolveElement } from './utils/dom';\nimport { clamp01, clampInt, mod, rampHermite, reelProgress } from './utils/math';\n\nconst MAX_REELS = 5;\nconst SPIN_TICK_START = 0.72;\nconst SPIN_TICK_RANGE = 0.28;\nconst SPIN_TICK_MAX_PX = 7;\nconst VELOCITY_DECAY = 0.85;\nconst VELOCITY_GAIN = 1 - VELOCITY_DECAY;\n\ntype SpinAnimation = {\n startTime: number;\n startOffsets: number[];\n deltas: number[];\n durationMs: number[];\n};\n\nexport class ReelDeal {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n\n private readonly opts: ReelDealOptions;\n private readonly visibleSlots = 3;\n private readonly heightScale = 2 / 3;\n private readonly centerIndex = 1;\n private readonly reels: number;\n private readonly spinConfig: NormalizedSpinConfig;\n private readonly spinBlur: ReelDealSpinBlur;\n private readonly webglShading: ReelDealWebGLShading;\n private readonly idleBob: ReelDealIdleBob | null;\n private readonly maxDpr: number;\n\n private spriteImg: HTMLImageElement | null = null;\n private spriteWidth = 0;\n private spriteHeight = 0;\n private frameHeight = 0;\n\n private width = 0;\n private height = 0;\n private dpr = 1;\n\n private offsets: number[];\n private velocities: number[];\n private lastVelT: number | null = null;\n private lastVelOffsets: number[];\n private idleStartTime: number | null = null;\n private idleBaseOffsets: number[];\n\n private gl: WebGLRenderingContext | WebGL2RenderingContext | null = null;\n private glProgram: WebGLProgram | null = null;\n private glBuffer: WebGLBuffer | null = null;\n private glTexture: WebGLTexture | null = null;\n private glAttribs: { pos: number; uv: number } | null = null;\n private webglInitError: string | null = null;\n private glUniforms: {\n texture: WebGLUniformLocation | null;\n reelX: WebGLUniformLocation | null;\n reelW: WebGLUniformLocation | null;\n offsetPx: WebGLUniformLocation | null;\n frameHeight: WebGLUniformLocation | null;\n spriteHeight: WebGLUniformLocation | null;\n visibleSlots: WebGLUniformLocation | null;\n heightScale: WebGLUniformLocation | null;\n edgePower: WebGLUniformLocation | null;\n warpAngle: WebGLUniformLocation | null;\n blurPx: WebGLUniformLocation | null;\n } | null = null;\n\n private anim: SpinAnimation | null = null;\n private rafId: number | null = null;\n private pendingSpinResolvers: Array<() => void> = [];\n\n private resizeObserver: ResizeObserver | null = null;\n private resizeQueued = false;\n\n private button: HTMLButtonElement | null = null;\n private buttonHandlerAttached = false;\n private spinning = false;\n private queuedSpins: ReelDealSpinState[] | null = null;\n\n constructor(options: ReelDealOptions) {\n this.opts = options;\n this.reels = clampInt(options.reels ?? 1, 1, MAX_REELS);\n this.spinConfig = normalizeSpinConfig(options.spinConfig);\n this.spinBlur = normalizeSpinBlur(options.spinBlur);\n this.webglShading = normalizeWebGLShading(options.webglShading);\n this.idleBob = normalizeIdleBob(options.idleBob);\n this.maxDpr = normalizeMaxDpr(options.maxDpr);\n\n this.canvas = resolveElement<HTMLCanvasElement>(options.canvas, 'Canvas');\n this.container = resolveElement<HTMLElement>(options.container, 'Container');\n\n this.offsets = new Array(this.reels).fill(0);\n this.velocities = new Array(this.reels).fill(0);\n this.lastVelOffsets = new Array(this.reels).fill(0);\n this.idleBaseOffsets = new Array(this.reels).fill(0);\n }\n\n async init(): Promise<void> {\n await this.loadSprite();\n\n this.tryInitWebGL();\n\n if (!this.gl) {\n throw new Error(\n `WebGL is not supported or failed to initialize. ${this.webglInitError ?? ''}`.trim(),\n );\n }\n\n this.syncContainerLayoutVars();\n this.ensureCanvasCoversContainer();\n this.setupResizeWatcher();\n this.resizeToContainer();\n\n this.setSegments(this.opts.initialSegments);\n\n this.button = this.resolveButton();\n this.queuedSpins = this.resolveSpinQueue();\n this.attachButtonHandler();\n\n this.render();\n this.startLoop();\n }\n\n destroy(): void {\n this.stop();\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.button && this.buttonHandlerAttached) {\n this.button.removeEventListener('click', this.handleButtonClick);\n this.buttonHandlerAttached = false;\n }\n\n this.releaseWebGLResources();\n }\n\n private ensureCanvasCoversContainer(): void {\n if (this.canvas === this.container) return;\n if (!this.container.contains(this.canvas)) return;\n\n if (!this.canvas.style.width) this.canvas.style.width = '100%';\n if (!this.canvas.style.height) this.canvas.style.height = '100%';\n if (!this.canvas.style.display) this.canvas.style.display = 'block';\n }\n\n private syncContainerLayoutVars(): void {\n const target = this.canvas === this.container ? this.canvas : this.container;\n\n target.style.setProperty('--reels', String(this.reels));\n target.style.setProperty('--visible-slots', String(this.visibleSlots));\n target.style.aspectRatio = `${this.reels} / ${this.visibleSlots * this.heightScale}`;\n\n if (this.canvas !== this.container) {\n this.container.style.overflow = 'hidden';\n }\n }\n\n setSegments(initialSegments?: number[]): void {\n this.ensureReady();\n\n const segments = initialSegments ?? [];\n\n for (let r = 0; r < this.reels; r += 1) {\n const segIndex = segments[r] ?? 0;\n const idx = clampInt(segIndex, 0, this.opts.slotCount - 1);\n\n this.offsets[r] = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = null;\n this.idleStartTime = null;\n this.anim = null;\n\n this.stop();\n this.render();\n this.startLoop();\n }\n\n setSegment(segmentIndex: number): void {\n this.setSegments(new Array(this.reels).fill(segmentIndex));\n }\n\n spinOnce(stopAtSegments: number[], config?: Partial<ReelDealSpinConfig>): Promise<void> {\n this.ensureReady();\n\n const { minSpins, staggerMs, speedPxS, durationMs } = this.getSpinConfig(config);\n const useSpeed = speedPxS > 0;\n\n const startOffsets = new Array(this.reels);\n const deltas = new Array(this.reels);\n const durations = new Array(this.reels);\n\n for (let r = 0; r < this.reels; r += 1) {\n const idx = clampInt(stopAtSegments[r] ?? 0, 0, this.opts.slotCount - 1);\n const desiredTop = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n const desired = mod(desiredTop, this.spriteHeight);\n const current = mod(this.offsets[r], this.spriteHeight);\n\n const alignDelta = mod(current - desired, this.spriteHeight);\n\n let delta = -(minSpins * this.spriteHeight + alignDelta);\n\n if (useSpeed && durationMs > 0) {\n const desiredDistance = (speedPxS * durationMs) / 1000;\n const loops = Math.max(\n minSpins,\n Math.ceil(Math.max(0, desiredDistance - alignDelta) / this.spriteHeight),\n );\n\n delta = -(alignDelta + loops * this.spriteHeight);\n }\n\n if (staggerMs > 0 && useSpeed) {\n const extraDelta = (speedPxS * staggerMs * r) / 1000;\n const extraLoops = Math.ceil(extraDelta / this.spriteHeight);\n\n delta -= extraLoops * this.spriteHeight;\n }\n\n startOffsets[r] = this.offsets[r];\n deltas[r] = delta;\n\n if (useSpeed) {\n durations[r] = Math.max(0, Math.round((Math.abs(delta) / speedPxS) * 1000));\n } else {\n durations[r] = Math.max(0, durationMs + staggerMs * r);\n }\n }\n\n const totalDelta = deltas.reduce((acc, v) => acc + Math.abs(v), 0);\n\n if (totalDelta === 0 || durations.every((entry) => entry <= 0)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.offsets[r] + deltas[r];\n }\n\n this.anim = null;\n this.resolvePendingSpins();\n this.render();\n\n return Promise.resolve();\n }\n\n const startTime = performance.now();\n\n this.anim = { startTime, startOffsets, deltas, durationMs: durations };\n this.idleStartTime = null;\n this.startLoop();\n\n return new Promise((resolve) => {\n this.pendingSpinResolvers.push(resolve);\n });\n }\n\n private getSpinConfig(overrides?: Partial<ReelDealSpinConfig>): NormalizedSpinConfig {\n if (!overrides) return this.spinConfig;\n\n return normalizeSpinConfig({ ...this.spinConfig, ...overrides });\n }\n\n async spinQueue(spins: ReelDealSpinState[]): Promise<void> {\n for (let i = 0; i < spins.length; i += 1) {\n const spin = spins[i];\n\n await this.spinOnce(spin.stopAtSegments);\n\n spin.callback?.(i, spin.stopAtSegments);\n }\n }\n\n stop(): void {\n if (this.rafId !== null) cancelAnimationFrame(this.rafId);\n\n this.rafId = null;\n this.anim = null;\n this.resolvePendingSpins();\n this.lastVelT = null;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n }\n\n private resolvePendingSpins(): void {\n if (this.pendingSpinResolvers.length === 0) return;\n\n const resolvers = this.pendingSpinResolvers;\n\n this.pendingSpinResolvers = [];\n\n for (const resolve of resolvers) {\n resolve();\n }\n }\n\n requestResize(): void {\n this.queueResize();\n }\n\n private ensureReady(): void {\n if (\n !this.spriteImg ||\n this.spriteWidth <= 0 ||\n this.spriteHeight <= 0 ||\n this.frameHeight <= 0\n ) {\n throw new Error('Sprite is not ready. Call init() and await it first.');\n }\n }\n\n private attachButtonHandler(): void {\n if (!this.button || this.buttonHandlerAttached) return;\n\n this.button.addEventListener('click', this.handleButtonClick, { passive: true });\n this.buttonHandlerAttached = true;\n }\n\n private handleButtonClick = async (): Promise<void> => {\n if (this.spinning) return;\n\n const spin = this.consumeNextSpin() ?? {\n stopAtSegments: this.buildRandomSegments(),\n };\n\n this.spinning = true;\n this.setButtonDisabled(true);\n\n try {\n await this.spinOnce(spin.stopAtSegments);\n } finally {\n if (this.queuedSpins && this.queuedSpins.length === 0) {\n this.setButtonDisabled(true);\n } else {\n this.setButtonDisabled(false);\n }\n\n this.spinning = false;\n }\n };\n\n private resolveButton(): HTMLButtonElement | null {\n if (!this.opts.button) return null;\n\n return resolveElement<HTMLButtonElement>(this.opts.button, 'Button');\n }\n\n private resolveSpinQueue(): ReelDealSpinState[] | null {\n return this.opts.queuedSpinStates ?? null;\n }\n\n private consumeNextSpin(): ReelDealSpinState | null {\n if (!this.queuedSpins) {\n this.queuedSpins = this.resolveSpinQueue();\n }\n\n if (!this.queuedSpins || this.queuedSpins.length === 0) return null;\n\n return this.queuedSpins.shift() ?? null;\n }\n\n private buildRandomSegments(): number[] {\n const max = Math.max(1, Math.floor(this.opts.slotCount));\n const segments: number[] = [];\n\n for (let r = 0; r < this.reels; r += 1) {\n segments.push(Math.floor(Math.random() * max));\n }\n\n return segments;\n }\n\n private setButtonDisabled(disabled: boolean): void {\n if (!this.button) return;\n\n this.button.disabled = disabled;\n\n if (disabled) this.button.setAttribute('aria-busy', 'true');\n else this.button.removeAttribute('aria-busy');\n }\n\n private shouldAnimate(): boolean {\n return this.anim !== null || this.idleBob !== null;\n }\n\n private startLoop(): void {\n if (this.rafId !== null) return;\n if (!this.shouldAnimate()) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n }\n\n private loop = (now: number): void => {\n if (this.anim) {\n const { startTime, durationMs, startOffsets, deltas } = this.anim;\n\n let allDone = true;\n\n for (let r = 0; r < this.reels; r += 1) {\n const duration = durationMs[r] ?? 0;\n const t = duration <= 0 ? 1 : Math.min(1, (now - startTime) / duration);\n const eased = reelProgress(t);\n\n if (t < 1) allDone = false;\n\n const baseOffset = startOffsets[r] + deltas[r] * eased;\n\n let tick = 0;\n\n if (t > SPIN_TICK_START && this.frameHeight > 0) {\n const u = clamp01((t - SPIN_TICK_START) / SPIN_TICK_RANGE);\n const env = (1 - u) ** 2;\n const phase = (baseOffset / this.frameHeight) * Math.PI * 2;\n const amp = Math.min(SPIN_TICK_MAX_PX, this.frameHeight * 0.06);\n\n tick = Math.sin(phase) * env * amp;\n }\n\n this.offsets[r] = baseOffset + tick;\n }\n\n this.updateVelocity(now);\n this.render();\n\n if (allDone) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.anim.startOffsets[r] + this.anim.deltas[r];\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.updateVelocity(now);\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n\n this.anim = null;\n this.idleStartTime = null;\n\n this.render();\n this.resolvePendingSpins();\n }\n } else if (this.idleBob) {\n this.updateIdle(now);\n this.render();\n }\n\n if (!this.shouldAnimate()) {\n this.rafId = null;\n\n return;\n }\n\n if (this.rafId === null) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n private render(): void {\n if (!this.spriteImg) return;\n if (this.width <= 0 || this.height <= 0) return;\n if (!this.gl) return;\n\n this.renderWebGL();\n }\n\n private getSpinBlurPxForReel(reel: number): number {\n if (!this.anim) return 0;\n\n const speed = Math.abs(this.velocities[reel] ?? 0);\n const speedAtMax = Math.max(1, this.spinBlur.speedAtMax);\n const t = clamp01(speed / speedAtMax);\n\n return t * Math.max(0, this.spinBlur.maxPx);\n }\n\n private renderWebGL(): void {\n if (\n !this.gl ||\n !this.glProgram ||\n !this.glAttribs ||\n !this.glUniforms ||\n !this.glTexture ||\n !this.glBuffer\n ) {\n return;\n }\n\n if (!this.spriteImg) return;\n\n const gl = this.gl;\n\n gl.useProgram(this.glProgram);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffer);\n\n gl.enableVertexAttribArray(this.glAttribs.pos);\n gl.vertexAttribPointer(this.glAttribs.pos, 2, gl.FLOAT, false, 16, 0);\n gl.enableVertexAttribArray(this.glAttribs.uv);\n gl.vertexAttribPointer(this.glAttribs.uv, 2, gl.FLOAT, false, 16, 8);\n\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.glTexture);\n gl.uniform1i(this.glUniforms.texture, 0);\n\n gl.uniform1f(this.glUniforms.frameHeight, this.frameHeight);\n gl.uniform1f(this.glUniforms.spriteHeight, this.spriteHeight);\n gl.uniform1f(this.glUniforms.visibleSlots, this.visibleSlots);\n gl.uniform1f(this.glUniforms.heightScale, this.heightScale);\n gl.uniform1f(this.glUniforms.edgePower, Math.max(0.5, this.webglShading.edgePower));\n\n const shading = this.webglShading;\n const warpAngle = (Math.max(0, shading.warpAngleDeg) * Math.PI) / 180;\n\n gl.uniform1f(this.glUniforms.warpAngle, warpAngle);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const reelW = 1 / this.reels;\n\n for (let r = 0; r < this.reels; r += 1) {\n gl.uniform1f(this.glUniforms.reelX, r * reelW);\n gl.uniform1f(this.glUniforms.reelW, reelW);\n\n const offsetPx = mod(this.offsets[r], this.spriteHeight);\n\n gl.uniform1f(this.glUniforms.offsetPx, offsetPx);\n gl.uniform1f(this.glUniforms.blurPx, this.getSpinBlurPxForReel(r));\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n }\n\n private updateVelocity(now: number): void {\n if (this.lastVelT === null) {\n this.lastVelT = now;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n }\n\n return;\n }\n\n const dt = (now - this.lastVelT) / 1000;\n\n if (dt <= 0) return;\n\n for (let r = 0; r < this.reels; r += 1) {\n const v = (this.offsets[r] - this.lastVelOffsets[r]) / dt;\n\n this.velocities[r] = this.velocities[r] * VELOCITY_DECAY + v * VELOCITY_GAIN;\n this.lastVelOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = now;\n }\n\n private updateIdle(now: number): void {\n if (!this.idleBob) return;\n if (this.idleStartTime === null) this.idleStartTime = now;\n\n const { amplitudePx, speedHz, phaseOffsetRad, delayMs, rampMs } = this.idleBob;\n const elapsedMs = now - this.idleStartTime;\n\n if (elapsedMs < Math.max(0, delayMs)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.idleBaseOffsets[r];\n this.velocities[r] = 0;\n }\n\n return;\n }\n\n const tSec = (elapsedMs - Math.max(0, delayMs)) / 1000;\n const rampT = Math.max(\n 0,\n Math.min(1, (elapsedMs - Math.max(0, delayMs)) / Math.max(1, rampMs)),\n );\n const ramp = rampHermite(rampT);\n const omega = speedHz * Math.PI * 2;\n\n for (let r = 0; r < this.reels; r += 1) {\n const phase = tSec * omega + r * phaseOffsetRad;\n\n this.offsets[r] = this.idleBaseOffsets[r] + Math.sin(phase) * (amplitudePx * ramp);\n this.velocities[r] = 0;\n }\n }\n\n private async loadSprite(): Promise<void> {\n const { sprite } = this.opts;\n const img = typeof sprite === 'string' ? new Image() : sprite;\n\n if (typeof sprite === 'string') {\n img.src = sprite;\n }\n\n if (!img.complete || img.naturalWidth <= 0) {\n await new Promise<void>((resolve, reject) => {\n const onLoad = () => resolve();\n const onError = () => reject(new Error('Failed to load sprite image'));\n\n img.addEventListener('load', onLoad, { once: true });\n img.addEventListener('error', onError, { once: true });\n });\n }\n\n const slotCount = Math.max(1, Math.floor(this.opts.slotCount));\n const spriteWidth = img.naturalWidth;\n const frameHeight = spriteWidth;\n const expectedSpriteHeight = frameHeight * slotCount;\n\n const naturalHeight = img.naturalHeight;\n const diff = Math.abs(naturalHeight - expectedSpriteHeight);\n\n if (diff > 2) {\n throw new Error(\n `Sprite size mismatch. Expected height ~${Math.round(expectedSpriteHeight)}px (slotCount=${slotCount}, slot aspect 1/1), got ${naturalHeight}px.`,\n );\n }\n\n this.spriteImg = img;\n this.spriteWidth = spriteWidth;\n this.frameHeight = Math.max(1, Math.round(frameHeight));\n this.spriteHeight = Math.max(1, Math.round(this.frameHeight * slotCount));\n }\n\n private setupResizeWatcher(): void {\n if (typeof ResizeObserver === 'undefined') return;\n if (this.resizeObserver) return;\n\n this.resizeObserver = new ResizeObserver(() => this.queueResize());\n this.resizeObserver.observe(this.container);\n }\n\n private queueResize = (): void => {\n if (this.resizeQueued) return;\n\n this.resizeQueued = true;\n\n requestAnimationFrame(() => {\n this.resizeQueued = false;\n this.resizeToContainer();\n });\n };\n\n private resizeToContainer(): void {\n const cssWidth = this.container.clientWidth;\n const cssHeight = this.container.clientHeight;\n const slotsViewport = this.visibleSlots * this.heightScale;\n\n if (cssWidth <= 0 || cssHeight <= 0) {\n this.applyResize(1, 1);\n\n return;\n }\n\n const slots = slotsViewport;\n const reels = this.reels;\n const fitByWidthH = (cssWidth / reels) * slots;\n\n let w = cssWidth;\n let h = fitByWidthH;\n\n if (h > cssHeight) {\n h = cssHeight;\n w = (cssHeight / slots) * reels;\n }\n\n const viewportWidth = Math.max(1, Math.floor(w));\n const viewportHeight = Math.max(1, Math.floor(h));\n\n this.applyResize(viewportWidth, viewportHeight);\n this.render();\n }\n\n private applyViewportCrop(): void {\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n this.canvas.style.transform = '';\n }\n\n private applyResize(width: number, height: number): void {\n const normalizedWidth = Math.max(1, Math.floor(width));\n const normalizedHeight = Math.max(1, Math.floor(height));\n\n const dpr = this.getTargetDpr();\n\n this.width = normalizedWidth;\n this.height = normalizedHeight;\n this.dpr = dpr;\n\n this.canvas.width = Math.floor(this.width * this.dpr);\n this.canvas.height = Math.floor(this.height * this.dpr);\n\n if (this.gl) {\n this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);\n }\n\n this.applyViewportCrop();\n }\n\n private getTargetDpr(): number {\n const rawDpr = window.devicePixelRatio || 1;\n const base = Math.max(1, rawDpr);\n\n return Math.min(this.maxDpr, base);\n }\n\n private tryInitWebGL(): void {\n if (!this.spriteImg) return;\n\n this.releaseWebGLResources();\n this.webglInitError = null;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n antialias: true,\n alpha: true,\n }) as WebGL2RenderingContext | null) ??\n (this.canvas.getContext('webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null) ??\n (this.canvas.getContext('experimental-webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null);\n\n if (!gl) {\n const fallback = document.createElement('canvas');\n const canWebGL = typeof WebGLRenderingContext !== 'undefined';\n const fallbackGl =\n (fallback.getContext('webgl2') as WebGL2RenderingContext | null) ??\n (fallback.getContext('webgl') as WebGLRenderingContext | null);\n\n this.webglInitError = `context is null (browser WebGL=${canWebGL ? 'yes' : 'no'}, fallback canvas=${fallbackGl ? 'yes' : 'no'})`;\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const vsSource = `\n attribute vec2 a_pos;\n attribute vec2 a_uv;\n varying vec2 v_uv;\n void main() {\n v_uv = a_uv;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n }\n `;\n\n const fsSource = `\n #ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n #endif\n\n varying vec2 v_uv;\n uniform sampler2D u_tex;\n uniform float u_reelX;\n uniform float u_reelW;\n uniform float u_offsetPx;\n uniform float u_frameHeight;\n uniform float u_spriteHeight;\n uniform float u_visibleSlots;\n uniform float u_heightScale;\n uniform float u_edgePower;\n uniform float u_warpAngle;\n uniform float u_blurPx;\n\n float saturate(float x) { return clamp(x, 0.0, 1.0); }\n\n void main() {\n if (v_uv.x < u_reelX || v_uv.x > (u_reelX + u_reelW)) {\n discard;\n }\n\n float localX = (v_uv.x - u_reelX) / u_reelW;\n // v_uv.y is authored as 0 at TOP, 1 at BOTTOM (Canvas-like).\n float localY = v_uv.y;\n float hs = max(0.0001, u_heightScale);\n float fullY = localY * hs + (1.0 - hs) * 0.5;\n\n float warp = max(0.0, u_warpAngle);\n float warpStrength = saturate(warp / 1.2);\n float center = 0.5;\n float centerBand = 1.0 / 3.0;\n float halfBand = centerBand * 0.5;\n float dist = abs(fullY - center);\n // Smoother transition: avoid a hard boundary at halfBand.\n float edgeWeight = smoothstep(halfBand, 0.5, dist);\n float edgePow = max(0.5, u_edgePower);\n edgeWeight = pow(edgeWeight, edgePow) * warpStrength;\n float tEase = smoothstep(0.0, 1.0, fullY);\n float warpedY = mix(fullY, tEase, edgeWeight);\n float y = (warpedY - 0.5) * 2.0;\n float theta = y * warp;\n float edgeWarp = edgeWeight;\n\n float yPx = u_offsetPx + warpedY * (u_frameHeight * u_visibleSlots);\n // With UNPACK_FLIP_Y_WEBGL=0 and Canvas-like UVs (v=0 at TOP),\n // v=0 samples the TOP of the original image, so pixel Y-from-top maps directly to v.\n // Normalize early to improve precision on mobile GPUs.\n float v = fract((u_offsetPx / u_spriteHeight) +\n warpedY * ((u_frameHeight * u_visibleSlots) / u_spriteHeight));\n\n vec4 base = texture2D(u_tex, vec2(localX, v));\n\n if (u_blurPx > 0.0) {\n // Clamp blur step to reduce banding on mobile GPUs.\n // Scale max step with speed proxy (u_blurPx).\n float maxStepPx = 1.0 + 3.0 * saturate(u_blurPx / 4.0);\n float blurPx = min(u_blurPx, maxStepPx);\n float blurStep = blurPx / max(1.0, u_spriteHeight);\n // 9-tap gaussian-ish blur to reduce banding on mobile.\n vec4 sum = base * 0.227027;\n sum += texture2D(u_tex, vec2(localX, fract(v + blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v - blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v + 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v + 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v - 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v + 4.0 * blurStep))) * 0.016216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 4.0 * blurStep))) * 0.016216;\n base = sum;\n }\n\n gl_FragColor = base;\n }\n `;\n\n const program = this.createWebGLProgram(gl, vsSource, fsSource);\n\n if (!program) {\n this.webglInitError = 'shader program failed to compile/link (see console)';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const buffer = gl.createBuffer();\n\n if (!buffer) {\n this.webglInitError = 'failed to create vertex buffer';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\n const quad = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0]);\n\n gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n const texture = gl.createTexture();\n\n if (!texture) {\n this.webglInitError = 'failed to create texture';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteBuffer(buffer);\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.spriteImg);\n\n const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0;\n const pot = isPowerOfTwo(this.spriteWidth) && isPowerOfTwo(this.spriteHeight);\n\n if (pot) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n }\n\n gl.useProgram(program);\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n this.gl = gl;\n this.glProgram = program;\n this.glBuffer = buffer;\n this.glTexture = texture;\n this.glAttribs = {\n pos: gl.getAttribLocation(program, 'a_pos'),\n uv: gl.getAttribLocation(program, 'a_uv'),\n };\n this.glUniforms = {\n texture: gl.getUniformLocation(program, 'u_tex'),\n reelX: gl.getUniformLocation(program, 'u_reelX'),\n reelW: gl.getUniformLocation(program, 'u_reelW'),\n offsetPx: gl.getUniformLocation(program, 'u_offsetPx'),\n frameHeight: gl.getUniformLocation(program, 'u_frameHeight'),\n spriteHeight: gl.getUniformLocation(program, 'u_spriteHeight'),\n visibleSlots: gl.getUniformLocation(program, 'u_visibleSlots'),\n heightScale: gl.getUniformLocation(program, 'u_heightScale'),\n edgePower: gl.getUniformLocation(program, 'u_edgePower'),\n warpAngle: gl.getUniformLocation(program, 'u_warpAngle'),\n blurPx: gl.getUniformLocation(program, 'u_blurPx'),\n };\n }\n\n private createWebGLProgram(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n vsSource: string,\n fsSource: string,\n ): WebGLProgram | null {\n const vs = this.createWebGLShader(gl, gl.VERTEX_SHADER, vsSource);\n const fs = this.createWebGLShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vs || !fs) {\n if (vs) gl.deleteShader(vs);\n if (fs) gl.deleteShader(fs);\n\n return null;\n }\n\n const program = gl.createProgram();\n\n if (!program) return null;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program) ?? 'unknown link error';\n\n this.webglInitError = `program link failed: ${info}`;\n\n console.warn('ReelDeal WebGL: program link failed', info);\n\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return null;\n }\n\n gl.detachShader(program, vs);\n gl.detachShader(program, fs);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return program;\n }\n\n private createWebGLShader(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader) ?? 'unknown compile error';\n\n this.webglInitError = `shader compile failed: ${info}`;\n\n console.warn('ReelDeal WebGL: shader compile failed', info);\n\n gl.deleteShader(shader);\n\n return null;\n }\n\n return shader;\n }\n\n private releaseWebGLResources(): void {\n if (!this.gl) return;\n if (this.glTexture) this.gl.deleteTexture(this.glTexture);\n if (this.glBuffer) this.gl.deleteBuffer(this.glBuffer);\n if (this.glProgram) this.gl.deleteProgram(this.glProgram);\n\n this.glTexture = null;\n this.glBuffer = null;\n this.glProgram = null;\n this.glAttribs = null;\n this.glUniforms = null;\n this.gl = null;\n }\n}\n"],"names":["defaultSpinConfig","defaultWebGLShading","defaultSpinBlur","defaultIdleBob","SPIN_ACCEL_END","SPIN_DECEL_START","clampInt","value","min","max","mod","n","m","clamp01","t","rampHermite","u","x","brakeHermite","reelProgress","tt","finiteOr","fallback","nonNegative","nonNegativeInt","DEFAULT_MAX_DPR","normalizeSpinConfig","overrides","base","normalizeSpinBlur","normalizeWebGLShading","normalizeIdleBob","normalizeMaxDpr","resolveElementById","id","kind","el","resolveElement","elOrId","MAX_REELS","SPIN_TICK_START","SPIN_TICK_RANGE","SPIN_TICK_MAX_PX","VELOCITY_DECAY","VELOCITY_GAIN","ReelDeal","options","target","initialSegments","segments","r","segIndex","idx","segmentIndex","stopAtSegments","config","minSpins","staggerMs","speedPxS","durationMs","useSpeed","startOffsets","deltas","durations","desiredTop","desired","current","alignDelta","delta","desiredDistance","loops","extraDelta","extraLoops","acc","v","entry","startTime","resolve","spins","spin","resolvers","disabled","now","allDone","duration","eased","baseOffset","tick","env","phase","amp","reel","speed","speedAtMax","gl","shading","warpAngle","reelW","offsetPx","dt","amplitudePx","speedHz","phaseOffsetRad","delayMs","rampMs","elapsedMs","tSec","rampT","ramp","omega","sprite","img","reject","onLoad","onError","slotCount","spriteWidth","frameHeight","expectedSpriteHeight","naturalHeight","cssWidth","cssHeight","slotsViewport","slots","reels","fitByWidthH","w","h","viewportWidth","viewportHeight","width","height","normalizedWidth","normalizedHeight","dpr","rawDpr","canWebGL","fallbackGl","program","buffer","quad","texture","isPowerOfTwo","vsSource","fsSource","vs","fs","info","type","source","shader"],"mappings":"AAOO,MAAMA,IAAwC;AAAA,EACnD,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AACb,GAEaC,IAA4C;AAAA,EACvD,cAAc;AAAA,EACd,WAAW;AACb,GAEaC,IAAoC;AAAA,EAC/C,OAAO;AAAA,EACP,YAAY;AACd,GAEaC,IAAkC;AAAA,EAC7C,aAAa;AAAA,EACb,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,QAAQ;AACV,GC9BMC,IAAiB,KACjBC,IAAmB,KAEZC,IAAW,CAACC,GAAeC,GAAaC,MACnD,KAAK,IAAID,GAAK,KAAK,IAAIC,GAAK,KAAK,MAAMF,CAAK,CAAC,CAAC,GAEnCG,IAAM,CAACC,GAAWC,OAAwBD,IAAIC,IAAKA,KAAKA,GAExDC,IAAU,CAACC,MAAsB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGA,CAAC,CAAC,GAE3DC,IAAc,CAACC,MAAsB;AAChD,QAAMC,IAAIJ,EAAQG,CAAC;AAEnB,SAAOC,KAAK,KAAK,IAAIA;AACvB,GAEaC,IAAe,CAACF,MAAsB;AACjD,QAAMC,IAAIJ,EAAQG,CAAC;AAEnB,SAAOC,IAAIA,KAAK,IAAIA,KAAK;AAC3B,GAEaE,IAAe,CAACL,MAAsB;AACjD,QAAMM,IAAKP,EAAQC,CAAC;AAEpB,MAAIM,KAAMhB,GAAgB;AACxB,UAAMY,IAAII,IAAKhB;AAEf,WAAOW,EAAYC,CAAC,IAAIZ;AAAA,EAC1B;AAEA,MAAIgB,IAAKf;AACP,WAAOe;AAGT,QAAMJ,KAAKI,IAAKf,MAAqB,IAAIA;AAEzC,SAAOA,IAAmBa,EAAaF,CAAC,KAAK,IAAIX;AACnD,GAEagB,IAAW,CAACd,GAA2Be,MAClD,OAAOf,KAAU,YAAY,OAAO,SAASA,CAAK,IAAIA,IAAQe,GAEnDC,IAAc,CAAChB,GAA2Be,MACrD,KAAK,IAAI,GAAGD,EAASd,GAAOe,CAAQ,CAAC,GAE1BE,IAAiB,CAACjB,GAA2Be,MACxD,KAAK,IAAI,GAAG,KAAK,MAAMD,EAASd,GAAOe,CAAQ,CAAC,CAAC,GCjC7CG,IAAkB,GAIXC,IAAsB,CACjCC,MACyB;AACzB,QAAMC,IAAO,EAAE,GAAG5B,GAAmB,GAAI2B,KAAa,CAAA,EAAC;AAEvD,SAAO;AAAA,IACL,UAAUH,EAAeI,EAAK,UAAU5B,EAAkB,QAAQ;AAAA,IAClE,YAAYwB,EAAeI,EAAK,YAAY5B,EAAkB,cAAc,CAAC;AAAA,IAC7E,UAAUwB,EAAeI,EAAK,UAAU5B,EAAkB,YAAY,CAAC;AAAA,IACvE,WAAWwB,EAAeI,EAAK,WAAW5B,EAAkB,aAAa,CAAC;AAAA,EAAA;AAE9E,GAEa6B,IAAoB,CAACF,MAA4D;AAC5F,QAAMC,IAAO,EAAE,GAAG1B,GAAiB,GAAIyB,KAAa,CAAA,EAAC;AAErD,SAAO;AAAA,IACL,OAAOJ,EAAYK,EAAK,OAAO1B,EAAgB,KAAK;AAAA,IACpD,YAAYqB,EAAYK,EAAK,YAAY1B,EAAgB,UAAU;AAAA,EAAA;AAEvE,GAEa4B,IAAwB,CACnCH,MACyB;AACzB,QAAMC,IAAO,EAAE,GAAG3B,GAAqB,GAAI0B,KAAa,CAAA,EAAC;AAEzD,SAAO;AAAA,IACL,cAAcJ,EAAYK,EAAK,cAAc3B,EAAoB,YAAY;AAAA,IAC7E,WAAWsB,EAAYK,EAAK,WAAW3B,EAAoB,SAAS;AAAA,EAAA;AAExE,GAEa8B,IAAmB,CAC9BJ,MAC2B;AAC3B,MAAIA,MAAc,GAAO,QAAO;AAEhC,QAAMC,IAAO,EAAE,GAAGzB,GAAgB,GAAIwB,KAAa,CAAA,EAAC;AAEpD,SAAO;AAAA,IACL,aAAaJ,EAAYK,EAAK,aAAazB,EAAe,WAAW;AAAA,IACrE,SAASoB,EAAYK,EAAK,SAASzB,EAAe,OAAO;AAAA,IACzD,gBAAgBkB,EAASO,EAAK,gBAAgBzB,EAAe,cAAc;AAAA,IAC3E,SAASqB,EAAeI,EAAK,SAASzB,EAAe,OAAO;AAAA,IAC5D,QAAQqB,EAAeI,EAAK,QAAQzB,EAAe,MAAM;AAAA,EAAA;AAE7D,GAEa6B,IAAkB,CAACzB,MAC9B,KAAK,IAAI,GAAGc,EAASd,GAAOkB,CAAe,CAAC,GCpEjCQ,IAAqB,CAAwBC,GAAYC,MAAoB;AACxF,QAAMC,IAAK,SAAS,eAAeF,CAAE;AAErC,MAAI,CAACE;AACH,UAAM,IAAI,MAAM,GAAGD,CAAI,eAAeD,CAAE,EAAE;AAG5C,SAAOE;AACT,GAEaC,IAAiB,CAAwBC,GAAoBH,MACpE,OAAOG,KAAW,WAAiBL,EAAsBK,GAAQH,CAAI,IAElEG,GCMHC,IAAY,GACZC,IAAkB,MAClBC,IAAkB,MAClBC,IAAmB,GACnBC,IAAiB,MACjBC,IAAgB,IAAID;AASnB,MAAME,EAAS;AAAA,EACH;AAAA,EACA;AAAA,EAEA;AAAA,EACA,eAAe;AAAA,EACf,cAAc,IAAI;AAAA,EAClB,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAqC;AAAA,EACrC,cAAc;AAAA,EACd,eAAe;AAAA,EACf,cAAc;AAAA,EAEd,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AAAA,EAEN;AAAA,EACA;AAAA,EACA,WAA0B;AAAA,EAC1B;AAAA,EACA,gBAA+B;AAAA,EAC/B;AAAA,EAEA,KAA4D;AAAA,EAC5D,YAAiC;AAAA,EACjC,WAA+B;AAAA,EAC/B,YAAiC;AAAA,EACjC,YAAgD;AAAA,EAChD,iBAAgC;AAAA,EAChC,aAYG;AAAA,EAEH,OAA6B;AAAA,EAC7B,QAAuB;AAAA,EACvB,uBAA0C,CAAA;AAAA,EAE1C,iBAAwC;AAAA,EACxC,eAAe;AAAA,EAEf,SAAmC;AAAA,EACnC,wBAAwB;AAAA,EACxB,WAAW;AAAA,EACX,cAA0C;AAAA,EAElD,YAAYC,GAA0B;AACpC,SAAK,OAAOA,GACZ,KAAK,QAAQxC,EAASwC,EAAQ,SAAS,GAAG,GAAGP,CAAS,GACtD,KAAK,aAAab,EAAoBoB,EAAQ,UAAU,GACxD,KAAK,WAAWjB,EAAkBiB,EAAQ,QAAQ,GAClD,KAAK,eAAehB,EAAsBgB,EAAQ,YAAY,GAC9D,KAAK,UAAUf,EAAiBe,EAAQ,OAAO,GAC/C,KAAK,SAASd,EAAgBc,EAAQ,MAAM,GAE5C,KAAK,SAAST,EAAkCS,EAAQ,QAAQ,QAAQ,GACxE,KAAK,YAAYT,EAA4BS,EAAQ,WAAW,WAAW,GAE3E,KAAK,UAAU,IAAI,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC,GAC3C,KAAK,aAAa,IAAI,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC,GAC9C,KAAK,iBAAiB,IAAI,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC,GAClD,KAAK,kBAAkB,IAAI,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC;AAAA,EACrD;AAAA,EAEA,MAAM,OAAsB;AAK1B,QAJA,MAAM,KAAK,WAAA,GAEX,KAAK,aAAA,GAED,CAAC,KAAK;AACR,YAAM,IAAI;AAAA,QACR,mDAAmD,KAAK,kBAAkB,EAAE,GAAG,KAAA;AAAA,MAAK;AAIxF,SAAK,wBAAA,GACL,KAAK,4BAAA,GACL,KAAK,mBAAA,GACL,KAAK,kBAAA,GAEL,KAAK,YAAY,KAAK,KAAK,eAAe,GAE1C,KAAK,SAAS,KAAK,cAAA,GACnB,KAAK,cAAc,KAAK,iBAAA,GACxB,KAAK,oBAAA,GAEL,KAAK,OAAA,GACL,KAAK,UAAA;AAAA,EACP;AAAA,EAEA,UAAgB;AACd,SAAK,KAAA,GAED,KAAK,mBACP,KAAK,eAAe,WAAA,GACpB,KAAK,iBAAiB,OAGpB,KAAK,UAAU,KAAK,0BACtB,KAAK,OAAO,oBAAoB,SAAS,KAAK,iBAAiB,GAC/D,KAAK,wBAAwB,KAG/B,KAAK,sBAAA;AAAA,EACP;AAAA,EAEQ,8BAAoC;AAC1C,IAAI,KAAK,WAAW,KAAK,aACpB,KAAK,UAAU,SAAS,KAAK,MAAM,MAEnC,KAAK,OAAO,MAAM,UAAO,KAAK,OAAO,MAAM,QAAQ,SACnD,KAAK,OAAO,MAAM,WAAQ,KAAK,OAAO,MAAM,SAAS,SACrD,KAAK,OAAO,MAAM,YAAS,KAAK,OAAO,MAAM,UAAU;AAAA,EAC9D;AAAA,EAEQ,0BAAgC;AACtC,UAAMC,IAAS,KAAK,WAAW,KAAK,YAAY,KAAK,SAAS,KAAK;AAEnE,IAAAA,EAAO,MAAM,YAAY,WAAW,OAAO,KAAK,KAAK,CAAC,GACtDA,EAAO,MAAM,YAAY,mBAAmB,OAAO,KAAK,YAAY,CAAC,GACrEA,EAAO,MAAM,cAAc,GAAG,KAAK,KAAK,MAAM,KAAK,eAAe,KAAK,WAAW,IAE9E,KAAK,WAAW,KAAK,cACvB,KAAK,UAAU,MAAM,WAAW;AAAA,EAEpC;AAAA,EAEA,YAAYC,GAAkC;AAC5C,SAAK,YAAA;AAEL,UAAMC,IAAWD,KAAmB,CAAA;AAEpC,aAASE,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK,GAAG;AACtC,YAAMC,IAAWF,EAASC,CAAC,KAAK,GAC1BE,IAAM9C,EAAS6C,GAAU,GAAG,KAAK,KAAK,YAAY,CAAC;AAEzD,WAAK,QAAQD,CAAC,IAAIE,IAAM,KAAK,cAAc,KAAK,cAAc,KAAK,aACnE,KAAK,eAAeF,CAAC,IAAI,KAAK,QAAQA,CAAC,GACvC,KAAK,WAAWA,CAAC,IAAI,GACrB,KAAK,gBAAgBA,CAAC,IAAI,KAAK,QAAQA,CAAC;AAAA,IAC1C;AAEA,SAAK,WAAW,MAChB,KAAK,gBAAgB,MACrB,KAAK,OAAO,MAEZ,KAAK,KAAA,GACL,KAAK,OAAA,GACL,KAAK,UAAA;AAAA,EACP;AAAA,EAEA,WAAWG,GAA4B;AACrC,SAAK,YAAY,IAAI,MAAM,KAAK,KAAK,EAAE,KAAKA,CAAY,CAAC;AAAA,EAC3D;AAAA,EAEA,SAASC,GAA0BC,GAAqD;AACtF,SAAK,YAAA;AAEL,UAAM,EAAE,UAAAC,GAAU,WAAAC,GAAW,UAAAC,GAAU,YAAAC,MAAe,KAAK,cAAcJ,CAAM,GACzEK,IAAWF,IAAW,GAEtBG,IAAe,IAAI,MAAM,KAAK,KAAK,GACnCC,IAAS,IAAI,MAAM,KAAK,KAAK,GAC7BC,IAAY,IAAI,MAAM,KAAK,KAAK;AAEtC,aAASb,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK,GAAG;AAEtC,YAAMc,IADM1D,EAASgD,EAAeJ,CAAC,KAAK,GAAG,GAAG,KAAK,KAAK,YAAY,CAAC,IAC9C,KAAK,cAAc,KAAK,cAAc,KAAK,aAC9De,IAAUvD,EAAIsD,GAAY,KAAK,YAAY,GAC3CE,IAAUxD,EAAI,KAAK,QAAQwC,CAAC,GAAG,KAAK,YAAY,GAEhDiB,IAAazD,EAAIwD,IAAUD,GAAS,KAAK,YAAY;AAE3D,UAAIG,IAAQ,EAAEZ,IAAW,KAAK,eAAeW;AAE7C,UAAIP,KAAYD,IAAa,GAAG;AAC9B,cAAMU,IAAmBX,IAAWC,IAAc,KAC5CW,IAAQ,KAAK;AAAA,UACjBd;AAAA,UACA,KAAK,KAAK,KAAK,IAAI,GAAGa,IAAkBF,CAAU,IAAI,KAAK,YAAY;AAAA,QAAA;AAGzE,QAAAC,IAAQ,EAAED,IAAaG,IAAQ,KAAK;AAAA,MACtC;AAEA,UAAIb,IAAY,KAAKG,GAAU;AAC7B,cAAMW,IAAcb,IAAWD,IAAYP,IAAK,KAC1CsB,IAAa,KAAK,KAAKD,IAAa,KAAK,YAAY;AAE3D,QAAAH,KAASI,IAAa,KAAK;AAAA,MAC7B;AAEA,MAAAX,EAAaX,CAAC,IAAI,KAAK,QAAQA,CAAC,GAChCY,EAAOZ,CAAC,IAAIkB,GAERR,IACFG,EAAUb,CAAC,IAAI,KAAK,IAAI,GAAG,KAAK,MAAO,KAAK,IAAIkB,CAAK,IAAIV,IAAY,GAAI,CAAC,IAE1EK,EAAUb,CAAC,IAAI,KAAK,IAAI,GAAGS,IAAaF,IAAYP,CAAC;AAAA,IAEzD;AAIA,QAFmBY,EAAO,OAAO,CAACW,GAAKC,MAAMD,IAAM,KAAK,IAAIC,CAAC,GAAG,CAAC,MAE9C,KAAKX,EAAU,MAAM,CAACY,MAAUA,KAAS,CAAC,GAAG;AAC9D,eAASzB,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,aAAK,QAAQA,CAAC,IAAI,KAAK,QAAQA,CAAC,IAAIY,EAAOZ,CAAC;AAG9C,kBAAK,OAAO,MACZ,KAAK,oBAAA,GACL,KAAK,OAAA,GAEE,QAAQ,QAAA;AAAA,IACjB;AAEA,UAAM0B,IAAY,YAAY,IAAA;AAE9B,gBAAK,OAAO,EAAE,WAAAA,GAAW,cAAAf,GAAc,QAAAC,GAAQ,YAAYC,EAAA,GAC3D,KAAK,gBAAgB,MACrB,KAAK,UAAA,GAEE,IAAI,QAAQ,CAACc,MAAY;AAC9B,WAAK,qBAAqB,KAAKA,CAAO;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEQ,cAAclD,GAA+D;AACnF,WAAKA,IAEED,EAAoB,EAAE,GAAG,KAAK,YAAY,GAAGC,GAAW,IAFxC,KAAK;AAAA,EAG9B;AAAA,EAEA,MAAM,UAAUmD,GAA2C;AACzD,aAAS,IAAI,GAAG,IAAIA,EAAM,QAAQ,KAAK,GAAG;AACxC,YAAMC,IAAOD,EAAM,CAAC;AAEpB,YAAM,KAAK,SAASC,EAAK,cAAc,GAEvCA,EAAK,WAAW,GAAGA,EAAK,cAAc;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,OAAa;AACX,IAAI,KAAK,UAAU,QAAM,qBAAqB,KAAK,KAAK,GAExD,KAAK,QAAQ,MACb,KAAK,OAAO,MACZ,KAAK,oBAAA,GACL,KAAK,WAAW;AAEhB,aAAS7B,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,WAAK,WAAWA,CAAC,IAAI;AAAA,EAEzB;AAAA,EAEQ,sBAA4B;AAClC,QAAI,KAAK,qBAAqB,WAAW,EAAG;AAE5C,UAAM8B,IAAY,KAAK;AAEvB,SAAK,uBAAuB,CAAA;AAE5B,eAAWH,KAAWG;AACpB,MAAAH,EAAA;AAAA,EAEJ;AAAA,EAEA,gBAAsB;AACpB,SAAK,YAAA;AAAA,EACP;AAAA,EAEQ,cAAoB;AAC1B,QACE,CAAC,KAAK,aACN,KAAK,eAAe,KACpB,KAAK,gBAAgB,KACrB,KAAK,eAAe;AAEpB,YAAM,IAAI,MAAM,sDAAsD;AAAA,EAE1E;AAAA,EAEQ,sBAA4B;AAClC,IAAI,CAAC,KAAK,UAAU,KAAK,0BAEzB,KAAK,OAAO,iBAAiB,SAAS,KAAK,mBAAmB,EAAE,SAAS,IAAM,GAC/E,KAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEQ,oBAAoB,YAA2B;AACrD,QAAI,KAAK,SAAU;AAEnB,UAAME,IAAO,KAAK,qBAAqB;AAAA,MACrC,gBAAgB,KAAK,oBAAA;AAAA,IAAoB;AAG3C,SAAK,WAAW,IAChB,KAAK,kBAAkB,EAAI;AAE3B,QAAI;AACF,YAAM,KAAK,SAASA,EAAK,cAAc;AAAA,IACzC,UAAA;AACE,MAAI,KAAK,eAAe,KAAK,YAAY,WAAW,IAClD,KAAK,kBAAkB,EAAI,IAE3B,KAAK,kBAAkB,EAAK,GAG9B,KAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,gBAA0C;AAChD,WAAK,KAAK,KAAK,SAER1C,EAAkC,KAAK,KAAK,QAAQ,QAAQ,IAFrC;AAAA,EAGhC;AAAA,EAEQ,mBAA+C;AACrD,WAAO,KAAK,KAAK,oBAAoB;AAAA,EACvC;AAAA,EAEQ,kBAA4C;AAKlD,WAJK,KAAK,gBACR,KAAK,cAAc,KAAK,iBAAA,IAGtB,CAAC,KAAK,eAAe,KAAK,YAAY,WAAW,IAAU,OAExD,KAAK,YAAY,MAAA,KAAW;AAAA,EACrC;AAAA,EAEQ,sBAAgC;AACtC,UAAM5B,IAAM,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAK,SAAS,CAAC,GACjDwC,IAAqB,CAAA;AAE3B,aAASC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,MAAAD,EAAS,KAAK,KAAK,MAAM,KAAK,OAAA,IAAWxC,CAAG,CAAC;AAG/C,WAAOwC;AAAA,EACT;AAAA,EAEQ,kBAAkBgC,GAAyB;AACjD,IAAK,KAAK,WAEV,KAAK,OAAO,WAAWA,GAEnBA,IAAU,KAAK,OAAO,aAAa,aAAa,MAAM,IACrD,KAAK,OAAO,gBAAgB,WAAW;AAAA,EAC9C;AAAA,EAEQ,gBAAyB;AAC/B,WAAO,KAAK,SAAS,QAAQ,KAAK,YAAY;AAAA,EAChD;AAAA,EAEQ,YAAkB;AACxB,IAAI,KAAK,UAAU,QACd,KAAK,oBAEV,KAAK,QAAQ,sBAAsB,KAAK,IAAI;AAAA,EAC9C;AAAA,EAEQ,OAAO,CAACC,MAAsB;AACpC,QAAI,KAAK,MAAM;AACb,YAAM,EAAE,WAAAN,GAAW,YAAAjB,GAAY,cAAAE,GAAc,QAAAC,EAAA,IAAW,KAAK;AAE7D,UAAIqB,IAAU;AAEd,eAASjC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK,GAAG;AACtC,cAAMkC,IAAWzB,EAAWT,CAAC,KAAK,GAC5BpC,IAAIsE,KAAY,IAAI,IAAI,KAAK,IAAI,IAAIF,IAAMN,KAAaQ,CAAQ,GAChEC,IAAQlE,EAAaL,CAAC;AAE5B,QAAIA,IAAI,MAAGqE,IAAU;AAErB,cAAMG,IAAazB,EAAaX,CAAC,IAAIY,EAAOZ,CAAC,IAAImC;AAEjD,YAAIE,IAAO;AAEX,YAAIzE,IAAI0B,KAAmB,KAAK,cAAc,GAAG;AAE/C,gBAAMgD,KAAO,IADH3E,GAASC,IAAI0B,KAAmBC,CAAe,MAClC,GACjBgD,IAASH,IAAa,KAAK,cAAe,KAAK,KAAK,GACpDI,IAAM,KAAK,IAAIhD,GAAkB,KAAK,cAAc,IAAI;AAE9D,UAAA6C,IAAO,KAAK,IAAIE,CAAK,IAAID,IAAME;AAAA,QACjC;AAEA,aAAK,QAAQxC,CAAC,IAAIoC,IAAaC;AAAA,MACjC;AAKA,UAHA,KAAK,eAAeL,CAAG,GACvB,KAAK,OAAA,GAEDC,GAAS;AACX,iBAASjC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,eAAK,QAAQA,CAAC,IAAI,KAAK,KAAK,aAAaA,CAAC,IAAI,KAAK,KAAK,OAAOA,CAAC,GAChE,KAAK,gBAAgBA,CAAC,IAAI,KAAK,QAAQA,CAAC;AAG1C,aAAK,eAAegC,CAAG;AAEvB,iBAAShC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,eAAK,WAAWA,CAAC,IAAI;AAGvB,aAAK,OAAO,MACZ,KAAK,gBAAgB,MAErB,KAAK,OAAA,GACL,KAAK,oBAAA;AAAA,MACP;AAAA,IACF,MAAA,CAAW,KAAK,YACd,KAAK,WAAWgC,CAAG,GACnB,KAAK,OAAA;AAGP,QAAI,CAAC,KAAK,iBAAiB;AACzB,WAAK,QAAQ;AAEb;AAAA,IACF;AAEA,IAAI,KAAK,UAAU,SAEnB,KAAK,QAAQ,sBAAsB,KAAK,IAAI;AAAA,EAC9C;AAAA,EAEQ,SAAe;AACrB,IAAK,KAAK,cACN,KAAK,SAAS,KAAK,KAAK,UAAU,KACjC,KAAK,MAEV,KAAK,YAAA;AAAA,EACP;AAAA,EAEQ,qBAAqBS,GAAsB;AACjD,QAAI,CAAC,KAAK,KAAM,QAAO;AAEvB,UAAMC,IAAQ,KAAK,IAAI,KAAK,WAAWD,CAAI,KAAK,CAAC,GAC3CE,IAAa,KAAK,IAAI,GAAG,KAAK,SAAS,UAAU;AAGvD,WAFUhF,EAAQ+E,IAAQC,CAAU,IAEzB,KAAK,IAAI,GAAG,KAAK,SAAS,KAAK;AAAA,EAC5C;AAAA,EAEQ,cAAoB;AAY1B,QAVE,CAAC,KAAK,MACN,CAAC,KAAK,aACN,CAAC,KAAK,aACN,CAAC,KAAK,cACN,CAAC,KAAK,aACN,CAAC,KAAK,YAKJ,CAAC,KAAK,UAAW;AAErB,UAAMC,IAAK,KAAK;AAEhB,IAAAA,EAAG,WAAW,KAAK,SAAS,GAC5BA,EAAG,WAAWA,EAAG,cAAc,KAAK,QAAQ,GAE5CA,EAAG,wBAAwB,KAAK,UAAU,GAAG,GAC7CA,EAAG,oBAAoB,KAAK,UAAU,KAAK,GAAGA,EAAG,OAAO,IAAO,IAAI,CAAC,GACpEA,EAAG,wBAAwB,KAAK,UAAU,EAAE,GAC5CA,EAAG,oBAAoB,KAAK,UAAU,IAAI,GAAGA,EAAG,OAAO,IAAO,IAAI,CAAC,GAEnEA,EAAG,cAAcA,EAAG,QAAQ,GAC5BA,EAAG,YAAYA,EAAG,YAAY,KAAK,SAAS,GAC5CA,EAAG,UAAU,KAAK,WAAW,SAAS,CAAC,GAEvCA,EAAG,UAAU,KAAK,WAAW,aAAa,KAAK,WAAW,GAC1DA,EAAG,UAAU,KAAK,WAAW,cAAc,KAAK,YAAY,GAC5DA,EAAG,UAAU,KAAK,WAAW,cAAc,KAAK,YAAY,GAC5DA,EAAG,UAAU,KAAK,WAAW,aAAa,KAAK,WAAW,GAC1DA,EAAG,UAAU,KAAK,WAAW,WAAW,KAAK,IAAI,KAAK,KAAK,aAAa,SAAS,CAAC;AAElF,UAAMC,IAAU,KAAK,cACfC,IAAa,KAAK,IAAI,GAAGD,EAAQ,YAAY,IAAI,KAAK,KAAM;AAElE,IAAAD,EAAG,UAAU,KAAK,WAAW,WAAWE,CAAS,GACjDF,EAAG,MAAMA,EAAG,gBAAgB;AAE5B,UAAMG,IAAQ,IAAI,KAAK;AAEvB,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,KAAK,GAAG;AACtC,MAAAH,EAAG,UAAU,KAAK,WAAW,OAAO,IAAIG,CAAK,GAC7CH,EAAG,UAAU,KAAK,WAAW,OAAOG,CAAK;AAEzC,YAAMC,IAAWxF,EAAI,KAAK,QAAQ,CAAC,GAAG,KAAK,YAAY;AAEvD,MAAAoF,EAAG,UAAU,KAAK,WAAW,UAAUI,CAAQ,GAC/CJ,EAAG,UAAU,KAAK,WAAW,QAAQ,KAAK,qBAAqB,CAAC,CAAC,GACjEA,EAAG,WAAWA,EAAG,gBAAgB,GAAG,CAAC;AAAA,IACvC;AAAA,EACF;AAAA,EAEQ,eAAeZ,GAAmB;AACxC,QAAI,KAAK,aAAa,MAAM;AAC1B,WAAK,WAAWA;AAEhB,eAAShC,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,aAAK,eAAeA,CAAC,IAAI,KAAK,QAAQA,CAAC,GACvC,KAAK,WAAWA,CAAC,IAAI;AAGvB;AAAA,IACF;AAEA,UAAMiD,KAAMjB,IAAM,KAAK,YAAY;AAEnC,QAAI,EAAAiB,KAAM,IAEV;AAAA,eAASjD,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK,GAAG;AACtC,cAAMwB,KAAK,KAAK,QAAQxB,CAAC,IAAI,KAAK,eAAeA,CAAC,KAAKiD;AAEvD,aAAK,WAAWjD,CAAC,IAAI,KAAK,WAAWA,CAAC,IAAIP,IAAiB+B,IAAI9B,GAC/D,KAAK,eAAeM,CAAC,IAAI,KAAK,QAAQA,CAAC;AAAA,MACzC;AAEA,WAAK,WAAWgC;AAAA;AAAA,EAClB;AAAA,EAEQ,WAAWA,GAAmB;AACpC,QAAI,CAAC,KAAK,QAAS;AACnB,IAAI,KAAK,kBAAkB,SAAM,KAAK,gBAAgBA;AAEtD,UAAM,EAAE,aAAAkB,GAAa,SAAAC,GAAS,gBAAAC,GAAgB,SAAAC,GAAS,QAAAC,EAAA,IAAW,KAAK,SACjEC,IAAYvB,IAAM,KAAK;AAE7B,QAAIuB,IAAY,KAAK,IAAI,GAAGF,CAAO,GAAG;AACpC,eAASrD,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK;AACnC,aAAK,QAAQA,CAAC,IAAI,KAAK,gBAAgBA,CAAC,GACxC,KAAK,WAAWA,CAAC,IAAI;AAGvB;AAAA,IACF;AAEA,UAAMwD,KAAQD,IAAY,KAAK,IAAI,GAAGF,CAAO,KAAK,KAC5CI,IAAQ,KAAK;AAAA,MACjB;AAAA,MACA,KAAK,IAAI,IAAIF,IAAY,KAAK,IAAI,GAAGF,CAAO,KAAK,KAAK,IAAI,GAAGC,CAAM,CAAC;AAAA,IAAA,GAEhEI,IAAO7F,EAAY4F,CAAK,GACxBE,IAAQR,IAAU,KAAK,KAAK;AAElC,aAASnD,IAAI,GAAGA,IAAI,KAAK,OAAOA,KAAK,GAAG;AACtC,YAAMuC,IAAQiB,IAAOG,IAAQ3D,IAAIoD;AAEjC,WAAK,QAAQpD,CAAC,IAAI,KAAK,gBAAgBA,CAAC,IAAI,KAAK,IAAIuC,CAAK,KAAKW,IAAcQ,IAC7E,KAAK,WAAW1D,CAAC,IAAI;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,aAA4B;AACxC,UAAM,EAAE,QAAA4D,MAAW,KAAK,MAClBC,IAAM,OAAOD,KAAW,WAAW,IAAI,UAAUA;AAEvD,IAAI,OAAOA,KAAW,aACpBC,EAAI,MAAMD,KAGR,CAACC,EAAI,YAAYA,EAAI,gBAAgB,MACvC,MAAM,IAAI,QAAc,CAAClC,GAASmC,MAAW;AAC3C,YAAMC,IAAS,MAAMpC,EAAA,GACfqC,IAAU,MAAMF,EAAO,IAAI,MAAM,6BAA6B,CAAC;AAErE,MAAAD,EAAI,iBAAiB,QAAQE,GAAQ,EAAE,MAAM,IAAM,GACnDF,EAAI,iBAAiB,SAASG,GAAS,EAAE,MAAM,IAAM;AAAA,IACvD,CAAC;AAGH,UAAMC,IAAY,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAK,SAAS,CAAC,GACvDC,IAAcL,EAAI,cAClBM,IAAcD,GACdE,IAAuBD,IAAcF,GAErCI,IAAgBR,EAAI;AAG1B,QAFa,KAAK,IAAIQ,IAAgBD,CAAoB,IAE/C;AACT,YAAM,IAAI;AAAA,QACR,0CAA0C,KAAK,MAAMA,CAAoB,CAAC,iBAAiBH,CAAS,2BAA2BI,CAAa;AAAA,MAAA;AAIhJ,SAAK,YAAYR,GACjB,KAAK,cAAcK,GACnB,KAAK,cAAc,KAAK,IAAI,GAAG,KAAK,MAAMC,CAAW,CAAC,GACtD,KAAK,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,cAAcF,CAAS,CAAC;AAAA,EAC1E;AAAA,EAEQ,qBAA2B;AACjC,IAAI,OAAO,iBAAmB,OAC1B,KAAK,mBAET,KAAK,iBAAiB,IAAI,eAAe,MAAM,KAAK,aAAa,GACjE,KAAK,eAAe,QAAQ,KAAK,SAAS;AAAA,EAC5C;AAAA,EAEQ,cAAc,MAAY;AAChC,IAAI,KAAK,iBAET,KAAK,eAAe,IAEpB,sBAAsB,MAAM;AAC1B,WAAK,eAAe,IACpB,KAAK,kBAAA;AAAA,IACP,CAAC;AAAA,EACH;AAAA,EAEQ,oBAA0B;AAChC,UAAMK,IAAW,KAAK,UAAU,aAC1BC,IAAY,KAAK,UAAU,cAC3BC,IAAgB,KAAK,eAAe,KAAK;AAE/C,QAAIF,KAAY,KAAKC,KAAa,GAAG;AACnC,WAAK,YAAY,GAAG,CAAC;AAErB;AAAA,IACF;AAEA,UAAME,IAAQD,GACRE,IAAQ,KAAK,OACbC,IAAeL,IAAWI,IAASD;AAEzC,QAAIG,IAAIN,GACJO,IAAIF;AAER,IAAIE,IAAIN,MACNM,IAAIN,GACJK,IAAKL,IAAYE,IAASC;AAG5B,UAAMI,IAAgB,KAAK,IAAI,GAAG,KAAK,MAAMF,CAAC,CAAC,GACzCG,IAAiB,KAAK,IAAI,GAAG,KAAK,MAAMF,CAAC,CAAC;AAEhD,SAAK,YAAYC,GAAeC,CAAc,GAC9C,KAAK,OAAA;AAAA,EACP;AAAA,EAEQ,oBAA0B;AAChC,SAAK,OAAO,MAAM,QAAQ,QAC1B,KAAK,OAAO,MAAM,SAAS,QAC3B,KAAK,OAAO,MAAM,YAAY;AAAA,EAChC;AAAA,EAEQ,YAAYC,GAAeC,GAAsB;AACvD,UAAMC,IAAkB,KAAK,IAAI,GAAG,KAAK,MAAMF,CAAK,CAAC,GAC/CG,IAAmB,KAAK,IAAI,GAAG,KAAK,MAAMF,CAAM,CAAC,GAEjDG,IAAM,KAAK,aAAA;AAEjB,SAAK,QAAQF,GACb,KAAK,SAASC,GACd,KAAK,MAAMC,GAEX,KAAK,OAAO,QAAQ,KAAK,MAAM,KAAK,QAAQ,KAAK,GAAG,GACpD,KAAK,OAAO,SAAS,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG,GAElD,KAAK,MACP,KAAK,GAAG,SAAS,GAAG,GAAG,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM,GAG9D,KAAK,kBAAA;AAAA,EACP;AAAA,EAEQ,eAAuB;AAC7B,UAAMC,IAAS,OAAO,oBAAoB,GACpC3G,IAAO,KAAK,IAAI,GAAG2G,CAAM;AAE/B,WAAO,KAAK,IAAI,KAAK,QAAQ3G,CAAI;AAAA,EACnC;AAAA,EAEQ,eAAqB;AAC3B,QAAI,CAAC,KAAK,UAAW;AAErB,SAAK,sBAAA,GACL,KAAK,iBAAiB;AAEtB,UAAMkE,IACH,KAAK,OAAO,WAAW,UAAU;AAAA,MAChC,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,CACR,KACA,KAAK,OAAO,WAAW,SAAS;AAAA,MAC/B,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,CACR,KACA,KAAK,OAAO,WAAW,sBAAsB;AAAA,MAC5C,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,CACR;AAEH,QAAI,CAACA,GAAI;AACP,YAAMxE,IAAW,SAAS,cAAc,QAAQ,GAC1CkH,IAAW,OAAO,wBAA0B,KAC5CC,IACHnH,EAAS,WAAW,QAAQ,KAC5BA,EAAS,WAAW,OAAO;AAE9B,WAAK,iBAAiB,kCAAkCkH,IAAW,QAAQ,IAAI,qBAAqBC,IAAa,QAAQ,IAAI,KAE7H,QAAQ,KAAK,mBAAmB,KAAK,cAAc;AAEnD;AAAA,IACF;AA6FA,UAAMC,IAAU,KAAK,mBAAmB5C,GA3FvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAUA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAiF6C;AAE9D,QAAI,CAAC4C,GAAS;AACZ,WAAK,iBAAiB,uDAEtB,QAAQ,KAAK,mBAAmB,KAAK,cAAc;AAEnD;AAAA,IACF;AAEA,UAAMC,IAAS7C,EAAG,aAAA;AAElB,QAAI,CAAC6C,GAAQ;AACX,WAAK,iBAAiB,kCAEtB,QAAQ,KAAK,mBAAmB,KAAK,cAAc,GAEnD7C,EAAG,cAAc4C,CAAO;AAExB;AAAA,IACF;AAEA,IAAA5C,EAAG,WAAWA,EAAG,cAAc6C,CAAM;AAErC,UAAMC,IAAO,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAElF,IAAA9C,EAAG,WAAWA,EAAG,cAAc8C,GAAM9C,EAAG,WAAW;AAEnD,UAAM+C,IAAU/C,EAAG,cAAA;AAEnB,QAAI,CAAC+C,GAAS;AACZ,WAAK,iBAAiB,4BAEtB,QAAQ,KAAK,mBAAmB,KAAK,cAAc,GAEnD/C,EAAG,aAAa6C,CAAM,GACtB7C,EAAG,cAAc4C,CAAO;AAExB;AAAA,IACF;AAEA,IAAA5C,EAAG,YAAYA,EAAG,YAAY+C,CAAO,GACrC/C,EAAG,YAAYA,EAAG,qBAAqB,CAAC,GACxCA,EAAG,WAAWA,EAAG,YAAY,GAAGA,EAAG,MAAMA,EAAG,MAAMA,EAAG,eAAe,KAAK,SAAS;AAElF,UAAMgD,IAAe,CAACnI,MAAuBA,IAAI,MAAMA,IAAKA,IAAI,OAAQ;AAGxE,IAFYmI,EAAa,KAAK,WAAW,KAAKA,EAAa,KAAK,YAAY,KAG1EhD,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBA,EAAG,MAAM,GAC5DA,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBA,EAAG,MAAM,GAC5DA,EAAG,eAAeA,EAAG,UAAU,GAC/BA,EAAG,cAAcA,EAAG,YAAYA,EAAG,oBAAoBA,EAAG,oBAAoB,GAC9EA,EAAG,cAAcA,EAAG,YAAYA,EAAG,oBAAoBA,EAAG,MAAM,MAEhEA,EAAG,cAAcA,EAAG,YAAYA,EAAG,oBAAoBA,EAAG,MAAM,GAChEA,EAAG,cAAcA,EAAG,YAAYA,EAAG,oBAAoBA,EAAG,MAAM,GAChEA,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBA,EAAG,aAAa,GACnEA,EAAG,cAAcA,EAAG,YAAYA,EAAG,gBAAgBA,EAAG,aAAa,IAGrEA,EAAG,WAAW4C,CAAO,GACrB5C,EAAG,WAAW,GAAG,GAAG,GAAG,CAAC,GACxBA,EAAG,OAAOA,EAAG,KAAK,GAClBA,EAAG,UAAUA,EAAG,WAAWA,EAAG,mBAAmB,GAEjD,KAAK,KAAKA,GACV,KAAK,YAAY4C,GACjB,KAAK,WAAWC,GAChB,KAAK,YAAYE,GACjB,KAAK,YAAY;AAAA,MACf,KAAK/C,EAAG,kBAAkB4C,GAAS,OAAO;AAAA,MAC1C,IAAI5C,EAAG,kBAAkB4C,GAAS,MAAM;AAAA,IAAA,GAE1C,KAAK,aAAa;AAAA,MAChB,SAAS5C,EAAG,mBAAmB4C,GAAS,OAAO;AAAA,MAC/C,OAAO5C,EAAG,mBAAmB4C,GAAS,SAAS;AAAA,MAC/C,OAAO5C,EAAG,mBAAmB4C,GAAS,SAAS;AAAA,MAC/C,UAAU5C,EAAG,mBAAmB4C,GAAS,YAAY;AAAA,MACrD,aAAa5C,EAAG,mBAAmB4C,GAAS,eAAe;AAAA,MAC3D,cAAc5C,EAAG,mBAAmB4C,GAAS,gBAAgB;AAAA,MAC7D,cAAc5C,EAAG,mBAAmB4C,GAAS,gBAAgB;AAAA,MAC7D,aAAa5C,EAAG,mBAAmB4C,GAAS,eAAe;AAAA,MAC3D,WAAW5C,EAAG,mBAAmB4C,GAAS,aAAa;AAAA,MACvD,WAAW5C,EAAG,mBAAmB4C,GAAS,aAAa;AAAA,MACvD,QAAQ5C,EAAG,mBAAmB4C,GAAS,UAAU;AAAA,IAAA;AAAA,EAErD;AAAA,EAEQ,mBACN5C,GACAiD,GACAC,GACqB;AACrB,UAAMC,IAAK,KAAK,kBAAkBnD,GAAIA,EAAG,eAAeiD,CAAQ,GAC1DG,IAAK,KAAK,kBAAkBpD,GAAIA,EAAG,iBAAiBkD,CAAQ;AAElE,QAAI,CAACC,KAAM,CAACC;AACV,aAAID,KAAInD,EAAG,aAAamD,CAAE,GACtBC,KAAIpD,EAAG,aAAaoD,CAAE,GAEnB;AAGT,UAAMR,IAAU5C,EAAG,cAAA;AAEnB,QAAI,CAAC4C,EAAS,QAAO;AAMrB,QAJA5C,EAAG,aAAa4C,GAASO,CAAE,GAC3BnD,EAAG,aAAa4C,GAASQ,CAAE,GAC3BpD,EAAG,YAAY4C,CAAO,GAElB,CAAC5C,EAAG,oBAAoB4C,GAAS5C,EAAG,WAAW,GAAG;AACpD,YAAMqD,IAAOrD,EAAG,kBAAkB4C,CAAO,KAAK;AAE9C,kBAAK,iBAAiB,wBAAwBS,CAAI,IAElD,QAAQ,KAAK,uCAAuCA,CAAI,GAExDrD,EAAG,cAAc4C,CAAO,GACxB5C,EAAG,aAAamD,CAAE,GAClBnD,EAAG,aAAaoD,CAAE,GAEX;AAAA,IACT;AAEA,WAAApD,EAAG,aAAa4C,GAASO,CAAE,GAC3BnD,EAAG,aAAa4C,GAASQ,CAAE,GAC3BpD,EAAG,aAAamD,CAAE,GAClBnD,EAAG,aAAaoD,CAAE,GAEXR;AAAA,EACT;AAAA,EAEQ,kBACN5C,GACAsD,GACAC,GACoB;AACpB,UAAMC,IAASxD,EAAG,aAAasD,CAAI;AAEnC,QAAI,CAACE,EAAQ,QAAO;AAKpB,QAHAxD,EAAG,aAAawD,GAAQD,CAAM,GAC9BvD,EAAG,cAAcwD,CAAM,GAEnB,CAACxD,EAAG,mBAAmBwD,GAAQxD,EAAG,cAAc,GAAG;AACrD,YAAMqD,IAAOrD,EAAG,iBAAiBwD,CAAM,KAAK;AAE5C,kBAAK,iBAAiB,0BAA0BH,CAAI,IAEpD,QAAQ,KAAK,yCAAyCA,CAAI,GAE1DrD,EAAG,aAAawD,CAAM,GAEf;AAAA,IACT;AAEA,WAAOA;AAAA,EACT;AAAA,EAEQ,wBAA8B;AACpC,IAAK,KAAK,OACN,KAAK,aAAW,KAAK,GAAG,cAAc,KAAK,SAAS,GACpD,KAAK,YAAU,KAAK,GAAG,aAAa,KAAK,QAAQ,GACjD,KAAK,aAAW,KAAK,GAAG,cAAc,KAAK,SAAS,GAExD,KAAK,YAAY,MACjB,KAAK,WAAW,MAChB,KAAK,YAAY,MACjB,KAAK,YAAY,MACjB,KAAK,aAAa,MAClB,KAAK,KAAK;AAAA,EACZ;AACF;"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
(function(d,g){typeof exports=="object"&&typeof module<"u"?g(exports):typeof define=="function"&&define.amd?define(["exports"],g):(d=typeof globalThis<"u"?globalThis:d||self,g(d.ReelDeal={}))})(this,(function(d){"use strict";const g={minSpins:2,durationMs:4e3,speedPxS:4e3,staggerMs:300},_={warpAngleDeg:90,edgePower:.5},P={maxPx:4,speedAtMax:2200},m={amplitudePx:8,speedHz:.25,phaseOffsetRad:.9,delayMs:500,rampMs:800},M=.1,S=.6,R=(a,e,i)=>Math.max(e,Math.min(i,Math.floor(a))),T=(a,e)=>(a%e+e)%e,v=a=>Math.max(0,Math.min(1,a)),C=a=>{const e=v(a);return e**2*(2-e)},G=a=>{const e=v(a);return e+e**2-e**3},X=a=>{const e=v(a);if(e<=M){const s=e/M;return C(s)*M}if(e<S)return e;const i=(e-S)/(1-S);return S+G(i)*(1-S)},E=(a,e)=>typeof a=="number"&&Number.isFinite(a)?a:e,b=(a,e)=>Math.max(0,E(a,e)),x=(a,e)=>Math.max(0,Math.floor(E(a,e))),z=2,U=a=>{const e={...g,...a??{}};return{minSpins:x(e.minSpins,g.minSpins),durationMs:x(e.durationMs,g.durationMs??0),speedPxS:x(e.speedPxS,g.speedPxS??0),staggerMs:x(e.staggerMs,g.staggerMs??0)}},N=a=>{const e={...P,...a??{}};return{maxPx:b(e.maxPx,P.maxPx),speedAtMax:b(e.speedAtMax,P.speedAtMax)}},F=a=>{const e={..._,...a??{}};return{warpAngleDeg:b(e.warpAngleDeg,_.warpAngleDeg),edgePower:b(e.edgePower,_.edgePower)}},V=a=>{if(a===!1)return null;const e={...m,...a??{}};return{amplitudePx:b(e.amplitudePx,m.amplitudePx),speedHz:b(e.speedHz,m.speedHz),phaseOffsetRad:E(e.phaseOffsetRad,m.phaseOffsetRad),delayMs:x(e.delayMs,m.delayMs),rampMs:x(e.rampMs,m.rampMs)}},k=a=>Math.max(1,E(a,z)),Y=(a,e)=>{const i=document.getElementById(a);if(!i)throw new Error(`${e} not found: ${a}`);return i},y=(a,e)=>typeof a=="string"?Y(a,e):a,q=5,W=.72,$=.28,Q=7,O=.85,K=1-O;class j{canvas;container;opts;visibleSlots=3;heightScale=2/3;centerIndex=1;reels;spinConfig;spinBlur;webglShading;idleBob;maxDpr;spriteImg=null;spriteWidth=0;spriteHeight=0;frameHeight=0;width=0;height=0;dpr=1;offsets;velocities;lastVelT=null;lastVelOffsets;idleStartTime=null;idleBaseOffsets;gl=null;glProgram=null;glBuffer=null;glTexture=null;glAttribs=null;webglInitError=null;glUniforms=null;anim=null;rafId=null;pendingSpinResolvers=[];resizeObserver=null;resizeQueued=!1;button=null;buttonHandlerAttached=!1;spinning=!1;queuedSpins=null;constructor(e){this.opts=e,this.reels=R(e.reels??1,1,q),this.spinConfig=U(e.spinConfig),this.spinBlur=N(e.spinBlur),this.webglShading=F(e.webglShading),this.idleBob=V(e.idleBob),this.maxDpr=k(e.maxDpr),this.canvas=y(e.canvas,"Canvas"),this.container=y(e.container,"Container"),this.offsets=new Array(this.reels).fill(0),this.velocities=new Array(this.reels).fill(0),this.lastVelOffsets=new Array(this.reels).fill(0),this.idleBaseOffsets=new Array(this.reels).fill(0)}async init(){if(await this.loadSprite(),this.tryInitWebGL(),!this.gl)throw new Error(`WebGL is not supported or failed to initialize. ${this.webglInitError??""}`.trim());this.syncContainerLayoutVars(),this.ensureCanvasCoversContainer(),this.setupResizeWatcher(),this.resizeToContainer(),this.setSegments(this.opts.initialSegments),this.button=this.resolveButton(),this.queuedSpins=this.resolveSpinQueue(),this.attachButtonHandler(),this.render(),this.startLoop()}destroy(){this.stop(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.button&&this.buttonHandlerAttached&&(this.button.removeEventListener("click",this.handleButtonClick),this.buttonHandlerAttached=!1),this.releaseWebGLResources()}ensureCanvasCoversContainer(){this.canvas!==this.container&&this.container.contains(this.canvas)&&(this.canvas.style.width||(this.canvas.style.width="100%"),this.canvas.style.height||(this.canvas.style.height="100%"),this.canvas.style.display||(this.canvas.style.display="block"))}syncContainerLayoutVars(){const e=this.canvas===this.container?this.canvas:this.container;e.style.setProperty("--reels",String(this.reels)),e.style.setProperty("--visible-slots",String(this.visibleSlots)),e.style.aspectRatio=`${this.reels} / ${this.visibleSlots*this.heightScale}`,this.canvas!==this.container&&(this.container.style.overflow="hidden")}setSegments(e){this.ensureReady();const i=e??[];for(let s=0;s<this.reels;s+=1){const t=i[s]??0,r=R(t,0,this.opts.slotCount-1);this.offsets[s]=r*this.frameHeight-this.centerIndex*this.frameHeight,this.lastVelOffsets[s]=this.offsets[s],this.velocities[s]=0,this.idleBaseOffsets[s]=this.offsets[s]}this.lastVelT=null,this.idleStartTime=null,this.anim=null,this.stop(),this.render(),this.startLoop()}setSegment(e){this.setSegments(new Array(this.reels).fill(e))}spinOnce(e,i){this.ensureReady();const{minSpins:s,staggerMs:t,speedPxS:r,durationMs:o}=this.getSpinConfig(i),n=r>0,c=new Array(this.reels),f=new Array(this.reels),u=new Array(this.reels);for(let l=0;l<this.reels;l+=1){const I=R(e[l]??0,0,this.opts.slotCount-1)*this.frameHeight-this.centerIndex*this.frameHeight,L=T(I,this.spriteHeight),J=T(this.offsets[l],this.spriteHeight),B=T(J-L,this.spriteHeight);let w=-(s*this.spriteHeight+B);if(n&&o>0){const D=r*o/1e3,H=Math.max(s,Math.ceil(Math.max(0,D-B)/this.spriteHeight));w=-(B+H*this.spriteHeight)}if(t>0&&n){const D=r*t*l/1e3,H=Math.ceil(D/this.spriteHeight);w-=H*this.spriteHeight}c[l]=this.offsets[l],f[l]=w,n?u[l]=Math.max(0,Math.round(Math.abs(w)/r*1e3)):u[l]=Math.max(0,o+t*l)}if(f.reduce((l,A)=>l+Math.abs(A),0)===0||u.every(l=>l<=0)){for(let l=0;l<this.reels;l+=1)this.offsets[l]=this.offsets[l]+f[l];return this.anim=null,this.resolvePendingSpins(),this.render(),Promise.resolve()}const h=performance.now();return this.anim={startTime:h,startOffsets:c,deltas:f,durationMs:u},this.idleStartTime=null,this.startLoop(),new Promise(l=>{this.pendingSpinResolvers.push(l)})}getSpinConfig(e){return e?U({...this.spinConfig,...e}):this.spinConfig}async spinQueue(e){for(let i=0;i<e.length;i+=1){const s=e[i];await this.spinOnce(s.stopAtSegments),s.callback?.(i,s.stopAtSegments)}}stop(){this.rafId!==null&&cancelAnimationFrame(this.rafId),this.rafId=null,this.anim=null,this.resolvePendingSpins(),this.lastVelT=null;for(let e=0;e<this.reels;e+=1)this.velocities[e]=0}resolvePendingSpins(){if(this.pendingSpinResolvers.length===0)return;const e=this.pendingSpinResolvers;this.pendingSpinResolvers=[];for(const i of e)i()}requestResize(){this.queueResize()}ensureReady(){if(!this.spriteImg||this.spriteWidth<=0||this.spriteHeight<=0||this.frameHeight<=0)throw new Error("Sprite is not ready. Call init() and await it first.")}attachButtonHandler(){!this.button||this.buttonHandlerAttached||(this.button.addEventListener("click",this.handleButtonClick,{passive:!0}),this.buttonHandlerAttached=!0)}handleButtonClick=async()=>{if(this.spinning)return;const e=this.consumeNextSpin()??{stopAtSegments:this.buildRandomSegments()};this.spinning=!0,this.setButtonDisabled(!0);try{await this.spinOnce(e.stopAtSegments)}finally{this.queuedSpins&&this.queuedSpins.length===0?this.setButtonDisabled(!0):this.setButtonDisabled(!1),this.spinning=!1}};resolveButton(){return this.opts.button?y(this.opts.button,"Button"):null}resolveSpinQueue(){return this.opts.queuedSpinStates??null}consumeNextSpin(){return this.queuedSpins||(this.queuedSpins=this.resolveSpinQueue()),!this.queuedSpins||this.queuedSpins.length===0?null:this.queuedSpins.shift()??null}buildRandomSegments(){const e=Math.max(1,Math.floor(this.opts.slotCount)),i=[];for(let s=0;s<this.reels;s+=1)i.push(Math.floor(Math.random()*e));return i}setButtonDisabled(e){this.button&&(this.button.disabled=e,e?this.button.setAttribute("aria-busy","true"):this.button.removeAttribute("aria-busy"))}shouldAnimate(){return this.anim!==null||this.idleBob!==null}startLoop(){this.rafId===null&&this.shouldAnimate()&&(this.rafId=requestAnimationFrame(this.loop))}loop=e=>{if(this.anim){const{startTime:i,durationMs:s,startOffsets:t,deltas:r}=this.anim;let o=!0;for(let n=0;n<this.reels;n+=1){const c=s[n]??0,f=c<=0?1:Math.min(1,(e-i)/c),u=X(f);f<1&&(o=!1);const p=t[n]+r[n]*u;let h=0;if(f>W&&this.frameHeight>0){const A=(1-v((f-W)/$))**2,I=p/this.frameHeight*Math.PI*2,L=Math.min(Q,this.frameHeight*.06);h=Math.sin(I)*A*L}this.offsets[n]=p+h}if(this.updateVelocity(e),this.render(),o){for(let n=0;n<this.reels;n+=1)this.offsets[n]=this.anim.startOffsets[n]+this.anim.deltas[n],this.idleBaseOffsets[n]=this.offsets[n];this.updateVelocity(e);for(let n=0;n<this.reels;n+=1)this.velocities[n]=0;this.anim=null,this.idleStartTime=null,this.render(),this.resolvePendingSpins()}}else this.idleBob&&(this.updateIdle(e),this.render());if(!this.shouldAnimate()){this.rafId=null;return}this.rafId!==null&&(this.rafId=requestAnimationFrame(this.loop))};render(){this.spriteImg&&(this.width<=0||this.height<=0||this.gl&&this.renderWebGL())}getSpinBlurPxForReel(e){if(!this.anim)return 0;const i=Math.abs(this.velocities[e]??0),s=Math.max(1,this.spinBlur.speedAtMax);return v(i/s)*Math.max(0,this.spinBlur.maxPx)}renderWebGL(){if(!this.gl||!this.glProgram||!this.glAttribs||!this.glUniforms||!this.glTexture||!this.glBuffer||!this.spriteImg)return;const e=this.gl;e.useProgram(this.glProgram),e.bindBuffer(e.ARRAY_BUFFER,this.glBuffer),e.enableVertexAttribArray(this.glAttribs.pos),e.vertexAttribPointer(this.glAttribs.pos,2,e.FLOAT,!1,16,0),e.enableVertexAttribArray(this.glAttribs.uv),e.vertexAttribPointer(this.glAttribs.uv,2,e.FLOAT,!1,16,8),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.glTexture),e.uniform1i(this.glUniforms.texture,0),e.uniform1f(this.glUniforms.frameHeight,this.frameHeight),e.uniform1f(this.glUniforms.spriteHeight,this.spriteHeight),e.uniform1f(this.glUniforms.visibleSlots,this.visibleSlots),e.uniform1f(this.glUniforms.heightScale,this.heightScale),e.uniform1f(this.glUniforms.edgePower,Math.max(.5,this.webglShading.edgePower));const i=this.webglShading,s=Math.max(0,i.warpAngleDeg)*Math.PI/180;e.uniform1f(this.glUniforms.warpAngle,s),e.clear(e.COLOR_BUFFER_BIT);const t=1/this.reels;for(let r=0;r<this.reels;r+=1){e.uniform1f(this.glUniforms.reelX,r*t),e.uniform1f(this.glUniforms.reelW,t);const o=T(this.offsets[r],this.spriteHeight);e.uniform1f(this.glUniforms.offsetPx,o),e.uniform1f(this.glUniforms.blurPx,this.getSpinBlurPxForReel(r)),e.drawArrays(e.TRIANGLE_STRIP,0,4)}}updateVelocity(e){if(this.lastVelT===null){this.lastVelT=e;for(let s=0;s<this.reels;s+=1)this.lastVelOffsets[s]=this.offsets[s],this.velocities[s]=0;return}const i=(e-this.lastVelT)/1e3;if(!(i<=0)){for(let s=0;s<this.reels;s+=1){const t=(this.offsets[s]-this.lastVelOffsets[s])/i;this.velocities[s]=this.velocities[s]*O+t*K,this.lastVelOffsets[s]=this.offsets[s]}this.lastVelT=e}}updateIdle(e){if(!this.idleBob)return;this.idleStartTime===null&&(this.idleStartTime=e);const{amplitudePx:i,speedHz:s,phaseOffsetRad:t,delayMs:r,rampMs:o}=this.idleBob,n=e-this.idleStartTime;if(n<Math.max(0,r)){for(let h=0;h<this.reels;h+=1)this.offsets[h]=this.idleBaseOffsets[h],this.velocities[h]=0;return}const c=(n-Math.max(0,r))/1e3,f=Math.max(0,Math.min(1,(n-Math.max(0,r))/Math.max(1,o))),u=C(f),p=s*Math.PI*2;for(let h=0;h<this.reels;h+=1){const l=c*p+h*t;this.offsets[h]=this.idleBaseOffsets[h]+Math.sin(l)*(i*u),this.velocities[h]=0}}async loadSprite(){const{sprite:e}=this.opts,i=typeof e=="string"?new Image:e;typeof e=="string"&&(i.src=e),(!i.complete||i.naturalWidth<=0)&&await new Promise((f,u)=>{const p=()=>f(),h=()=>u(new Error("Failed to load sprite image"));i.addEventListener("load",p,{once:!0}),i.addEventListener("error",h,{once:!0})});const s=Math.max(1,Math.floor(this.opts.slotCount)),t=i.naturalWidth,r=t,o=r*s,n=i.naturalHeight;if(Math.abs(n-o)>2)throw new Error(`Sprite size mismatch. Expected height ~${Math.round(o)}px (slotCount=${s}, slot aspect 1/1), got ${n}px.`);this.spriteImg=i,this.spriteWidth=t,this.frameHeight=Math.max(1,Math.round(r)),this.spriteHeight=Math.max(1,Math.round(this.frameHeight*s))}setupResizeWatcher(){typeof ResizeObserver>"u"||this.resizeObserver||(this.resizeObserver=new ResizeObserver(()=>this.queueResize()),this.resizeObserver.observe(this.container))}queueResize=()=>{this.resizeQueued||(this.resizeQueued=!0,requestAnimationFrame(()=>{this.resizeQueued=!1,this.resizeToContainer()}))};resizeToContainer(){const e=this.container.clientWidth,i=this.container.clientHeight,s=this.visibleSlots*this.heightScale;if(e<=0||i<=0){this.applyResize(1,1);return}const t=s,r=this.reels,o=e/r*t;let n=e,c=o;c>i&&(c=i,n=i/t*r);const f=Math.max(1,Math.floor(n)),u=Math.max(1,Math.floor(c));this.applyResize(f,u),this.render()}applyViewportCrop(){this.canvas.style.width="100%",this.canvas.style.height="100%",this.canvas.style.transform=""}applyResize(e,i){const s=Math.max(1,Math.floor(e)),t=Math.max(1,Math.floor(i)),r=this.getTargetDpr();this.width=s,this.height=t,this.dpr=r,this.canvas.width=Math.floor(this.width*this.dpr),this.canvas.height=Math.floor(this.height*this.dpr),this.gl&&this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.applyViewportCrop()}getTargetDpr(){const e=window.devicePixelRatio||1,i=Math.max(1,e);return Math.min(this.maxDpr,i)}tryInitWebGL(){if(!this.spriteImg)return;this.releaseWebGLResources(),this.webglInitError=null;const e=this.canvas.getContext("webgl2",{antialias:!0,alpha:!0})??this.canvas.getContext("webgl",{antialias:!0,alpha:!0})??this.canvas.getContext("experimental-webgl",{antialias:!0,alpha:!0});if(!e){const u=document.createElement("canvas"),p=typeof WebGLRenderingContext<"u",h=u.getContext("webgl2")??u.getContext("webgl");this.webglInitError=`context is null (browser WebGL=${p?"yes":"no"}, fallback canvas=${h?"yes":"no"})`,console.warn("ReelDeal WebGL:",this.webglInitError);return}const t=this.createWebGLProgram(e,`
|
|
2
|
+
attribute vec2 a_pos;
|
|
3
|
+
attribute vec2 a_uv;
|
|
4
|
+
varying vec2 v_uv;
|
|
5
|
+
void main() {
|
|
6
|
+
v_uv = a_uv;
|
|
7
|
+
gl_Position = vec4(a_pos, 0.0, 1.0);
|
|
8
|
+
}
|
|
9
|
+
`,`
|
|
10
|
+
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
|
11
|
+
precision highp float;
|
|
12
|
+
#else
|
|
13
|
+
precision mediump float;
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
varying vec2 v_uv;
|
|
17
|
+
uniform sampler2D u_tex;
|
|
18
|
+
uniform float u_reelX;
|
|
19
|
+
uniform float u_reelW;
|
|
20
|
+
uniform float u_offsetPx;
|
|
21
|
+
uniform float u_frameHeight;
|
|
22
|
+
uniform float u_spriteHeight;
|
|
23
|
+
uniform float u_visibleSlots;
|
|
24
|
+
uniform float u_heightScale;
|
|
25
|
+
uniform float u_edgePower;
|
|
26
|
+
uniform float u_warpAngle;
|
|
27
|
+
uniform float u_blurPx;
|
|
28
|
+
|
|
29
|
+
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
|
30
|
+
|
|
31
|
+
void main() {
|
|
32
|
+
if (v_uv.x < u_reelX || v_uv.x > (u_reelX + u_reelW)) {
|
|
33
|
+
discard;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
float localX = (v_uv.x - u_reelX) / u_reelW;
|
|
37
|
+
// v_uv.y is authored as 0 at TOP, 1 at BOTTOM (Canvas-like).
|
|
38
|
+
float localY = v_uv.y;
|
|
39
|
+
float hs = max(0.0001, u_heightScale);
|
|
40
|
+
float fullY = localY * hs + (1.0 - hs) * 0.5;
|
|
41
|
+
|
|
42
|
+
float warp = max(0.0, u_warpAngle);
|
|
43
|
+
float warpStrength = saturate(warp / 1.2);
|
|
44
|
+
float center = 0.5;
|
|
45
|
+
float centerBand = 1.0 / 3.0;
|
|
46
|
+
float halfBand = centerBand * 0.5;
|
|
47
|
+
float dist = abs(fullY - center);
|
|
48
|
+
// Smoother transition: avoid a hard boundary at halfBand.
|
|
49
|
+
float edgeWeight = smoothstep(halfBand, 0.5, dist);
|
|
50
|
+
float edgePow = max(0.5, u_edgePower);
|
|
51
|
+
edgeWeight = pow(edgeWeight, edgePow) * warpStrength;
|
|
52
|
+
float tEase = smoothstep(0.0, 1.0, fullY);
|
|
53
|
+
float warpedY = mix(fullY, tEase, edgeWeight);
|
|
54
|
+
float y = (warpedY - 0.5) * 2.0;
|
|
55
|
+
float theta = y * warp;
|
|
56
|
+
float edgeWarp = edgeWeight;
|
|
57
|
+
|
|
58
|
+
float yPx = u_offsetPx + warpedY * (u_frameHeight * u_visibleSlots);
|
|
59
|
+
// With UNPACK_FLIP_Y_WEBGL=0 and Canvas-like UVs (v=0 at TOP),
|
|
60
|
+
// v=0 samples the TOP of the original image, so pixel Y-from-top maps directly to v.
|
|
61
|
+
// Normalize early to improve precision on mobile GPUs.
|
|
62
|
+
float v = fract((u_offsetPx / u_spriteHeight) +
|
|
63
|
+
warpedY * ((u_frameHeight * u_visibleSlots) / u_spriteHeight));
|
|
64
|
+
|
|
65
|
+
vec4 base = texture2D(u_tex, vec2(localX, v));
|
|
66
|
+
|
|
67
|
+
if (u_blurPx > 0.0) {
|
|
68
|
+
// Clamp blur step to reduce banding on mobile GPUs.
|
|
69
|
+
// Scale max step with speed proxy (u_blurPx).
|
|
70
|
+
float maxStepPx = 1.0 + 3.0 * saturate(u_blurPx / 4.0);
|
|
71
|
+
float blurPx = min(u_blurPx, maxStepPx);
|
|
72
|
+
float blurStep = blurPx / max(1.0, u_spriteHeight);
|
|
73
|
+
// 9-tap gaussian-ish blur to reduce banding on mobile.
|
|
74
|
+
vec4 sum = base * 0.227027;
|
|
75
|
+
sum += texture2D(u_tex, vec2(localX, fract(v + blurStep))) * 0.1945946;
|
|
76
|
+
sum += texture2D(u_tex, vec2(localX, fract(v - blurStep))) * 0.1945946;
|
|
77
|
+
sum += texture2D(u_tex, vec2(localX, fract(v + 2.0 * blurStep))) * 0.1216216;
|
|
78
|
+
sum += texture2D(u_tex, vec2(localX, fract(v - 2.0 * blurStep))) * 0.1216216;
|
|
79
|
+
sum += texture2D(u_tex, vec2(localX, fract(v + 3.0 * blurStep))) * 0.054054;
|
|
80
|
+
sum += texture2D(u_tex, vec2(localX, fract(v - 3.0 * blurStep))) * 0.054054;
|
|
81
|
+
sum += texture2D(u_tex, vec2(localX, fract(v + 4.0 * blurStep))) * 0.016216;
|
|
82
|
+
sum += texture2D(u_tex, vec2(localX, fract(v - 4.0 * blurStep))) * 0.016216;
|
|
83
|
+
base = sum;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
gl_FragColor = base;
|
|
87
|
+
}
|
|
88
|
+
`);if(!t){this.webglInitError="shader program failed to compile/link (see console)",console.warn("ReelDeal WebGL:",this.webglInitError);return}const r=e.createBuffer();if(!r){this.webglInitError="failed to create vertex buffer",console.warn("ReelDeal WebGL:",this.webglInitError),e.deleteProgram(t);return}e.bindBuffer(e.ARRAY_BUFFER,r);const o=new Float32Array([-1,-1,0,1,1,-1,1,1,-1,1,0,0,1,1,1,0]);e.bufferData(e.ARRAY_BUFFER,o,e.STATIC_DRAW);const n=e.createTexture();if(!n){this.webglInitError="failed to create texture",console.warn("ReelDeal WebGL:",this.webglInitError),e.deleteBuffer(r),e.deleteProgram(t);return}e.bindTexture(e.TEXTURE_2D,n),e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL,0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,this.spriteImg);const c=u=>u>0&&(u&u-1)===0;c(this.spriteWidth)&&c(this.spriteHeight)?(e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.REPEAT),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.REPEAT),e.generateMipmap(e.TEXTURE_2D),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR)):(e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE)),e.useProgram(t),e.clearColor(0,0,0,0),e.enable(e.BLEND),e.blendFunc(e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),this.gl=e,this.glProgram=t,this.glBuffer=r,this.glTexture=n,this.glAttribs={pos:e.getAttribLocation(t,"a_pos"),uv:e.getAttribLocation(t,"a_uv")},this.glUniforms={texture:e.getUniformLocation(t,"u_tex"),reelX:e.getUniformLocation(t,"u_reelX"),reelW:e.getUniformLocation(t,"u_reelW"),offsetPx:e.getUniformLocation(t,"u_offsetPx"),frameHeight:e.getUniformLocation(t,"u_frameHeight"),spriteHeight:e.getUniformLocation(t,"u_spriteHeight"),visibleSlots:e.getUniformLocation(t,"u_visibleSlots"),heightScale:e.getUniformLocation(t,"u_heightScale"),edgePower:e.getUniformLocation(t,"u_edgePower"),warpAngle:e.getUniformLocation(t,"u_warpAngle"),blurPx:e.getUniformLocation(t,"u_blurPx")}}createWebGLProgram(e,i,s){const t=this.createWebGLShader(e,e.VERTEX_SHADER,i),r=this.createWebGLShader(e,e.FRAGMENT_SHADER,s);if(!t||!r)return t&&e.deleteShader(t),r&&e.deleteShader(r),null;const o=e.createProgram();if(!o)return null;if(e.attachShader(o,t),e.attachShader(o,r),e.linkProgram(o),!e.getProgramParameter(o,e.LINK_STATUS)){const n=e.getProgramInfoLog(o)??"unknown link error";return this.webglInitError=`program link failed: ${n}`,console.warn("ReelDeal WebGL: program link failed",n),e.deleteProgram(o),e.deleteShader(t),e.deleteShader(r),null}return e.detachShader(o,t),e.detachShader(o,r),e.deleteShader(t),e.deleteShader(r),o}createWebGLShader(e,i,s){const t=e.createShader(i);if(!t)return null;if(e.shaderSource(t,s),e.compileShader(t),!e.getShaderParameter(t,e.COMPILE_STATUS)){const r=e.getShaderInfoLog(t)??"unknown compile error";return this.webglInitError=`shader compile failed: ${r}`,console.warn("ReelDeal WebGL: shader compile failed",r),e.deleteShader(t),null}return t}releaseWebGLResources(){this.gl&&(this.glTexture&&this.gl.deleteTexture(this.glTexture),this.glBuffer&&this.gl.deleteBuffer(this.glBuffer),this.glProgram&&this.gl.deleteProgram(this.glProgram),this.glTexture=null,this.glBuffer=null,this.glProgram=null,this.glAttribs=null,this.glUniforms=null,this.gl=null)}}d.ReelDeal=j,d.defaultIdleBob=m,d.defaultSpinBlur=P,d.defaultSpinConfig=g,d.defaultWebGLShading=_,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
|
89
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/defaults.ts","../src/utils/math.ts","../src/normalize.ts","../src/utils/dom.ts","../src/ReelDeal.ts"],"sourcesContent":["import type {\n ReelDealIdleBob,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealWebGLShading,\n} from './types';\n\nexport const defaultSpinConfig: ReelDealSpinConfig = {\n minSpins: 2,\n durationMs: 4000,\n speedPxS: 4000,\n staggerMs: 300,\n};\n\nexport const defaultWebGLShading: ReelDealWebGLShading = {\n warpAngleDeg: 90,\n edgePower: 0.5,\n};\n\nexport const defaultSpinBlur: ReelDealSpinBlur = {\n maxPx: 4,\n speedAtMax: 2200,\n};\n\nexport const defaultIdleBob: ReelDealIdleBob = {\n amplitudePx: 8,\n speedHz: 0.25,\n phaseOffsetRad: 0.9,\n delayMs: 500,\n rampMs: 800,\n};\n","const SPIN_ACCEL_END = 0.1;\nconst SPIN_DECEL_START = 0.6;\n\nexport const clampInt = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, Math.floor(value)));\n\nexport const mod = (n: number, m: number): number => ((n % m) + m) % m;\n\nexport const clamp01 = (t: number): number => Math.max(0, Math.min(1, t));\n\nexport const rampHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x ** 2 * (2 - x);\n};\n\nexport const brakeHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x + x ** 2 - x ** 3;\n};\n\nexport const reelProgress = (t: number): number => {\n const tt = clamp01(t);\n\n if (tt <= SPIN_ACCEL_END) {\n const u = tt / SPIN_ACCEL_END;\n\n return rampHermite(u) * SPIN_ACCEL_END;\n }\n\n if (tt < SPIN_DECEL_START) {\n return tt;\n }\n\n const u = (tt - SPIN_DECEL_START) / (1 - SPIN_DECEL_START);\n\n return SPIN_DECEL_START + brakeHermite(u) * (1 - SPIN_DECEL_START);\n};\n\nexport const finiteOr = (value: number | undefined, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n\nexport const nonNegative = (value: number | undefined, fallback: number): number =>\n Math.max(0, finiteOr(value, fallback));\n\nexport const nonNegativeInt = (value: number | undefined, fallback: number): number =>\n Math.max(0, Math.floor(finiteOr(value, fallback)));\n","import {\n defaultIdleBob,\n defaultSpinBlur,\n defaultSpinConfig,\n defaultWebGLShading,\n} from './defaults';\nimport type {\n ReelDealIdleBob,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealWebGLShading,\n} from './types';\nimport { finiteOr, nonNegative, nonNegativeInt } from './utils/math';\n\nconst DEFAULT_MAX_DPR = 2;\n\nexport type NormalizedSpinConfig = Required<ReelDealSpinConfig>;\n\nexport const normalizeSpinConfig = (\n overrides?: Partial<ReelDealSpinConfig>,\n): NormalizedSpinConfig => {\n const base = { ...defaultSpinConfig, ...(overrides ?? {}) };\n\n return {\n minSpins: nonNegativeInt(base.minSpins, defaultSpinConfig.minSpins),\n durationMs: nonNegativeInt(base.durationMs, defaultSpinConfig.durationMs ?? 0),\n speedPxS: nonNegativeInt(base.speedPxS, defaultSpinConfig.speedPxS ?? 0),\n staggerMs: nonNegativeInt(base.staggerMs, defaultSpinConfig.staggerMs ?? 0),\n };\n};\n\nexport const normalizeSpinBlur = (overrides?: Partial<ReelDealSpinBlur>): ReelDealSpinBlur => {\n const base = { ...defaultSpinBlur, ...(overrides ?? {}) };\n\n return {\n maxPx: nonNegative(base.maxPx, defaultSpinBlur.maxPx),\n speedAtMax: nonNegative(base.speedAtMax, defaultSpinBlur.speedAtMax),\n };\n};\n\nexport const normalizeWebGLShading = (\n overrides?: Partial<ReelDealWebGLShading>,\n): ReelDealWebGLShading => {\n const base = { ...defaultWebGLShading, ...(overrides ?? {}) };\n\n return {\n warpAngleDeg: nonNegative(base.warpAngleDeg, defaultWebGLShading.warpAngleDeg),\n edgePower: nonNegative(base.edgePower, defaultWebGLShading.edgePower),\n };\n};\n\nexport const normalizeIdleBob = (\n overrides?: Partial<ReelDealIdleBob> | false,\n): ReelDealIdleBob | null => {\n if (overrides === false) return null;\n\n const base = { ...defaultIdleBob, ...(overrides ?? {}) };\n\n return {\n amplitudePx: nonNegative(base.amplitudePx, defaultIdleBob.amplitudePx),\n speedHz: nonNegative(base.speedHz, defaultIdleBob.speedHz),\n phaseOffsetRad: finiteOr(base.phaseOffsetRad, defaultIdleBob.phaseOffsetRad),\n delayMs: nonNegativeInt(base.delayMs, defaultIdleBob.delayMs),\n rampMs: nonNegativeInt(base.rampMs, defaultIdleBob.rampMs),\n };\n};\n\nexport const normalizeMaxDpr = (value?: number): number =>\n Math.max(1, finiteOr(value, DEFAULT_MAX_DPR));\n","export const resolveElementById = <T extends HTMLElement>(id: string, kind: string): T => {\n const el = document.getElementById(id);\n\n if (!el) {\n throw new Error(`${kind} not found: ${id}`);\n }\n\n return el as T;\n};\n\nexport const resolveElement = <T extends HTMLElement>(elOrId: T | string, kind: string): T => {\n if (typeof elOrId === 'string') return resolveElementById<T>(elOrId, kind);\n\n return elOrId;\n};\n","import {\n type NormalizedSpinConfig,\n normalizeIdleBob,\n normalizeMaxDpr,\n normalizeSpinBlur,\n normalizeSpinConfig,\n normalizeWebGLShading,\n} from './normalize';\nimport type {\n ReelDealIdleBob,\n ReelDealOptions,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealSpinState,\n ReelDealWebGLShading,\n} from './types';\nimport { resolveElement } from './utils/dom';\nimport { clamp01, clampInt, mod, rampHermite, reelProgress } from './utils/math';\n\nconst MAX_REELS = 5;\nconst SPIN_TICK_START = 0.72;\nconst SPIN_TICK_RANGE = 0.28;\nconst SPIN_TICK_MAX_PX = 7;\nconst VELOCITY_DECAY = 0.85;\nconst VELOCITY_GAIN = 1 - VELOCITY_DECAY;\n\ntype SpinAnimation = {\n startTime: number;\n startOffsets: number[];\n deltas: number[];\n durationMs: number[];\n};\n\nexport class ReelDeal {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n\n private readonly opts: ReelDealOptions;\n private readonly visibleSlots = 3;\n private readonly heightScale = 2 / 3;\n private readonly centerIndex = 1;\n private readonly reels: number;\n private readonly spinConfig: NormalizedSpinConfig;\n private readonly spinBlur: ReelDealSpinBlur;\n private readonly webglShading: ReelDealWebGLShading;\n private readonly idleBob: ReelDealIdleBob | null;\n private readonly maxDpr: number;\n\n private spriteImg: HTMLImageElement | null = null;\n private spriteWidth = 0;\n private spriteHeight = 0;\n private frameHeight = 0;\n\n private width = 0;\n private height = 0;\n private dpr = 1;\n\n private offsets: number[];\n private velocities: number[];\n private lastVelT: number | null = null;\n private lastVelOffsets: number[];\n private idleStartTime: number | null = null;\n private idleBaseOffsets: number[];\n\n private gl: WebGLRenderingContext | WebGL2RenderingContext | null = null;\n private glProgram: WebGLProgram | null = null;\n private glBuffer: WebGLBuffer | null = null;\n private glTexture: WebGLTexture | null = null;\n private glAttribs: { pos: number; uv: number } | null = null;\n private webglInitError: string | null = null;\n private glUniforms: {\n texture: WebGLUniformLocation | null;\n reelX: WebGLUniformLocation | null;\n reelW: WebGLUniformLocation | null;\n offsetPx: WebGLUniformLocation | null;\n frameHeight: WebGLUniformLocation | null;\n spriteHeight: WebGLUniformLocation | null;\n visibleSlots: WebGLUniformLocation | null;\n heightScale: WebGLUniformLocation | null;\n edgePower: WebGLUniformLocation | null;\n warpAngle: WebGLUniformLocation | null;\n blurPx: WebGLUniformLocation | null;\n } | null = null;\n\n private anim: SpinAnimation | null = null;\n private rafId: number | null = null;\n private pendingSpinResolvers: Array<() => void> = [];\n\n private resizeObserver: ResizeObserver | null = null;\n private resizeQueued = false;\n\n private button: HTMLButtonElement | null = null;\n private buttonHandlerAttached = false;\n private spinning = false;\n private queuedSpins: ReelDealSpinState[] | null = null;\n\n constructor(options: ReelDealOptions) {\n this.opts = options;\n this.reels = clampInt(options.reels ?? 1, 1, MAX_REELS);\n this.spinConfig = normalizeSpinConfig(options.spinConfig);\n this.spinBlur = normalizeSpinBlur(options.spinBlur);\n this.webglShading = normalizeWebGLShading(options.webglShading);\n this.idleBob = normalizeIdleBob(options.idleBob);\n this.maxDpr = normalizeMaxDpr(options.maxDpr);\n\n this.canvas = resolveElement<HTMLCanvasElement>(options.canvas, 'Canvas');\n this.container = resolveElement<HTMLElement>(options.container, 'Container');\n\n this.offsets = new Array(this.reels).fill(0);\n this.velocities = new Array(this.reels).fill(0);\n this.lastVelOffsets = new Array(this.reels).fill(0);\n this.idleBaseOffsets = new Array(this.reels).fill(0);\n }\n\n async init(): Promise<void> {\n await this.loadSprite();\n\n this.tryInitWebGL();\n\n if (!this.gl) {\n throw new Error(\n `WebGL is not supported or failed to initialize. ${this.webglInitError ?? ''}`.trim(),\n );\n }\n\n this.syncContainerLayoutVars();\n this.ensureCanvasCoversContainer();\n this.setupResizeWatcher();\n this.resizeToContainer();\n\n this.setSegments(this.opts.initialSegments);\n\n this.button = this.resolveButton();\n this.queuedSpins = this.resolveSpinQueue();\n this.attachButtonHandler();\n\n this.render();\n this.startLoop();\n }\n\n destroy(): void {\n this.stop();\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.button && this.buttonHandlerAttached) {\n this.button.removeEventListener('click', this.handleButtonClick);\n this.buttonHandlerAttached = false;\n }\n\n this.releaseWebGLResources();\n }\n\n private ensureCanvasCoversContainer(): void {\n if (this.canvas === this.container) return;\n if (!this.container.contains(this.canvas)) return;\n\n if (!this.canvas.style.width) this.canvas.style.width = '100%';\n if (!this.canvas.style.height) this.canvas.style.height = '100%';\n if (!this.canvas.style.display) this.canvas.style.display = 'block';\n }\n\n private syncContainerLayoutVars(): void {\n const target = this.canvas === this.container ? this.canvas : this.container;\n\n target.style.setProperty('--reels', String(this.reels));\n target.style.setProperty('--visible-slots', String(this.visibleSlots));\n target.style.aspectRatio = `${this.reels} / ${this.visibleSlots * this.heightScale}`;\n\n if (this.canvas !== this.container) {\n this.container.style.overflow = 'hidden';\n }\n }\n\n setSegments(initialSegments?: number[]): void {\n this.ensureReady();\n\n const segments = initialSegments ?? [];\n\n for (let r = 0; r < this.reels; r += 1) {\n const segIndex = segments[r] ?? 0;\n const idx = clampInt(segIndex, 0, this.opts.slotCount - 1);\n\n this.offsets[r] = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = null;\n this.idleStartTime = null;\n this.anim = null;\n\n this.stop();\n this.render();\n this.startLoop();\n }\n\n setSegment(segmentIndex: number): void {\n this.setSegments(new Array(this.reels).fill(segmentIndex));\n }\n\n spinOnce(stopAtSegments: number[], config?: Partial<ReelDealSpinConfig>): Promise<void> {\n this.ensureReady();\n\n const { minSpins, staggerMs, speedPxS, durationMs } = this.getSpinConfig(config);\n const useSpeed = speedPxS > 0;\n\n const startOffsets = new Array(this.reels);\n const deltas = new Array(this.reels);\n const durations = new Array(this.reels);\n\n for (let r = 0; r < this.reels; r += 1) {\n const idx = clampInt(stopAtSegments[r] ?? 0, 0, this.opts.slotCount - 1);\n const desiredTop = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n const desired = mod(desiredTop, this.spriteHeight);\n const current = mod(this.offsets[r], this.spriteHeight);\n\n const alignDelta = mod(current - desired, this.spriteHeight);\n\n let delta = -(minSpins * this.spriteHeight + alignDelta);\n\n if (useSpeed && durationMs > 0) {\n const desiredDistance = (speedPxS * durationMs) / 1000;\n const loops = Math.max(\n minSpins,\n Math.ceil(Math.max(0, desiredDistance - alignDelta) / this.spriteHeight),\n );\n\n delta = -(alignDelta + loops * this.spriteHeight);\n }\n\n if (staggerMs > 0 && useSpeed) {\n const extraDelta = (speedPxS * staggerMs * r) / 1000;\n const extraLoops = Math.ceil(extraDelta / this.spriteHeight);\n\n delta -= extraLoops * this.spriteHeight;\n }\n\n startOffsets[r] = this.offsets[r];\n deltas[r] = delta;\n\n if (useSpeed) {\n durations[r] = Math.max(0, Math.round((Math.abs(delta) / speedPxS) * 1000));\n } else {\n durations[r] = Math.max(0, durationMs + staggerMs * r);\n }\n }\n\n const totalDelta = deltas.reduce((acc, v) => acc + Math.abs(v), 0);\n\n if (totalDelta === 0 || durations.every((entry) => entry <= 0)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.offsets[r] + deltas[r];\n }\n\n this.anim = null;\n this.resolvePendingSpins();\n this.render();\n\n return Promise.resolve();\n }\n\n const startTime = performance.now();\n\n this.anim = { startTime, startOffsets, deltas, durationMs: durations };\n this.idleStartTime = null;\n this.startLoop();\n\n return new Promise((resolve) => {\n this.pendingSpinResolvers.push(resolve);\n });\n }\n\n private getSpinConfig(overrides?: Partial<ReelDealSpinConfig>): NormalizedSpinConfig {\n if (!overrides) return this.spinConfig;\n\n return normalizeSpinConfig({ ...this.spinConfig, ...overrides });\n }\n\n async spinQueue(spins: ReelDealSpinState[]): Promise<void> {\n for (let i = 0; i < spins.length; i += 1) {\n const spin = spins[i];\n\n await this.spinOnce(spin.stopAtSegments);\n\n spin.callback?.(i, spin.stopAtSegments);\n }\n }\n\n stop(): void {\n if (this.rafId !== null) cancelAnimationFrame(this.rafId);\n\n this.rafId = null;\n this.anim = null;\n this.resolvePendingSpins();\n this.lastVelT = null;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n }\n\n private resolvePendingSpins(): void {\n if (this.pendingSpinResolvers.length === 0) return;\n\n const resolvers = this.pendingSpinResolvers;\n\n this.pendingSpinResolvers = [];\n\n for (const resolve of resolvers) {\n resolve();\n }\n }\n\n requestResize(): void {\n this.queueResize();\n }\n\n private ensureReady(): void {\n if (\n !this.spriteImg ||\n this.spriteWidth <= 0 ||\n this.spriteHeight <= 0 ||\n this.frameHeight <= 0\n ) {\n throw new Error('Sprite is not ready. Call init() and await it first.');\n }\n }\n\n private attachButtonHandler(): void {\n if (!this.button || this.buttonHandlerAttached) return;\n\n this.button.addEventListener('click', this.handleButtonClick, { passive: true });\n this.buttonHandlerAttached = true;\n }\n\n private handleButtonClick = async (): Promise<void> => {\n if (this.spinning) return;\n\n const spin = this.consumeNextSpin() ?? {\n stopAtSegments: this.buildRandomSegments(),\n };\n\n this.spinning = true;\n this.setButtonDisabled(true);\n\n try {\n await this.spinOnce(spin.stopAtSegments);\n } finally {\n if (this.queuedSpins && this.queuedSpins.length === 0) {\n this.setButtonDisabled(true);\n } else {\n this.setButtonDisabled(false);\n }\n\n this.spinning = false;\n }\n };\n\n private resolveButton(): HTMLButtonElement | null {\n if (!this.opts.button) return null;\n\n return resolveElement<HTMLButtonElement>(this.opts.button, 'Button');\n }\n\n private resolveSpinQueue(): ReelDealSpinState[] | null {\n return this.opts.queuedSpinStates ?? null;\n }\n\n private consumeNextSpin(): ReelDealSpinState | null {\n if (!this.queuedSpins) {\n this.queuedSpins = this.resolveSpinQueue();\n }\n\n if (!this.queuedSpins || this.queuedSpins.length === 0) return null;\n\n return this.queuedSpins.shift() ?? null;\n }\n\n private buildRandomSegments(): number[] {\n const max = Math.max(1, Math.floor(this.opts.slotCount));\n const segments: number[] = [];\n\n for (let r = 0; r < this.reels; r += 1) {\n segments.push(Math.floor(Math.random() * max));\n }\n\n return segments;\n }\n\n private setButtonDisabled(disabled: boolean): void {\n if (!this.button) return;\n\n this.button.disabled = disabled;\n\n if (disabled) this.button.setAttribute('aria-busy', 'true');\n else this.button.removeAttribute('aria-busy');\n }\n\n private shouldAnimate(): boolean {\n return this.anim !== null || this.idleBob !== null;\n }\n\n private startLoop(): void {\n if (this.rafId !== null) return;\n if (!this.shouldAnimate()) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n }\n\n private loop = (now: number): void => {\n if (this.anim) {\n const { startTime, durationMs, startOffsets, deltas } = this.anim;\n\n let allDone = true;\n\n for (let r = 0; r < this.reels; r += 1) {\n const duration = durationMs[r] ?? 0;\n const t = duration <= 0 ? 1 : Math.min(1, (now - startTime) / duration);\n const eased = reelProgress(t);\n\n if (t < 1) allDone = false;\n\n const baseOffset = startOffsets[r] + deltas[r] * eased;\n\n let tick = 0;\n\n if (t > SPIN_TICK_START && this.frameHeight > 0) {\n const u = clamp01((t - SPIN_TICK_START) / SPIN_TICK_RANGE);\n const env = (1 - u) ** 2;\n const phase = (baseOffset / this.frameHeight) * Math.PI * 2;\n const amp = Math.min(SPIN_TICK_MAX_PX, this.frameHeight * 0.06);\n\n tick = Math.sin(phase) * env * amp;\n }\n\n this.offsets[r] = baseOffset + tick;\n }\n\n this.updateVelocity(now);\n this.render();\n\n if (allDone) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.anim.startOffsets[r] + this.anim.deltas[r];\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.updateVelocity(now);\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n\n this.anim = null;\n this.idleStartTime = null;\n\n this.render();\n this.resolvePendingSpins();\n }\n } else if (this.idleBob) {\n this.updateIdle(now);\n this.render();\n }\n\n if (!this.shouldAnimate()) {\n this.rafId = null;\n\n return;\n }\n\n if (this.rafId === null) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n private render(): void {\n if (!this.spriteImg) return;\n if (this.width <= 0 || this.height <= 0) return;\n if (!this.gl) return;\n\n this.renderWebGL();\n }\n\n private getSpinBlurPxForReel(reel: number): number {\n if (!this.anim) return 0;\n\n const speed = Math.abs(this.velocities[reel] ?? 0);\n const speedAtMax = Math.max(1, this.spinBlur.speedAtMax);\n const t = clamp01(speed / speedAtMax);\n\n return t * Math.max(0, this.spinBlur.maxPx);\n }\n\n private renderWebGL(): void {\n if (\n !this.gl ||\n !this.glProgram ||\n !this.glAttribs ||\n !this.glUniforms ||\n !this.glTexture ||\n !this.glBuffer\n ) {\n return;\n }\n\n if (!this.spriteImg) return;\n\n const gl = this.gl;\n\n gl.useProgram(this.glProgram);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffer);\n\n gl.enableVertexAttribArray(this.glAttribs.pos);\n gl.vertexAttribPointer(this.glAttribs.pos, 2, gl.FLOAT, false, 16, 0);\n gl.enableVertexAttribArray(this.glAttribs.uv);\n gl.vertexAttribPointer(this.glAttribs.uv, 2, gl.FLOAT, false, 16, 8);\n\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.glTexture);\n gl.uniform1i(this.glUniforms.texture, 0);\n\n gl.uniform1f(this.glUniforms.frameHeight, this.frameHeight);\n gl.uniform1f(this.glUniforms.spriteHeight, this.spriteHeight);\n gl.uniform1f(this.glUniforms.visibleSlots, this.visibleSlots);\n gl.uniform1f(this.glUniforms.heightScale, this.heightScale);\n gl.uniform1f(this.glUniforms.edgePower, Math.max(0.5, this.webglShading.edgePower));\n\n const shading = this.webglShading;\n const warpAngle = (Math.max(0, shading.warpAngleDeg) * Math.PI) / 180;\n\n gl.uniform1f(this.glUniforms.warpAngle, warpAngle);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const reelW = 1 / this.reels;\n\n for (let r = 0; r < this.reels; r += 1) {\n gl.uniform1f(this.glUniforms.reelX, r * reelW);\n gl.uniform1f(this.glUniforms.reelW, reelW);\n\n const offsetPx = mod(this.offsets[r], this.spriteHeight);\n\n gl.uniform1f(this.glUniforms.offsetPx, offsetPx);\n gl.uniform1f(this.glUniforms.blurPx, this.getSpinBlurPxForReel(r));\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n }\n\n private updateVelocity(now: number): void {\n if (this.lastVelT === null) {\n this.lastVelT = now;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n }\n\n return;\n }\n\n const dt = (now - this.lastVelT) / 1000;\n\n if (dt <= 0) return;\n\n for (let r = 0; r < this.reels; r += 1) {\n const v = (this.offsets[r] - this.lastVelOffsets[r]) / dt;\n\n this.velocities[r] = this.velocities[r] * VELOCITY_DECAY + v * VELOCITY_GAIN;\n this.lastVelOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = now;\n }\n\n private updateIdle(now: number): void {\n if (!this.idleBob) return;\n if (this.idleStartTime === null) this.idleStartTime = now;\n\n const { amplitudePx, speedHz, phaseOffsetRad, delayMs, rampMs } = this.idleBob;\n const elapsedMs = now - this.idleStartTime;\n\n if (elapsedMs < Math.max(0, delayMs)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = this.idleBaseOffsets[r];\n this.velocities[r] = 0;\n }\n\n return;\n }\n\n const tSec = (elapsedMs - Math.max(0, delayMs)) / 1000;\n const rampT = Math.max(\n 0,\n Math.min(1, (elapsedMs - Math.max(0, delayMs)) / Math.max(1, rampMs)),\n );\n const ramp = rampHermite(rampT);\n const omega = speedHz * Math.PI * 2;\n\n for (let r = 0; r < this.reels; r += 1) {\n const phase = tSec * omega + r * phaseOffsetRad;\n\n this.offsets[r] = this.idleBaseOffsets[r] + Math.sin(phase) * (amplitudePx * ramp);\n this.velocities[r] = 0;\n }\n }\n\n private async loadSprite(): Promise<void> {\n const { sprite } = this.opts;\n const img = typeof sprite === 'string' ? new Image() : sprite;\n\n if (typeof sprite === 'string') {\n img.src = sprite;\n }\n\n if (!img.complete || img.naturalWidth <= 0) {\n await new Promise<void>((resolve, reject) => {\n const onLoad = () => resolve();\n const onError = () => reject(new Error('Failed to load sprite image'));\n\n img.addEventListener('load', onLoad, { once: true });\n img.addEventListener('error', onError, { once: true });\n });\n }\n\n const slotCount = Math.max(1, Math.floor(this.opts.slotCount));\n const spriteWidth = img.naturalWidth;\n const frameHeight = spriteWidth;\n const expectedSpriteHeight = frameHeight * slotCount;\n\n const naturalHeight = img.naturalHeight;\n const diff = Math.abs(naturalHeight - expectedSpriteHeight);\n\n if (diff > 2) {\n throw new Error(\n `Sprite size mismatch. Expected height ~${Math.round(expectedSpriteHeight)}px (slotCount=${slotCount}, slot aspect 1/1), got ${naturalHeight}px.`,\n );\n }\n\n this.spriteImg = img;\n this.spriteWidth = spriteWidth;\n this.frameHeight = Math.max(1, Math.round(frameHeight));\n this.spriteHeight = Math.max(1, Math.round(this.frameHeight * slotCount));\n }\n\n private setupResizeWatcher(): void {\n if (typeof ResizeObserver === 'undefined') return;\n if (this.resizeObserver) return;\n\n this.resizeObserver = new ResizeObserver(() => this.queueResize());\n this.resizeObserver.observe(this.container);\n }\n\n private queueResize = (): void => {\n if (this.resizeQueued) return;\n\n this.resizeQueued = true;\n\n requestAnimationFrame(() => {\n this.resizeQueued = false;\n this.resizeToContainer();\n });\n };\n\n private resizeToContainer(): void {\n const cssWidth = this.container.clientWidth;\n const cssHeight = this.container.clientHeight;\n const slotsViewport = this.visibleSlots * this.heightScale;\n\n if (cssWidth <= 0 || cssHeight <= 0) {\n this.applyResize(1, 1);\n\n return;\n }\n\n const slots = slotsViewport;\n const reels = this.reels;\n const fitByWidthH = (cssWidth / reels) * slots;\n\n let w = cssWidth;\n let h = fitByWidthH;\n\n if (h > cssHeight) {\n h = cssHeight;\n w = (cssHeight / slots) * reels;\n }\n\n const viewportWidth = Math.max(1, Math.floor(w));\n const viewportHeight = Math.max(1, Math.floor(h));\n\n this.applyResize(viewportWidth, viewportHeight);\n this.render();\n }\n\n private applyViewportCrop(): void {\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n this.canvas.style.transform = '';\n }\n\n private applyResize(width: number, height: number): void {\n const normalizedWidth = Math.max(1, Math.floor(width));\n const normalizedHeight = Math.max(1, Math.floor(height));\n\n const dpr = this.getTargetDpr();\n\n this.width = normalizedWidth;\n this.height = normalizedHeight;\n this.dpr = dpr;\n\n this.canvas.width = Math.floor(this.width * this.dpr);\n this.canvas.height = Math.floor(this.height * this.dpr);\n\n if (this.gl) {\n this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);\n }\n\n this.applyViewportCrop();\n }\n\n private getTargetDpr(): number {\n const rawDpr = window.devicePixelRatio || 1;\n const base = Math.max(1, rawDpr);\n\n return Math.min(this.maxDpr, base);\n }\n\n private tryInitWebGL(): void {\n if (!this.spriteImg) return;\n\n this.releaseWebGLResources();\n this.webglInitError = null;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n antialias: true,\n alpha: true,\n }) as WebGL2RenderingContext | null) ??\n (this.canvas.getContext('webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null) ??\n (this.canvas.getContext('experimental-webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null);\n\n if (!gl) {\n const fallback = document.createElement('canvas');\n const canWebGL = typeof WebGLRenderingContext !== 'undefined';\n const fallbackGl =\n (fallback.getContext('webgl2') as WebGL2RenderingContext | null) ??\n (fallback.getContext('webgl') as WebGLRenderingContext | null);\n\n this.webglInitError = `context is null (browser WebGL=${canWebGL ? 'yes' : 'no'}, fallback canvas=${fallbackGl ? 'yes' : 'no'})`;\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const vsSource = `\n attribute vec2 a_pos;\n attribute vec2 a_uv;\n varying vec2 v_uv;\n void main() {\n v_uv = a_uv;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n }\n `;\n\n const fsSource = `\n #ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n #endif\n\n varying vec2 v_uv;\n uniform sampler2D u_tex;\n uniform float u_reelX;\n uniform float u_reelW;\n uniform float u_offsetPx;\n uniform float u_frameHeight;\n uniform float u_spriteHeight;\n uniform float u_visibleSlots;\n uniform float u_heightScale;\n uniform float u_edgePower;\n uniform float u_warpAngle;\n uniform float u_blurPx;\n\n float saturate(float x) { return clamp(x, 0.0, 1.0); }\n\n void main() {\n if (v_uv.x < u_reelX || v_uv.x > (u_reelX + u_reelW)) {\n discard;\n }\n\n float localX = (v_uv.x - u_reelX) / u_reelW;\n // v_uv.y is authored as 0 at TOP, 1 at BOTTOM (Canvas-like).\n float localY = v_uv.y;\n float hs = max(0.0001, u_heightScale);\n float fullY = localY * hs + (1.0 - hs) * 0.5;\n\n float warp = max(0.0, u_warpAngle);\n float warpStrength = saturate(warp / 1.2);\n float center = 0.5;\n float centerBand = 1.0 / 3.0;\n float halfBand = centerBand * 0.5;\n float dist = abs(fullY - center);\n // Smoother transition: avoid a hard boundary at halfBand.\n float edgeWeight = smoothstep(halfBand, 0.5, dist);\n float edgePow = max(0.5, u_edgePower);\n edgeWeight = pow(edgeWeight, edgePow) * warpStrength;\n float tEase = smoothstep(0.0, 1.0, fullY);\n float warpedY = mix(fullY, tEase, edgeWeight);\n float y = (warpedY - 0.5) * 2.0;\n float theta = y * warp;\n float edgeWarp = edgeWeight;\n\n float yPx = u_offsetPx + warpedY * (u_frameHeight * u_visibleSlots);\n // With UNPACK_FLIP_Y_WEBGL=0 and Canvas-like UVs (v=0 at TOP),\n // v=0 samples the TOP of the original image, so pixel Y-from-top maps directly to v.\n // Normalize early to improve precision on mobile GPUs.\n float v = fract((u_offsetPx / u_spriteHeight) +\n warpedY * ((u_frameHeight * u_visibleSlots) / u_spriteHeight));\n\n vec4 base = texture2D(u_tex, vec2(localX, v));\n\n if (u_blurPx > 0.0) {\n // Clamp blur step to reduce banding on mobile GPUs.\n // Scale max step with speed proxy (u_blurPx).\n float maxStepPx = 1.0 + 3.0 * saturate(u_blurPx / 4.0);\n float blurPx = min(u_blurPx, maxStepPx);\n float blurStep = blurPx / max(1.0, u_spriteHeight);\n // 9-tap gaussian-ish blur to reduce banding on mobile.\n vec4 sum = base * 0.227027;\n sum += texture2D(u_tex, vec2(localX, fract(v + blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v - blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v + 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v + 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v - 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v + 4.0 * blurStep))) * 0.016216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 4.0 * blurStep))) * 0.016216;\n base = sum;\n }\n\n gl_FragColor = base;\n }\n `;\n\n const program = this.createWebGLProgram(gl, vsSource, fsSource);\n\n if (!program) {\n this.webglInitError = 'shader program failed to compile/link (see console)';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const buffer = gl.createBuffer();\n\n if (!buffer) {\n this.webglInitError = 'failed to create vertex buffer';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\n const quad = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0]);\n\n gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n const texture = gl.createTexture();\n\n if (!texture) {\n this.webglInitError = 'failed to create texture';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteBuffer(buffer);\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.spriteImg);\n\n const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0;\n const pot = isPowerOfTwo(this.spriteWidth) && isPowerOfTwo(this.spriteHeight);\n\n if (pot) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n }\n\n gl.useProgram(program);\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n this.gl = gl;\n this.glProgram = program;\n this.glBuffer = buffer;\n this.glTexture = texture;\n this.glAttribs = {\n pos: gl.getAttribLocation(program, 'a_pos'),\n uv: gl.getAttribLocation(program, 'a_uv'),\n };\n this.glUniforms = {\n texture: gl.getUniformLocation(program, 'u_tex'),\n reelX: gl.getUniformLocation(program, 'u_reelX'),\n reelW: gl.getUniformLocation(program, 'u_reelW'),\n offsetPx: gl.getUniformLocation(program, 'u_offsetPx'),\n frameHeight: gl.getUniformLocation(program, 'u_frameHeight'),\n spriteHeight: gl.getUniformLocation(program, 'u_spriteHeight'),\n visibleSlots: gl.getUniformLocation(program, 'u_visibleSlots'),\n heightScale: gl.getUniformLocation(program, 'u_heightScale'),\n edgePower: gl.getUniformLocation(program, 'u_edgePower'),\n warpAngle: gl.getUniformLocation(program, 'u_warpAngle'),\n blurPx: gl.getUniformLocation(program, 'u_blurPx'),\n };\n }\n\n private createWebGLProgram(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n vsSource: string,\n fsSource: string,\n ): WebGLProgram | null {\n const vs = this.createWebGLShader(gl, gl.VERTEX_SHADER, vsSource);\n const fs = this.createWebGLShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vs || !fs) {\n if (vs) gl.deleteShader(vs);\n if (fs) gl.deleteShader(fs);\n\n return null;\n }\n\n const program = gl.createProgram();\n\n if (!program) return null;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program) ?? 'unknown link error';\n\n this.webglInitError = `program link failed: ${info}`;\n\n console.warn('ReelDeal WebGL: program link failed', info);\n\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return null;\n }\n\n gl.detachShader(program, vs);\n gl.detachShader(program, fs);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return program;\n }\n\n private createWebGLShader(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader) ?? 'unknown compile error';\n\n this.webglInitError = `shader compile failed: ${info}`;\n\n console.warn('ReelDeal WebGL: shader compile failed', info);\n\n gl.deleteShader(shader);\n\n return null;\n }\n\n return shader;\n }\n\n private releaseWebGLResources(): void {\n if (!this.gl) return;\n if (this.glTexture) this.gl.deleteTexture(this.glTexture);\n if (this.glBuffer) this.gl.deleteBuffer(this.glBuffer);\n if (this.glProgram) this.gl.deleteProgram(this.glProgram);\n\n this.glTexture = null;\n this.glBuffer = null;\n this.glProgram = null;\n this.glAttribs = null;\n this.glUniforms = null;\n this.gl = null;\n }\n}\n"],"names":["defaultSpinConfig","defaultWebGLShading","defaultSpinBlur","defaultIdleBob","SPIN_ACCEL_END","SPIN_DECEL_START","clampInt","value","min","max","mod","n","m","clamp01","t","rampHermite","u","x","brakeHermite","reelProgress","tt","finiteOr","fallback","nonNegative","nonNegativeInt","DEFAULT_MAX_DPR","normalizeSpinConfig","overrides","base","normalizeSpinBlur","normalizeWebGLShading","normalizeIdleBob","normalizeMaxDpr","resolveElementById","id","kind","el","resolveElement","elOrId","MAX_REELS","SPIN_TICK_START","SPIN_TICK_RANGE","SPIN_TICK_MAX_PX","VELOCITY_DECAY","VELOCITY_GAIN","ReelDeal","options","target","initialSegments","segments","r","segIndex","idx","segmentIndex","stopAtSegments","config","minSpins","staggerMs","speedPxS","durationMs","useSpeed","startOffsets","deltas","durations","desiredTop","desired","current","alignDelta","delta","desiredDistance","loops","extraDelta","extraLoops","acc","v","entry","startTime","resolve","spins","spin","resolvers","disabled","now","allDone","duration","eased","baseOffset","tick","env","phase","amp","reel","speed","speedAtMax","gl","shading","warpAngle","reelW","offsetPx","dt","amplitudePx","speedHz","phaseOffsetRad","delayMs","rampMs","elapsedMs","tSec","rampT","ramp","omega","sprite","img","reject","onLoad","onError","slotCount","spriteWidth","frameHeight","expectedSpriteHeight","naturalHeight","cssWidth","cssHeight","slotsViewport","slots","reels","fitByWidthH","w","h","viewportWidth","viewportHeight","width","height","normalizedWidth","normalizedHeight","dpr","rawDpr","canWebGL","fallbackGl","program","buffer","quad","texture","isPowerOfTwo","vsSource","fsSource","vs","fs","info","type","source","shader"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reel-deal",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-dev.1",
|
|
4
|
+
"description": "High-performance WebGL slot reel animation for any HTMLCanvasElement.",
|
|
5
5
|
"author": "ux-ui.pro",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -13,39 +13,68 @@
|
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://github.com/ux-ui-pro/reel-deal",
|
|
15
15
|
"sideEffects": false,
|
|
16
|
+
"type": "module",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18.0.0"
|
|
19
|
+
},
|
|
16
20
|
"scripts": {
|
|
17
21
|
"clean": "rimraf dist",
|
|
18
22
|
"build": "vite build",
|
|
23
|
+
"dev": "vite",
|
|
19
24
|
"verify": "yarn lint && yarn typecheck",
|
|
20
25
|
"lint": "biome check src",
|
|
21
26
|
"lint:fix": "biome check --write src",
|
|
22
27
|
"format": "biome format --write src",
|
|
23
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
28
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
29
|
+
"prepublishOnly": "yarn clean && yarn build && yarn verify"
|
|
24
30
|
},
|
|
25
31
|
"source": "src/index.ts",
|
|
26
32
|
"main": "dist/index.cjs.js",
|
|
27
33
|
"module": "dist/index.es.js",
|
|
34
|
+
"browser": "./dist/index.umd.js",
|
|
28
35
|
"types": "dist/index.d.ts",
|
|
29
36
|
"exports": {
|
|
30
37
|
".": {
|
|
31
38
|
"types": "./dist/index.d.ts",
|
|
32
39
|
"import": "./dist/index.es.js",
|
|
33
|
-
"require": "./dist/index.cjs.js"
|
|
40
|
+
"require": "./dist/index.cjs.js",
|
|
41
|
+
"default": "./dist/index.umd.js"
|
|
34
42
|
},
|
|
35
43
|
"./dist/*": "./dist/*"
|
|
36
44
|
},
|
|
37
45
|
"files": [
|
|
38
|
-
"dist
|
|
46
|
+
"dist",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE"
|
|
39
49
|
],
|
|
40
50
|
"devDependencies": {
|
|
41
|
-
"@biomejs/biome": "2.3.
|
|
42
|
-
"@types/node": "25.0.
|
|
51
|
+
"@biomejs/biome": "2.3.12",
|
|
52
|
+
"@types/node": "25.0.10",
|
|
43
53
|
"rimraf": "6.1.2",
|
|
44
54
|
"typescript": "5.9.3",
|
|
45
|
-
"vite": "7.3.
|
|
55
|
+
"vite": "7.3.1",
|
|
46
56
|
"vite-plugin-dts": "4.5.4"
|
|
47
57
|
},
|
|
48
58
|
"keywords": [
|
|
49
|
-
|
|
59
|
+
"typescript",
|
|
60
|
+
"javascript",
|
|
61
|
+
"canvas",
|
|
62
|
+
"html5-canvas",
|
|
63
|
+
"webgl",
|
|
64
|
+
"animation",
|
|
65
|
+
"slot",
|
|
66
|
+
"slots",
|
|
67
|
+
"slot-machine",
|
|
68
|
+
"reel",
|
|
69
|
+
"reels",
|
|
70
|
+
"slot-reel",
|
|
71
|
+
"casino",
|
|
72
|
+
"game",
|
|
73
|
+
"sprite",
|
|
74
|
+
"spritesheet",
|
|
75
|
+
"motion-blur",
|
|
76
|
+
"performance",
|
|
77
|
+
"dpr",
|
|
78
|
+
"browser"
|
|
50
79
|
]
|
|
51
80
|
}
|