Create a premium, cinematic “Double Spin Loot Box Module” for an online sweepstakes casino platform called Nioplay. This should be a single, complete hero-style UI scene rendered in a WIDESCREEN 16:9 ASPECT RATIO. The image should not look like a webpage screenshot — instead, it should look like a high-end product UI render designed for presentation and implementation.
60c16c9
verified
| class LootBoxCarousel extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.boxes = [ | |
| { rarity: 'common', value: 5, color: 'green' }, | |
| { rarity: 'common', value: 5, color: 'green' }, | |
| { rarity: 'common', value: 10, color: 'green' }, | |
| { rarity: 'rare', value: 15, color: 'blue' }, | |
| { rarity: 'rare', value: 20, color: 'blue' }, | |
| { rarity: 'epic', value: 30, color: 'purple' }, | |
| { rarity: 'legendary', value: 50, color: 'gold' }, | |
| { rarity: 'epic', value: 25, color: 'purple' }, | |
| { rarity: 'rare', value: 15, color: 'blue' }, | |
| { rarity: 'common', value: 10, color: 'green' } | |
| ]; | |
| this.selectedIndex = 0; | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.render(); | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| width: 100%; | |
| position: relative; | |
| height: 180px; | |
| margin-bottom: 20px; | |
| } | |
| .track { | |
| position: relative; | |
| height: 100%; | |
| background: rgba(20, 20, 20, 0.5); | |
| border-radius: 8px; | |
| border: 1px solid rgba(255, 140, 0, 0.2); | |
| box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5); | |
| overflow: hidden; | |
| } | |
| .track::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: linear-gradient(90deg, rgba(255, 140, 0, 0.1) 0%, transparent 50%, rgba(255, 140, 0, 0.1) 100%); | |
| } | |
| .boxes-container { | |
| position: absolute; | |
| display: flex; | |
| height: 100%; | |
| align-items: center; | |
| gap: 20px; | |
| padding: 0 50%; | |
| will-change: transform; | |
| transition: transform 0.1s linear; | |
| } | |
| .loot-box { | |
| width: 100px; | |
| height: 100px; | |
| border-radius: 8px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| position: relative; | |
| transition: all 0.3s ease; | |
| z-index: 5; | |
| } | |
| .loot-box.common { | |
| background: rgba(30, 30, 30, 0.8); | |
| border: 2px solid var(--premium-green); | |
| box-shadow: 0 0 15px var(--premium-green); | |
| } | |
| .loot-box.rare { | |
| background: rgba(30, 30, 30, 0.8); | |
| border: 2px solid var(--premium-blue); | |
| box-shadow: 0 0 15px var(--premium-blue); | |
| } | |
| .loot-box.epic { | |
| background: rgba(30, 30, 30, 0.8); | |
| border: 2px solid var(--premium-purple); | |
| box-shadow: 0 0 20px var(--premium-purple); | |
| } | |
| .loot-box.legendary { | |
| background: rgba(30, 30, 30, 0.8); | |
| border: 2px solid var(--premium-gold); | |
| box-shadow: 0 0 25px var(--premium-gold); | |
| } | |
| .loot-box.selected { | |
| transform: scale(1.2); | |
| z-index: 10; | |
| } | |
| .loot-box.selected.common { | |
| box-shadow: 0 0 25px var(--premium-green); | |
| } | |
| .loot-box.selected.rare { | |
| box-shadow: 0 0 30px var(--premium-blue); | |
| } | |
| .loot-box.selected.epic { | |
| box-shadow: 0 0 35px var(--premium-purple); | |
| } | |
| .loot-box.selected.legendary { | |
| box-shadow: 0 0 40px var(--premium-gold); | |
| } | |
| .box-icon { | |
| font-size: 24px; | |
| margin-bottom: 5px; | |
| } | |
| .rarity { | |
| font-size: 10px; | |
| text-transform: uppercase; | |
| font-weight: bold; | |
| margin-top: 5px; | |
| } | |
| .selection-window { | |
| position: absolute; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| height: 120px; | |
| width: 120px; | |
| border: 2px solid rgba(255, 140, 0, 0.8); | |
| border-radius: 8px; | |
| box-shadow: 0 0 20px rgba(255, 140, 0, 0.5); | |
| z-index: 4; | |
| pointer-events: none; | |
| } | |
| </style> | |
| <div class="track"> | |
| <div class="selection-window"></div> | |
| <div class="boxes-container"> | |
| ${this.boxes.map((box, index) => ` | |
| <div class="loot-box ${box.rarity} ${index === this.selectedIndex ? 'selected' : ''}" data-index="${index}"> | |
| <i data-feather="box" class="box-icon"></i> | |
| <span class="rarity">${box.rarity}</span> | |
| </div> | |
| `).join('')} | |
| </div> | |
| </div> | |
| `; | |
| // Initialize feather icons | |
| feather.replace(); | |
| } | |
| startSpin() { | |
| const container = this.shadowRoot.querySelector('.boxes-container'); | |
| const boxes = this.shadowRoot.querySelectorAll('.loot-box'); | |
| // Reset selection | |
| boxes.forEach(box => box.classList.remove('selected')); | |
| // Animate spin | |
| let currentPosition = 0; | |
| let speed = 0; | |
| const maxSpeed = 30; | |
| const deceleration = 0.5; | |
| const targetIndex = Math.floor(Math.random() * this.boxes.length); | |
| const animate = () => { | |
| currentPosition += speed; | |
| speed = Math.max(0, speed - deceleration); | |
| // Apply movement | |
| container.style.transform = `translateX(${currentPosition}px)`; | |
| // Highlight box in center | |
| const centerPosition = -currentPosition - container.offsetWidth / 2 + this.offsetWidth / 2; | |
| const boxWidth = 120; // box width + gap | |
| const centerIndex = Math.round(centerPosition / boxWidth) % this.boxes.length; | |
| const selectedIndex = (centerIndex + this.boxes.length) % this.boxes.length; | |
| boxes.forEach((box, i) => { | |
| box.classList.toggle('selected', i === selectedIndex); | |
| }); | |
| // Continue animation until stopped | |
| if (speed > 0) { | |
| requestAnimationFrame(animate); | |
| } else { | |
| // Final selection | |
| this.selectedIndex = targetIndex; | |
| this.render(); | |
| } | |
| }; | |
| // Initial acceleration | |
| speed = maxSpeed; | |
| animate(); | |
| } | |
| getSelectedBox() { | |
| return this.boxes[this.selectedIndex]; | |
| } | |
| } | |
| customElements.define('loot-box-carousel', LootBoxCarousel); |