Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Green Button Explode</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 32px; | |
| font-family: 'Segoe UI', system-ui, sans-serif; | |
| background: linear-gradient(135deg, #0a1f0f 0%, #1a3d24 100%); | |
| overflow: hidden; | |
| cursor: default; | |
| } | |
| /* Screen shake animation */ | |
| @keyframes shake { | |
| 0%, 100% { transform: translate(0, 0); } | |
| 10% { transform: translate(-12px, 8px); } | |
| 20% { transform: translate(10px, -10px); } | |
| 30% { transform: translate(-8px, 6px); } | |
| 40% { transform: translate(8px, -6px); } | |
| 50% { transform: translate(-6px, -8px); } | |
| 60% { transform: translate(6px, 8px); } | |
| 70% { transform: translate(-4px, 4px); } | |
| 80% { transform: translate(4px, -4px); } | |
| 90% { transform: translate(-2px, 2px); } | |
| } | |
| .shake { animation: shake 0.6s ease; } | |
| .flash { | |
| position: fixed; | |
| inset: 0; | |
| background: #fff; | |
| pointer-events: none; | |
| opacity: 0; | |
| z-index: 999; | |
| } | |
| @keyframes flashAnim { | |
| 0% { opacity: 0; } | |
| 15% { opacity: 0.7; } | |
| 100% { opacity: 0; } | |
| } | |
| .flash.active { animation: flashAnim 0.5s ease; } | |
| /* Button container */ | |
| #button-container { | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| /* The green button */ | |
| #blue-button { | |
| padding: 28px 80px; | |
| font-size: 32px; | |
| font-weight: 800; | |
| letter-spacing: 2px; | |
| text-transform: uppercase; | |
| color: #fff; | |
| background: linear-gradient(180deg, #22c55e 0%, #15803d 100%); | |
| border: none; | |
| border-radius: 16px; | |
| cursor: pointer; | |
| box-shadow: | |
| 0 8px 0 #166534, | |
| 0 12px 28px rgba(22, 101, 52, 0.5), | |
| inset 0 2px 0 rgba(255,255,255,0.3); | |
| transition: transform 0.1s, box-shadow 0.1s, opacity 0.15s; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.3); | |
| user-select: none; | |
| position: relative; | |
| z-index: 10; | |
| } | |
| #blue-button:hover { | |
| background: linear-gradient(180deg, #4ade80 0%, #16a34a 100%); | |
| transform: translateY(-2px); | |
| box-shadow: | |
| 0 10px 0 #166534, | |
| 0 16px 32px rgba(22, 101, 52, 0.6), | |
| inset 0 2px 0 rgba(255,255,255,0.4); | |
| } | |
| #blue-button:active { | |
| transform: translateY(4px); | |
| box-shadow: | |
| 0 4px 0 #166534, | |
| 0 6px 16px rgba(22, 101, 52, 0.4), | |
| inset 0 2px 0 rgba(255,255,255,0.2); | |
| } | |
| #blue-button.exploding { | |
| opacity: 0; | |
| transform: scale(1.3); | |
| transition: opacity 0.15s, transform 0.15s; | |
| } | |
| /* Respawn button */ | |
| #respawn { | |
| padding: 16px 48px; | |
| font-size: 20px; | |
| font-weight: 700; | |
| color: #fff; | |
| background: linear-gradient(180deg, #059669 0%, #047857 100%); | |
| border: none; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| box-shadow: 0 6px 0 #065f46, 0 8px 20px rgba(6,95,70,0.4); | |
| transition: transform 0.1s, box-shadow 0.1s; | |
| display: none; | |
| } | |
| #respawn:hover { transform: translateY(-2px); box-shadow: 0 8px 0 #065f46, 0 10px 24px rgba(6,95,70,0.5); } | |
| #respawn:active { transform: translateY(3px); box-shadow: 0 3px 0 #065f46, 0 5px 12px rgba(6,95,70,0.3); } | |
| /* Particle canvas */ | |
| #particles { | |
| position: fixed; | |
| top: 0; left: 0; | |
| width: 100%; height: 100%; | |
| pointer-events: none; | |
| z-index: 100; | |
| } | |
| .label { | |
| color: rgba(255,255,255,0.5); | |
| font-size: 14px; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| margin-top: 8px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <canvas id="particles"></canvas> | |
| <div class="flash" id="flash"></div> | |
| <div id="button-container"> | |
| <button id="blue-button">CLICK ME</button> | |
| </div> | |
| <button id="respawn">RESPAWN</button> | |
| <div class="label">Click the button to explode it</div> | |
| <script> | |
| const canvas = document.getElementById('particles'); | |
| const ctx = canvas.getContext('2d'); | |
| const btn = document.getElementById('blue-button'); | |
| const respawn = document.getElementById('respawn'); | |
| const flash = document.getElementById('flash'); | |
| let particles = []; | |
| let exploded = false; | |
| function resize() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| resize(); | |
| window.addEventListener('resize', resize); | |
| // Particle class | |
| class Particle { | |
| constructor(x, y, color, vx, vy, size, life) { | |
| this.x = x; | |
| this.y = y; | |
| this.vx = vx; | |
| this.vy = vy; | |
| this.size = size; | |
| this.color = color; | |
| this.life = life; | |
| this.maxLife = life; | |
| this.gravity = 0.3; | |
| this.rotation = Math.random() * Math.PI * 2; | |
| this.rotSpeed = (Math.random() - 0.5) * 0.4; | |
| this.shape = Math.random() > 0.4 ? 'circle' : 'square'; | |
| } | |
| update() { | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| this.vy += this.gravity; | |
| this.vx *= 0.99; | |
| this.rotation += this.rotSpeed; | |
| this.life--; | |
| } | |
| draw() { | |
| const alpha = Math.max(0, this.life / this.maxLife); | |
| ctx.save(); | |
| ctx.globalAlpha = alpha; | |
| ctx.translate(this.x, this.y); | |
| ctx.rotate(this.rotation); | |
| ctx.fillStyle = this.color; | |
| if (this.shape === 'circle') { | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, this.size, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } else { | |
| ctx.fillRect(-this.size, -this.size, this.size * 2, this.size * 2); | |
| } | |
| // Glow | |
| ctx.shadowBlur = 12; | |
| ctx.shadowColor = this.color; | |
| ctx.beginPath(); | |
| if (this.shape === 'circle') { | |
| ctx.arc(0, 0, this.size * 0.6, 0, Math.PI * 2); | |
| } else { | |
| ctx.fillRect(-this.size * 0.6, -this.size * 0.6, this.size * 1.2, this.size * 1.2); | |
| } | |
| ctx.fill(); | |
| ctx.restore(); | |
| } | |
| } | |
| // Shockwave ring | |
| class Ring { | |
| constructor(x, y, color, maxRadius, lineWidth) { | |
| this.x = x; | |
| this.y = y; | |
| this.radius = 0; | |
| this.maxRadius = maxRadius; | |
| this.color = color; | |
| this.lineWidth = lineWidth; | |
| this.life = 30; | |
| this.maxLife = 30; | |
| } | |
| update() { | |
| this.radius += (this.maxRadius - this.radius) * 0.18; | |
| this.life--; | |
| } | |
| draw() { | |
| const alpha = Math.max(0, this.life / this.maxLife); | |
| ctx.save(); | |
| ctx.globalAlpha = alpha; | |
| ctx.strokeStyle = this.color; | |
| ctx.lineWidth = this.lineWidth * alpha; | |
| ctx.shadowBlur = 20; | |
| ctx.shadowColor = this.color; | |
| ctx.beginPath(); | |
| ctx.arc(this.x, this.y, Math.max(0.1, this.radius), 0, Math.PI * 2); | |
| ctx.stroke(); | |
| ctx.restore(); | |
| } | |
| } | |
| let rings = []; | |
| function explode() { | |
| if (exploded) return; | |
| exploded = true; | |
| const rect = btn.getBoundingClientRect(); | |
| const cx = rect.left + rect.width / 2; | |
| const cy = rect.top + rect.height / 2; | |
| // Flash | |
| flash.classList.remove('active'); | |
| void flash.offsetWidth; | |
| flash.classList.add('active'); | |
| // Shake | |
| document.body.classList.remove('shake'); | |
| void document.body.offsetWidth; | |
| document.body.classList.add('shake'); | |
| // Button vanish | |
| btn.classList.add('exploding'); | |
| // Colors — green palette | |
| const colors = [ | |
| '#22c55e', '#15803d', '#4ade80', '#16a34a', '#86efac', | |
| '#dcfce7', '#166534', '#6ee7b7', '#34d399', '#ffffff' | |
| ]; | |
| // Burst particles | |
| const count = 180; | |
| for (let i = 0; i < count; i++) { | |
| const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5) * 0.5; | |
| const speed = Math.random() * 18 + 6; | |
| const vx = Math.cos(angle) * speed; | |
| const vy = Math.sin(angle) * speed - Math.random() * 4; | |
| const size = Math.random() * 8 + 3; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| const life = Math.floor(Math.random() * 40 + 50); | |
| particles.push(new Particle(cx, cy, color, vx, vy, size, life)); | |
| } | |
| // Extra debris (smaller, faster) | |
| for (let i = 0; i < 60; i++) { | |
| const angle = Math.random() * Math.PI * 2; | |
| const speed = Math.random() * 28 + 10; | |
| const vx = Math.cos(angle) * speed; | |
| const vy = Math.sin(angle) * speed - Math.random() * 6; | |
| const size = Math.random() * 4 + 1; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| const life = Math.floor(Math.random() * 30 + 40); | |
| particles.push(new Particle(cx, cy, color, vx, vy, size, life)); | |
| } | |
| // Shockwave rings — green | |
| rings.push(new Ring(cx, cy, '#4ade80', 250, 8)); | |
| setTimeout(() => rings.push(new Ring(cx, cy, '#86efac', 180, 5)), 80); | |
| setTimeout(() => rings.push(new Ring(cx, cy, '#ffffff', 120, 3)), 160); | |
| // Sparkles | |
| for (let i = 0; i < 30; i++) { | |
| const angle = Math.random() * Math.PI * 2; | |
| const speed = Math.random() * 6 + 2; | |
| const vx = Math.cos(angle) * speed; | |
| const vy = Math.sin(angle) * speed - Math.random() * 3; | |
| const size = Math.random() * 3 + 2; | |
| const color = '#ffffff'; | |
| const life = Math.floor(Math.random() * 20 + 30); | |
| particles.push(new Particle(cx, cy, color, vx, vy, size, life)); | |
| } | |
| // Show respawn after delay | |
| setTimeout(() => { respawn.style.display = 'inline-block'; }, 400); | |
| } | |
| function respawnButton() { | |
| exploded = false; | |
| btn.classList.remove('exploding'); | |
| respawn.style.display = 'none'; | |
| } | |
| // Animation loop | |
| function animate() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Rings | |
| rings = rings.filter(r => r.life > 0); | |
| for (const r of rings) { r.update(); r.draw(); } | |
| // Particles | |
| particles = particles.filter(p => p.life > 0); | |
| for (const p of particles) { p.update(); p.draw(); } | |
| requestAnimationFrame(animate); | |
| } | |
| animate(); | |
| btn.addEventListener('click', explode); | |
| respawn.addEventListener('click', respawnButton); | |
| </script> | |
| </body> | |
| </html> |