From b24edcefca3ff77a9cb8cb2126f96b0c36296279 Mon Sep 17 00:00:00 2001 From: Jonathan Berrisch Date: Wed, 11 Jun 2025 14:31:53 +0200 Subject: [PATCH] Add glitter --- app.js | 170 ++++++++++++++++++++++++++++++++++++++++++++++---- styles.css | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 11 deletions(-) diff --git a/app.js b/app.js index 3c5dc63..cd617fc 100644 --- a/app.js +++ b/app.js @@ -30,17 +30,144 @@ return; } - var svgData = new XMLSerializer().serializeToString(svg); - var svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); - var svgUrl = URL.createObjectURL(svgBlob); + // Trigger glitter effect on QR code + triggerQRGlitterEffect(); + + // Small delay to show the effect before download + setTimeout(function() { + var svgData = new XMLSerializer().serializeToString(svg); + var svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); + var svgUrl = URL.createObjectURL(svgBlob); - var downloadLink = document.createElement("a"); - downloadLink.href = svgUrl; - downloadLink.download = "qrcode.svg"; - document.body.appendChild(downloadLink); - downloadLink.click(); - document.body.removeChild(downloadLink); - URL.revokeObjectURL(svgUrl); + var downloadLink = document.createElement("a"); + downloadLink.href = svgUrl; + downloadLink.download = "qrcode.svg"; + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); + URL.revokeObjectURL(svgUrl); + }, 300); + } + + function getRandomGlitterColor() { + const colors = [ + '#FFD700', // Gold + '#FF69B4', // Hot Pink + '#00CED1', // Dark Turquoise + '#FF6347', // Tomato + '#9370DB', // Medium Purple + '#32CD32', // Lime Green + '#FF1493', // Deep Pink + '#00BFFF', // Deep Sky Blue + '#FFA500', // Orange + '#DA70D6', // Orchid + '#ADFF2F', // Green Yellow + '#FF4500', // Orange Red + '#7B68EE', // Medium Slate Blue + '#00FF7F', // Spring Green + '#DC143C', // Crimson + '#40E0D0' // Turquoise + ]; + return colors[Math.floor(Math.random() * colors.length)]; + } + + function createGlitter(container) { + const glitter = document.createElement('div'); + glitter.className = 'glitter'; + + // Random position within button + const x = Math.random() * container.offsetWidth; + const y = Math.random() * container.offsetHeight; + + glitter.style.left = x + 'px'; + glitter.style.top = y + 'px'; + + // Random color + const color = getRandomGlitterColor(); + glitter.style.background = color; + glitter.style.boxShadow = `0 0 6px ${color}, 0 0 12px ${color}, 0 0 18px ${color}`; + + // Random animation type + const animations = ['anim1', 'anim2', 'anim3']; + const randomAnim = animations[Math.floor(Math.random() * animations.length)]; + glitter.classList.add(randomAnim); + + // Random delay + glitter.style.animationDelay = Math.random() * 0.5 + 's'; + + container.appendChild(glitter); + + // Remove glitter after animation + setTimeout(() => { + if (glitter.parentNode) { + glitter.parentNode.removeChild(glitter); + } + }, 2000); + } + + function startGlitterEffect(button) { + const container = button.querySelector('.glitter-container'); + if (!container) return; + + // Create multiple glitter particles + for (let i = 0; i < 3; i++) { + setTimeout(() => createGlitter(container), i * 200); + } + } + + function createQRGlitter(container) { + const glitter = document.createElement('div'); + glitter.className = 'qr-glitter'; + + // Random position within QR code area + const x = Math.random() * container.offsetWidth; + const y = Math.random() * container.offsetHeight; + + glitter.style.left = x + 'px'; + glitter.style.top = y + 'px'; + + // Random color + const color = getRandomGlitterColor(); + glitter.style.background = color; + glitter.style.boxShadow = `0 0 8px ${color}, 0 0 16px ${color}, 0 0 24px ${color}`; + + // Random animation type + const animations = ['anim1', 'anim2', 'anim3']; + const randomAnim = animations[Math.floor(Math.random() * animations.length)]; + glitter.classList.add(randomAnim); + + // Random delay + glitter.style.animationDelay = Math.random() * 0.3 + 's'; + + container.appendChild(glitter); + + // Remove glitter after animation + setTimeout(() => { + if (glitter.parentNode) { + glitter.parentNode.removeChild(glitter); + } + }, 2500); + } + + function triggerQRGlitterEffect() { + const qrcode = document.getElementById('qrcode'); + let container = qrcode.querySelector('.qr-glitter-container'); + + // Create container if it doesn't exist + if (!container) { + container = document.createElement('div'); + container.className = 'qr-glitter-container'; + qrcode.appendChild(container); + } + + // Create multiple waves of glitter + for (let wave = 0; wave < 3; wave++) { + setTimeout(() => { + for (let i = 0; i < 5; i++) { + setTimeout(() => createQRGlitter(container), i * 100); + } + }, wave * 300); + } } function initializeApp() { @@ -55,6 +182,11 @@ var colorInput = document.getElementById("color"); var downloadButton = document.getElementById("download"); + // Create glitter container for download button + const glitterContainer = document.createElement('div'); + glitterContainer.className = 'glitter-container'; + downloadButton.appendChild(glitterContainer); + // Text input events textInput.addEventListener('input', debouncedMakeCode); textInput.addEventListener('blur', makeCode); @@ -67,8 +199,24 @@ // Color input events colorInput.addEventListener('change', makeCode); - // Download button event + // Download button events downloadButton.addEventListener('click', downloadSVG); + + // Glitter effect on hover + let glitterInterval; + downloadButton.addEventListener('mouseenter', function() { + startGlitterEffect(downloadButton); + glitterInterval = setInterval(() => { + startGlitterEffect(downloadButton); + }, 600); + }); + + downloadButton.addEventListener('mouseleave', function() { + if (glitterInterval) { + clearInterval(glitterInterval); + glitterInterval = null; + } + }); } // Initialize when DOM is ready diff --git a/styles.css b/styles.css index 07954ce..197fec7 100644 --- a/styles.css +++ b/styles.css @@ -75,6 +75,8 @@ h1 { font-weight: 500; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; + position: relative; + overflow: hidden; } #download:hover { @@ -82,6 +84,94 @@ h1 { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); } +.glitter-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + overflow: hidden; + border-radius: 25px; +} + +.glitter { + position: absolute; + width: 4px; + height: 4px; + border-radius: 50%; + pointer-events: none; + opacity: 0; +} + +@keyframes glitter1 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0); + } + + 50% { + opacity: 1; + transform: translateY(-20px) translateX(10px) scale(1); + } + + 100% { + opacity: 0; + transform: translateY(-40px) translateX(20px) scale(0); + } +} + +@keyframes glitter2 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0) rotate(0deg); + } + + 50% { + opacity: 1; + transform: translateY(-15px) translateX(-15px) scale(1.2) rotate(180deg); + } + + 100% { + opacity: 0; + transform: translateY(-30px) translateX(-30px) scale(0) rotate(360deg); + } +} + +@keyframes glitter3 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0); + } + + 30% { + opacity: 1; + transform: translateY(-10px) translateX(5px) scale(0.8); + } + + 70% { + opacity: 1; + transform: translateY(-25px) translateX(-5px) scale(1.1); + } + + 100% { + opacity: 0; + transform: translateY(-35px) translateX(-10px) scale(0); + } +} + +.glitter.anim1 { + animation: glitter1 1.5s ease-out infinite; +} + +.glitter.anim2 { + animation: glitter2 1.8s ease-out infinite; +} + +.glitter.anim3 { + animation: glitter3 1.2s ease-out infinite; +} + .qr-container { margin: 30px 0; display: flex; @@ -91,12 +181,14 @@ h1 { border-radius: 15px; padding: 20px; overflow: visible; + position: relative; } #qrcode { display: flex; justify-content: center; align-items: center; + position: relative; } #qrcode svg { @@ -111,6 +203,94 @@ h1 { display: block; } +.qr-glitter-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + overflow: visible; + z-index: 10; +} + +.qr-glitter { + position: absolute; + width: 6px; + height: 6px; + border-radius: 50%; + pointer-events: none; + opacity: 0; +} + +@keyframes qrGlitter1 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0); + } + + 50% { + opacity: 1; + transform: translateY(-30px) translateX(15px) scale(1); + } + + 100% { + opacity: 0; + transform: translateY(-60px) translateX(30px) scale(0); + } +} + +@keyframes qrGlitter2 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0) rotate(0deg); + } + + 50% { + opacity: 1; + transform: translateY(-25px) translateX(-20px) scale(1.3) rotate(180deg); + } + + 100% { + opacity: 0; + transform: translateY(-50px) translateX(-40px) scale(0) rotate(360deg); + } +} + +@keyframes qrGlitter3 { + 0% { + opacity: 0; + transform: translateY(0) translateX(0) scale(0); + } + + 30% { + opacity: 1; + transform: translateY(-15px) translateX(8px) scale(0.9); + } + + 70% { + opacity: 1; + transform: translateY(-35px) translateX(-8px) scale(1.2); + } + + 100% { + opacity: 0; + transform: translateY(-55px) translateX(-15px) scale(0); + } +} + +.qr-glitter.anim1 { + animation: qrGlitter1 2s ease-out; +} + +.qr-glitter.anim2 { + animation: qrGlitter2 2.2s ease-out; +} + +.qr-glitter.anim3 { + animation: qrGlitter3 1.8s ease-out; +} + .footer { margin-top: 20px; color: #666;