Beneath the loot box spin carousel, add a SECOND horizontal RNG slider for the “Multiplier Roll.”
0a557cb
verified
| document.addEventListener('DOMContentLoaded', () => { | |
| // Initialize scene | |
| const container = document.getElementById('animation-container'); | |
| const lootBoxesContainer = document.getElementById('loot-boxes'); | |
| const rollButton = document.getElementById('roll-button'); | |
| // Box types and colors | |
| const boxTypes = [ | |
| { name: 'Common', class: 'common', chance: 60 }, | |
| { name: 'Rare', class: 'rare', chance: 25 }, | |
| { name: 'Epic', class: 'epic', chance: 10 }, | |
| { name: 'Legendary', class: 'legendary', chance: 5 } | |
| ]; | |
| // Generate loot boxes | |
| const totalBoxes = 12; | |
| const boxes = []; | |
| for (let i = 0; i < totalBoxes; i++) { | |
| const box = document.createElement('div'); | |
| box.className = 'loot-box'; | |
| // Determine box type based on chance | |
| const random = Math.random() * 100; | |
| let cumulativeChance = 0; | |
| let boxType; | |
| for (const type of boxTypes) { | |
| cumulativeChance += type.chance; | |
| if (random <= cumulativeChance) { | |
| boxType = type; | |
| break; | |
| } | |
| } | |
| box.innerHTML = ` | |
| <div class="loot-box-inner"> | |
| <div class="loot-box-front ${boxType.class}"> | |
| <div class="loot-box-lid ${boxType.class}"></div> | |
| <span class="text-xs opacity-70">${boxType.name}</span> | |
| </div> | |
| <div class="loot-box-back"></div> | |
| </div> | |
| `; | |
| boxes.push(box); | |
| lootBoxesContainer.appendChild(box); | |
| } | |
| // Add event listener to roll button | |
| rollButton.addEventListener('click', () => { | |
| if (lootBoxesContainer.classList.contains('rolling')) return; | |
| startRollAnimation(); | |
| startMultiplierRoll(); | |
| }); | |
| function startMultiplierRoll() { | |
| const multiplierSlider = document.getElementById('multiplier-slider'); | |
| multiplierSlider.classList.add('blurred'); | |
| const multipliers = Array.from(document.querySelectorAll('.multiplier-pill')); | |
| multipliers.forEach(pill => pill.classList.remove('selected')); | |
| // High-speed sliding phase | |
| const slideDistance = Math.random() * 3000 + 1500; | |
| gsap.to(multiplierSlider, { | |
| x: `-=${slideDistance}`, | |
| duration: 2.5, | |
| ease: 'power2.inOut', | |
| onComplete: () => { | |
| // Select a random multiplier (bias towards higher multipliers) | |
| const weightedMultipliers = [0,1,2,3,4,5,0,1,2,3,4,5]; | |
| const centerMultiplierIndex = Math.random() < 0.3 ? | |
| Math.floor(Math.random() * 3) + 6 : // Higher chance for better multipliers | |
| Math.floor(Math.random() * 6) + 3; // Normal distribution | |
| const selectedMultiplier = multipliers[weightedMultipliers[centerMultiplierIndex]]; | |
| selectedMultiplier.classList.add('selected'); | |
| // Align the selected multiplier to center | |
| const containerWidth = document.getElementById('multiplier-track').offsetWidth; | |
| const pillWidth = selectedMultiplier.offsetWidth; | |
| const pillOffset = selectedMultiplier.offsetLeft; | |
| const targetX = containerWidth / 2 - pillWidth / 2 - pillOffset; | |
| gsap.to(multiplierSlider, { | |
| x: targetX, | |
| duration: 0.8, | |
| ease: 'elastic.out(1, 0.5)', | |
| onComplete: () => { | |
| multiplierSlider.classList.remove('blurred'); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| function startRollAnimation() { | |
| // Disable button during animation | |
| lootBoxesContainer.classList.add('rolling'); | |
| rollButton.classList.add('opacity-50', 'pointer-events-none'); | |
| // Clear previous selection | |
| document.querySelectorAll('.loot-box.selected').forEach(box => { | |
| box.classList.remove('selected'); | |
| }); | |
| // Add motion blur | |
| lootBoxesContainer.classList.add('motion-blur'); | |
| // Create speed lines | |
| const speedLines = document.createElement('div'); | |
| speedLines.className = 'speed-lines'; | |
| container.appendChild(speedLines); | |
| // Energy burst effect | |
| const energyBurst = document.createElement('div'); | |
| energyBurst.className = 'energy-burst'; | |
| energyBurst.style.width = '200px'; | |
| energyBurst.style.height = '200px'; | |
| energyBurst.style.left = rollButton.offsetLeft + rollButton.offsetWidth / 2 - 100 + 'px'; | |
| energyBurst.style.top = rollButton.offsetTop + rollButton.offsetHeight / 2 - 100 + 'px'; | |
| container.appendChild(energyBurst); | |
| // Animate energy burst | |
| gsap.to(energyBurst, { | |
| scale: 1.5, | |
| opacity: 0.8, | |
| duration: 0.3, | |
| ease: 'power2.out', | |
| onComplete: () => { | |
| gsap.to(energyBurst, { | |
| scale: 3, | |
| opacity: 0, | |
| duration: 0.5, | |
| onComplete: () => { | |
| energyBurst.remove(); | |
| } | |
| }); | |
| } | |
| }); | |
| // High-speed sliding phase | |
| const slideDistance = Math.random() * 5000 + 3000; | |
| gsap.to(lootBoxesContainer, { | |
| x: `-=${slideDistance}`, | |
| duration: 3, | |
| ease: 'power2.inOut', | |
| onUpdate: () => { | |
| // Add speed lines effect | |
| gsap.to(speedLines, { | |
| opacity: 0.5, | |
| duration: 0.1 | |
| }); | |
| }, | |
| onComplete: () => { | |
| // Remove speed lines | |
| gsap.to(speedLines, { | |
| opacity: 0, | |
| duration: 0.5, | |
| onComplete: () => { | |
| speedLines.remove(); | |
| } | |
| }); | |
| // Remove motion blur | |
| lootBoxesContainer.classList.remove('motion-blur'); | |
| // Select a random box (bias towards center boxes) | |
| const centerBoxIndex = Math.floor(Math.random() * 4) + 4; | |
| const selectedBox = boxes[centerBoxIndex]; | |
| selectedBox.classList.add('selected'); | |
| // Align the selected box to center | |
| const containerWidth = container.offsetWidth; | |
| const boxWidth = selectedBox.offsetWidth; | |
| const boxOffset = selectedBox.offsetLeft; | |
| const targetX = containerWidth / 2 - boxWidth / 2 - boxOffset; | |
| gsap.to(lootBoxesContainer, { | |
| x: targetX, | |
| duration: 0.8, | |
| ease: 'elastic.out(1, 0.5)', | |
| onComplete: () => { | |
| // Open the box after a delay | |
| setTimeout(() => { | |
| openBox(selectedBox); | |
| }, 500); | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| function openBox(box) { | |
| const lid = box.querySelector('.loot-box-lid'); | |
| const isLegendary = box.classList.contains('legendary'); | |
| // Animate lid opening | |
| gsap.to(lid, { | |
| rotationX: -120, | |
| transformOrigin: 'bottom', | |
| duration: 0.5, | |
| ease: 'back.out(1)' | |
| }); | |
| // Create light explosion | |
| const explosion = document.createElement('div'); | |
| explosion.className = 'absolute inset-0 rounded-full bg-amber-500/20 blur-xl'; | |
| explosion.style.width = '200px'; | |
| explosion.style.height = '200px'; | |
| explosion.style.left = '50%'; | |
| explosion.style.top = '50%'; | |
| explosion.style.transform = 'translate(-50%, -50%) scale(0)'; | |
| box.appendChild(explosion); | |
| // Animate explosion | |
| gsap.to(explosion, { | |
| scale: 2, | |
| opacity: 0.8, | |
| duration: 0.3, | |
| ease: 'power2.out', | |
| onComplete: () => { | |
| gsap.to(explosion, { | |
| scale: 3, | |
| opacity: 0, | |
| duration: 0.5, | |
| onComplete: () => { | |
| explosion.remove(); | |
| } | |
| }); | |
| } | |
| }); | |
| // Create particles | |
| createParticles(box, isLegendary); | |
| // Create prize reveal | |
| const prizeValue = isLegendary ? '5000 SC' : ['100 SC', '250 SC', '500 SC', '1000 SC'][Math.floor(Math.random() * 4)]; | |
| const prize = document.createElement('div'); | |
| prize.className = 'prize-reveal'; | |
| prize.textContent = prizeValue; | |
| box.appendChild(prize); | |
| // Enable button again | |
| setTimeout(() => { | |
| lootBoxesContainer.classList.remove('rolling'); | |
| rollButton.classList.remove('opacity-50', 'pointer-events-none'); | |
| }, 2000); | |
| } | |
| function createParticles(box, isLegendary) { | |
| const particleCount = isLegendary ? 30 : 15; | |
| const colors = isLegendary | |
| ? ['#FF8C00', '#FFD700', '#FFA500', '#FFFFFF'] | |
| : ['#FFFFFF', '#CCCCCC', '#AAAAAA']; | |
| for (let i = 0; i < particleCount; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'absolute rounded-full'; | |
| particle.style.width = Math.random() * 8 + 2 + 'px'; | |
| particle.style.height = particle.style.width; | |
| particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; | |
| particle.style.left = '50%'; | |
| particle.style.top = '50%'; | |
| particle.style.opacity = '0'; | |
| box.appendChild(particle); | |
| const angle = Math.random() * Math.PI * 2; | |
| const distance = Math.random() * 50 + 30; | |
| const duration = Math.random() * 1 + 0.5; | |
| gsap.to(particle, { | |
| x: Math.cos(angle) * distance, | |
| y: Math.sin(angle) * distance - 50, | |
| opacity: 1, | |
| duration: duration * 0.3, | |
| ease: 'power2.out' | |
| }); | |
| gsap.to(particle, { | |
| y: `-=${Math.random() * 30 + 20}`, | |
| opacity: 0, | |
| duration: duration * 0.7, | |
| delay: duration * 0.3, | |
| ease: 'power2.in', | |
| onComplete: () => { | |
| particle.remove(); | |
| } | |
| }); | |
| } | |
| } | |
| // Ambient animations | |
| setInterval(() => { | |
| const randomBox = boxes[Math.floor(Math.random() * boxes.length)]; | |
| if (!randomBox.classList.contains('selected') && !lootBoxesContainer.classList.contains('rolling')) { | |
| gsap.to(randomBox, { | |
| y: -10, | |
| duration: 1, | |
| yoyo: true, | |
| repeat: 1, | |
| ease: 'sine.inOut' | |
| }); | |
| } | |
| }, 3000); | |
| }); |