Spinners are essential UI elements that enhance user experience by indicating loading processes. They keep users engaged and informed, ensuring a seamless interaction.
In this article, we explore 10 spinner examples that can elevate your web design. Each example showcases unique styles and functionalities to inspire your next project.
CODE1
Here's the code:
CODETEXT1
CODE2
Here's the code:
CODETEXT2
CODE3
Here's the code:
CODETEXT3
CODE4
Here's the code:
CODETEXT4
CODE5
Here's the code:
CODETEXT5
Designing your own spinner has never been easier with Subframe. Its drag-and-drop interface and intuitive, responsive canvas ensure pixel-perfect UI every time. Loved by designers and developers alike, Subframe makes the design process seamless and efficient.
Start for free and elevate your web design today!
CODE6
Here's the code:
CODETEXT6
CODE7
Here's the code:
CODETEXT7
CODE8
Here's the code:
CODETEXT8
CODE9
Here's the code:
CODETEXT9
CODE10
Here's the code:
CODETEXT10
Ready to elevate your UI design game? With Subframe, you can create pixel-perfect interfaces, including spinners, in minutes. Its drag-and-drop editor ensures efficiency and precision.
Don't wait! Start for free and begin designing stunning UIs immediately. Experience the ease and power of Subframe today!
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Social Media Loader</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; } body { width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #f9f9fb; overflow: hidden; } .loader-container { position: relative; width: 100%; max-width: 500px; height: 450px; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: white; border-radius: 24px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.05); padding: 2rem; overflow: hidden; transition: all 0.4s ease; } .loader-container:hover { transform: translateY(-5px); box-shadow: 0 18px 42px rgba(0, 0, 0, 0.08); } .loader-heading { font-size: 1.8rem; font-weight: 700; color: #333; margin-bottom: 1rem; text-align: center; } .loader-subheading { font-size: 1rem; font-weight: 400; color: #666; text-align: center; margin-bottom: 2.5rem; max-width: 320px; line-height: 1.5; } .loader-wrapper { position: relative; width: 120px; height: 120px; margin-bottom: 2.5rem; } .spinner { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 4px solid transparent; border-top-color: #6366F1; animation: spin 1.8s cubic-bezier(0.39, 0.575, 0.565, 1) infinite; } .spinner:nth-child(1) { border-top-color: #6366F1; } .spinner:nth-child(2) { border-top-color: #8B5CF6; width: 85%; height: 85%; top: 7.5%; left: 7.5%; animation-delay: -0.2s; animation-duration: 1.6s; } .spinner:nth-child(3) { border-top-color: #EC4899; width: 70%; height: 70%; top: 15%; left: 15%; animation-delay: -0.4s; animation-duration: 1.4s; } .spinner-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 50%; height: 50%; background: linear-gradient(135deg, #6366F1, #EC4899); border-radius: 50%; display: flex; justify-content: center; align-items: center; color: white; font-size: 0.75rem; font-weight: 600; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); animation: pulse 2s cubic-bezier(0.4, 0, 0.2, 1) infinite; } .status-container { display: flex; flex-direction: column; align-items: center; } .status-text { font-size: 1rem; font-weight: 600; color: #333; margin-bottom: 0.5rem; } .progress-text { font-size: 0.85rem; color: #666; margin-bottom: 1rem; min-height: 20px; } .activity-indicator { display: flex; gap: 8px; margin-bottom: 1.5rem; } .activity-dot { width: 8px; height: 8px; border-radius: 50%; background-color: #d1d5db; } .activity-dot.active { animation: fadeInOut 1.5s infinite; } .activity-dot:nth-child(1).active { animation-delay: 0s; background-color: #6366F1; } .activity-dot:nth-child(2).active { animation-delay: 0.3s; background-color: #8B5CF6; } .activity-dot:nth-child(3).active { animation-delay: 0.6s; background-color: #EC4899; } .cancel-button { background: none; border: none; color: #6366F1; font-size: 0.9rem; font-weight: 600; cursor: pointer; padding: 8px 16px; border-radius: 8px; transition: all 0.2s ease; } .cancel-button:hover { background-color: rgba(99, 102, 241, 0.1); } .pulse-ring { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 150%; height: 150%; border-radius: 50%; border: 2px solid rgba(99, 102, 241, 0.5); opacity: 0; animation: pulse-ring 3s cubic-bezier(0.39, 0.575, 0.565, 1) infinite; } .pulse-ring:nth-child(2) { animation-delay: 1s; } .network-icon { position: absolute; width: 28px; height: 28px; border-radius: 50%; background: white; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); display: flex; justify-content: center; align-items: center; animation: float 3s infinite ease-in-out; } .network-icon:nth-child(1) { top: 10%; left: 15%; animation-delay: 0.2s; } .network-icon:nth-child(2) { top: 20%; right: 15%; animation-delay: 0.5s; } .network-icon:nth-child(3) { bottom: 15%; left: 20%; animation-delay: 0.8s; } .network-icon:nth-child(4) { bottom: 10%; right: 20%; animation-delay: 1.1s; } .network-icon i { font-size: 1rem; color: #6366F1; } @keyframes spin { 0% { transform: rotate(0deg); } 80%, 100% { transform: rotate(360deg); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } @keyframes fadeInOut { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } } @keyframes pulse-ring { 0% { opacity: 0.5; transform: translate(-50%, -50%) scale(0.8); } 100% { opacity: 0; transform: translate(-50%, -50%) scale(1.2); } } @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } } @media (max-width: 600px) { .loader-container { max-width: 90%; height: auto; padding: 1.5rem; } .loader-heading { font-size: 1.5rem; } .loader-wrapper { width: 100px; height: 100px; } } </style> </head> <body> <div class="loader-container"> <div class="network-icon"><i>📸</i></div> <div class="network-icon"><i>📱</i></div> <div class="network-icon"><i>💬</i></div> <div class="network-icon"><i>❤️</i></div> <h1 class="loader-heading">Syncing your social world</h1> <p class="loader-subheading">We're connecting to your friends and updating your feeds with the latest content</p> <div class="loader-wrapper"> <div class="pulse-ring"></div> <div class="pulse-ring"></div> <div class="spinner"></div> <div class="spinner"></div> <div class="spinner"></div> <div class="spinner-center"> <span id="percentage">0%</span> </div> </div> <div class="status-container"> <div class="status-text" id="status-text">Establishing connection</div> <div class="progress-text" id="progress-text"></div> <div class="activity-indicator"> <div class="activity-dot active"></div> <div class="activity-dot active"></div> <div class="activity-dot active"></div> </div> <button class="cancel-button" id="cancel-button">Cancel sync</button> </div> </div> <script> const statusMessages = [ "Establishing connection", "Retrieving friend updates", "Syncing message history", "Downloading media items", "Updating story feed", "Syncing profile changes", "Finishing up" ]; const progressMessages = [ "Connecting to remote servers...", "Scanning for new content...", "Backing up your messages...", "Processing new photos and videos...", "Refreshing your timeline...", "Applying latest settings...", "Almost there!" ]; const statusText = document.getElementById('status-text'); const progressText = document.getElementById('progress-text'); const percentageText = document.getElementById('percentage'); const cancelButton = document.getElementById('cancel-button'); let currentStep = 0; let percentage = 0; let progressInterval; let messageInterval; function startSync() { updateStatusMessage(); progressInterval = setInterval(() => { percentage += 1; if (percentage > 100) { percentage = 100; clearInterval(progressInterval); completeSync(); return; } percentageText.textContent = `${percentage}%`; // Change status message at certain percentage milestones if (percentage === 15) changeStatusMessage(1); if (percentage === 35) changeStatusMessage(2); if (percentage === 50) changeStatusMessage(3); if (percentage === 65) changeStatusMessage(4); if (percentage === 80) changeStatusMessage(5); if (percentage === 95) changeStatusMessage(6); }, 120); // Cycle through different progress messages messageInterval = setInterval(() => { progressText.textContent = getRandomSubProgressMessage(); }, 2500); } function changeStatusMessage(step) { currentStep = step; updateStatusMessage(); } function updateStatusMessage() { statusText.textContent = statusMessages[currentStep]; progressText.textContent = progressMessages[currentStep]; } function getRandomSubProgressMessage() { const subMessages = [ "Processing data packet " + Math.floor(Math.random() * 100) + " of 100", "Checking for " + Math.floor(Math.random() * 30) + " new notifications", "Authenticating with secure servers", "Optimizing your experience", "Applying privacy settings", "Buffering media content", "Checking for updates" ]; return subMessages[Math.floor(Math.random() * subMessages.length)]; } function completeSync() { clearInterval(messageInterval); statusText.textContent = "Sync completed successfully!"; progressText.textContent = "Your social media is now up to date"; cancelButton.textContent = "Done"; // Reset the animation after 3 seconds setTimeout(() => { percentage = 0; currentStep = 0; percentageText.textContent = `${percentage}%`; cancelButton.textContent = "Cancel sync"; startSync(); }, 3000); } cancelButton.addEventListener('click', function() { if (cancelButton.textContent === "Done") { percentage = 0; currentStep = 0; percentageText.textContent = `${percentage}%`; cancelButton.textContent = "Cancel sync"; startSync(); return; } // Cancel the sync clearInterval(progressInterval); clearInterval(messageInterval); statusText.textContent = "Sync cancelled"; progressText.textContent = "You can restart the sync anytime"; cancelButton.textContent = "Restart"; // Animate percentage back to zero const decreaseInterval = setInterval(() => { percentage -= 2; if (percentage <= 0) { percentage = 0; clearInterval(decreaseInterval); } percentageText.textContent = `${percentage}%`; }, 20); }); // Start the animation when the page loads window.addEventListener('load', startSync); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Corporate Minimalist Spinner</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Arial', sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 700px; width: 100%; background-color: #f9f9f9; overflow: hidden; } .dashboard-container { width: 650px; height: 650px; background-color: #ffffff; border-radius: 10px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05); padding: 2rem; display: flex; flex-direction: column; position: relative; overflow: hidden; } .dashboard-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; } .dashboard-title { font-size: 1.5rem; font-weight: 600; color: #333; } .dashboard-subtitle { font-size: 0.9rem; color: #888; margin-top: 0.25rem; } .loading-container { position: relative; flex-grow: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; } .spinner-container { position: relative; width: 180px; height: 180px; margin-bottom: 2rem; } .spinner { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 4px solid transparent; border-top-color: #333; animation: spin 1.5s linear infinite; } .spinner-inner { position: absolute; top: 15px; left: 15px; right: 15px; bottom: 15px; border-radius: 50%; border: 4px solid transparent; border-top-color: #888; animation: spin 2s linear infinite reverse; } .spinner-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 30px; height: 30px; background-color: #333; border-radius: 50%; z-index: 2; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } .spinner-pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 180px; height: 180px; border-radius: 50%; background-color: rgba(51, 51, 51, 0.05); animation: pulse 2s ease-out infinite; } .progress-text { font-size: 1.2rem; font-weight: 500; color: #333; margin-top: 1rem; letter-spacing: 0.05em; } .status-message { font-size: 0.9rem; color: #888; margin-top: 0.5rem; max-width: 300px; text-align: center; line-height: 1.5; } .dashboard-footer { margin-top: auto; display: flex; justify-content: space-between; align-items: center; padding-top: 1.5rem; border-top: 1px solid #f0f0f0; } .cancel-btn { background: none; border: none; color: #888; font-size: 0.9rem; cursor: pointer; transition: color 0.3s; } .cancel-btn:hover { color: #333; } .progress-bar { position: absolute; bottom: 0; left: 0; height: 4px; background-color: #333; width: 0%; transition: width 0.3s ease; } .timestamp { font-size: 0.8rem; color: #aaa; } .speed-control { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.5rem; } .speed-btn { background: none; border: 1px solid #eee; padding: 0.2rem 0.5rem; font-size: 0.8rem; border-radius: 3px; cursor: pointer; transition: all 0.3s; } .speed-btn:hover, .speed-btn.active { background-color: #f0f0f0; color: #333; } .speed-btn.active { border-color: #ddd; font-weight: 600; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes pulse { 0% { transform: translate(-50%, -50%) scale(0.8); opacity: 0.7; } 100% { transform: translate(-50%, -50%) scale(1.2); opacity: 0; } } @media (max-width: 700px) { .dashboard-container { width: 95%; height: auto; min-height: 500px; } .spinner-container { width: 120px; height: 120px; } .spinner-pulse { width: 120px; height: 120px; } .dashboard-header { flex-direction: column; align-items: flex-start; } } /* Dark mode toggle */ .mode-switch { position: absolute; top: 1rem; right: 1rem; width: 40px; height: 20px; background-color: #eee; border-radius: 10px; cursor: pointer; transition: background-color 0.3s; } .mode-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background-color: #fff; border-radius: 50%; transition: transform 0.3s; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } body.dark-mode { background-color: #1a1a1a; } body.dark-mode .dashboard-container { background-color: #222; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); } body.dark-mode .dashboard-title, body.dark-mode .progress-text { color: #f0f0f0; } body.dark-mode .dashboard-subtitle, body.dark-mode .status-message, body.dark-mode .cancel-btn { color: #aaa; } body.dark-mode .spinner { border-top-color: #f0f0f0; } body.dark-mode .spinner-inner { border-top-color: #aaa; } body.dark-mode .spinner-center { background-color: #f0f0f0; } body.dark-mode .dashboard-footer { border-top-color: #333; } body.dark-mode .progress-bar { background-color: #f0f0f0; } body.dark-mode .speed-btn { border-color: #333; color: #aaa; } body.dark-mode .speed-btn:hover, body.dark-mode .speed-btn.active { background-color: #333; color: #f0f0f0; } body.dark-mode .mode-switch { background-color: #666; } body.dark-mode .mode-switch::after { transform: translateX(20px); background-color: #222; } </style> </head> <body> <div class="dashboard-container"> <div class="dashboard-header"> <div> <h1 class="dashboard-title">Data Processing</h1> <p class="dashboard-subtitle">Enterprise Analytics Pipeline</p> </div> </div> <div class="mode-switch" id="modeSwitch"></div> <div class="loading-container"> <div class="spinner-pulse"></div> <div class="spinner-container"> <div class="spinner"></div> <div class="spinner-inner"></div> <div class="spinner-center"></div> </div> <p class="progress-text" id="progressText">Processing 45%</p> <p class="status-message" id="statusMessage">Aggregating quarterly performance metrics across departments</p> <div class="speed-control"> <button class="speed-btn" data-speed="slow">Slow</button> <button class="speed-btn active" data-speed="normal">Normal</button> <button class="speed-btn" data-speed="fast">Fast</button> </div> </div> <div class="dashboard-footer"> <button class="cancel-btn">Cancel Process</button> <span class="timestamp" id="timestamp">Last update: Just now</span> </div> <div class="progress-bar" id="progressBar"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Initial progress values let progress = 45; let speed = 1; // Status messages for different progress stages const statusMessages = [ "Initializing secure data connection...", "Collecting interdepartmental metrics...", "Analyzing year-over-year performance trends...", "Aggregating quarterly performance metrics across departments...", "Generating executive dashboard visualizations...", "Applying predictive analytics models...", "Cross-referencing financial indicators...", "Finalizing data integrity verification...", "Preparing comprehensive quarterly reports...", "Process complete. Optimizing dashboard view..." ]; // Elements const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); const statusMessage = document.getElementById('statusMessage'); const timestamp = document.getElementById('timestamp'); const cancelBtn = document.querySelector('.cancel-btn'); const speedBtns = document.querySelectorAll('.speed-btn'); const modeSwitch = document.getElementById('modeSwitch'); // Update progress bar and text function updateProgress() { if (progress < 100) { progress += (0.2 * speed); if (progress > 100) progress = 100; progressBar.style.width = `${progress}%`; progressText.textContent = `Processing ${Math.floor(progress)}%`; // Update status message based on progress const messageIndex = Math.min(Math.floor(progress / 11), statusMessages.length - 1); statusMessage.textContent = statusMessages[messageIndex]; // Update timestamp periodically if (Math.random() > 0.9) { const now = new Date(); const timeString = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}); timestamp.textContent = `Last update: ${timeString}`; } setTimeout(updateProgress, 100); } else { progressText.textContent = 'Process Complete'; statusMessage.textContent = statusMessages[statusMessages.length - 1]; cancelBtn.textContent = 'View Results'; } } // Initialize the progress setTimeout(updateProgress, 500); // Speed control speedBtns.forEach(btn => { btn.addEventListener('click', function() { speedBtns.forEach(b => b.classList.remove('active')); this.classList.add('active'); const selectedSpeed = this.getAttribute('data-speed'); switch(selectedSpeed) { case 'slow': speed = 0.5; break; case 'normal': speed = 1; break; case 'fast': speed = 2; break; } }); }); // Dark mode toggle modeSwitch.addEventListener('click', function() { document.body.classList.toggle('dark-mode'); }); // Cancel button hover animation cancelBtn.addEventListener('mouseenter', function() { this.style.transition = 'transform 0.3s'; this.style.transform = 'translateY(-2px)'; }); cancelBtn.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); // Reset button cancelBtn.addEventListener('click', function() { if (progress < 100) { progress = 0; progressBar.style.width = '0%'; progressText.textContent = 'Process Cancelled'; statusMessage.textContent = 'Operation has been cancelled. Click to restart.'; setTimeout(() => { progress = 0; updateProgress(); }, 2000); } else { // Reset after completion progress = 0; progressBar.style.width = '0%'; cancelBtn.textContent = 'Cancel Process'; setTimeout(updateProgress, 500); } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Finance App Loader</title> <style> :root { --primary: #476C9B; --secondary: #984447; --accent: #ADD9F4; --neutral-dark: #2B2D42; --neutral-light: #EDF2F4; --gold: #D5A021; --silver: #C0C0C0; --platinum: #E5E4E2; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, var(--neutral-dark) 0%, #1A1B29 100%); overflow: hidden; } .app-container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; position: relative; padding: 2rem; } .demo-screen { width: 100%; max-width: 400px; background: var(--neutral-dark); border-radius: 16px; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3); padding: 2rem; color: var(--neutral-light); position: relative; overflow: hidden; z-index: 1; } .demo-screen::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.2) 100%); z-index: -1; } .demo-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .demo-logo { display: flex; align-items: center; } .demo-logo-icon { width: 24px; height: 24px; background: var(--gold); border-radius: 6px; margin-right: 8px; display: flex; justify-content: center; align-items: center; } .demo-logo-icon::before { content: '$'; font-weight: bold; color: var(--neutral-dark); font-size: 16px; } .demo-logo-text { font-weight: 600; font-size: 18px; color: var(--neutral-light); } .demo-user { width: 32px; height: 32px; background: var(--primary); border-radius: 50%; display: flex; justify-content: center; align-items: center; color: var(--neutral-light); font-weight: bold; font-size: 14px; cursor: pointer; transition: all 0.3s ease; } .demo-user:hover { transform: scale(1.1); box-shadow: 0 0 15px rgba(71, 108, 155, 0.5); } .loader-container { width: 100%; height: 280px; display: flex; flex-direction: column; justify-content: center; align-items: center; position: relative; margin-bottom: 1.5rem; } .loader-spinner { width: 100px; height: 100px; position: relative; margin-bottom: 2rem; } .spinner-segment { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 4px solid transparent; box-sizing: border-box; } .segment-1 { border-top-color: var(--platinum); animation: spin1 1.5s ease-in-out infinite; } .segment-2 { border-right-color: var(--silver); animation: spin2 1.5s ease-in-out infinite; } .segment-3 { border-bottom-color: var(--gold); animation: spin3 1.5s ease-in-out infinite; } @keyframes spin1 { 0% { transform: rotate(0deg); border-width: 4px; } 50% { transform: rotate(180deg); border-width: 6px; } 100% { transform: rotate(360deg); border-width: 4px; } } @keyframes spin2 { 0% { transform: rotate(90deg); border-width: 4px; } 50% { transform: rotate(270deg); border-width: 6px; } 100% { transform: rotate(450deg); border-width: 4px; } } @keyframes spin3 { 0% { transform: rotate(180deg); border-width: 4px; } 50% { transform: rotate(360deg); border-width: 6px; } 100% { transform: rotate(540deg); border-width: 4px; } } .loader-inner { position: absolute; width: 70px; height: 70px; background: linear-gradient(135deg, var(--silver) 0%, var(--platinum) 100%); border-radius: 50%; display: flex; justify-content: center; align-items: center; color: var(--neutral-dark); font-weight: bold; font-size: 12px; box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.2); animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { 0% { transform: scale(1); opacity: 0.8; } 50% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); opacity: 0.8; } } .loader-text { position: absolute; bottom: 0; left: 0; width: 100%; text-align: center; font-size: 16px; font-weight: 500; color: var(--neutral-light); opacity: 0.9; } .status-text { display: inline-block; min-width: 200px; position: relative; text-align: center; margin-top: 10px; font-size: 14px; color: var(--accent); opacity: 0; animation: fadeInOut 3s ease-in-out infinite; } @keyframes fadeInOut { 0%, 100% { opacity: 0; } 50% { opacity: 1; } } .transaction-info { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 1.2rem; margin-bottom: 1.5rem; border: 1px solid rgba(255, 255, 255, 0.1); } .transaction-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.8rem; } .transaction-title { font-size: 14px; font-weight: 600; color: var(--neutral-light); } .transaction-amount { font-size: 14px; font-weight: 700; color: var(--gold); } .transaction-details { display: flex; flex-direction: column; gap: 0.5rem; } .detail-row { display: flex; justify-content: space-between; font-size: 12px; } .detail-label { color: rgba(255, 255, 255, 0.5); } .detail-value { color: var(--neutral-light); font-weight: 500; } .cancel-button { width: 100%; padding: 0.9rem; background: transparent; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; color: var(--neutral-light); font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; } .cancel-button:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.3); } .geometric-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; overflow: hidden; } .geometric-shape { position: absolute; background: linear-gradient(135deg, var(--primary) 0%, rgba(71, 108, 155, 0.3) 100%); opacity: 0.1; border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } .shape-1 { width: 400px; height: 400px; top: -150px; left: -150px; animation: float1 20s ease-in-out infinite; } .shape-2 { width: 300px; height: 300px; bottom: -100px; right: -100px; animation: float2 15s ease-in-out infinite; } @keyframes float1 { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 50% { transform: translate(30px, 30px) rotate(5deg); } } @keyframes float2 { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 50% { transform: translate(-30px, -20px) rotate(-5deg); } } /* Responsive adjustments */ @media (max-width: 500px) { .demo-screen { max-width: 320px; padding: 1.5rem; } .loader-spinner { width: 80px; height: 80px; } .loader-inner { width: 56px; height: 56px; font-size: 10px; } } @media (max-width: 400px) { .demo-screen { max-width: 280px; padding: 1rem; } .transaction-info { padding: 1rem; } } </style> </head> <body> <div class="app-container"> <div class="demo-screen"> <div class="demo-header"> <div class="demo-logo"> <div class="demo-logo-icon"></div> <div class="demo-logo-text">FinancePro</div> </div> <div class="demo-user">JP</div> </div> <div class="loader-container"> <div class="loader-spinner"> <div class="spinner-segment segment-1"></div> <div class="spinner-segment segment-2"></div> <div class="spinner-segment segment-3"></div> <div class="loader-inner"> <span id="progress-percentage">67%</span> </div> </div> <div class="loader-text">Processing Transaction</div> <div class="status-text" id="status-message">Verifying account details...</div> </div> <div class="transaction-info"> <div class="transaction-header"> <div class="transaction-title">International Wire Transfer</div> <div class="transaction-amount">$12,450.00</div> </div> <div class="transaction-details"> <div class="detail-row"> <div class="detail-label">From Account</div> <div class="detail-value">••••8742</div> </div> <div class="detail-row"> <div class="detail-label">Recipient</div> <div class="detail-value">Globex Corp Ltd.</div> </div> <div class="detail-row"> <div class="detail-label">Reference</div> <div class="detail-value">INV-20231105</div> </div> <div class="detail-row"> <div class="detail-label">Fee</div> <div class="detail-value">$25.00</div> </div> </div> </div> <button class="cancel-button" id="cancel-btn">Cancel Transaction</button> </div> <div class="geometric-bg"> <div class="geometric-shape shape-1"></div> <div class="geometric-shape shape-2"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const statusMessages = [ "Verifying account details...", "Processing currency conversion...", "Calculating exchange rates...", "Applying regulatory checks...", "Confirming recipient details...", "Finalizing transaction..." ]; const progressNumbers = [34, 48, 67, 72, 85, 93, 97]; let currentStatusIndex = 0; let currentProgressIndex = 0; const statusMessageElement = document.getElementById('status-message'); const progressPercentageElement = document.getElementById('progress-percentage'); const cancelBtn = document.getElementById('cancel-btn'); // Update status message function updateStatusMessage() { statusMessageElement.textContent = statusMessages[currentStatusIndex]; currentStatusIndex = (currentStatusIndex + 1) % statusMessages.length; } // Update progress percentage function updateProgressPercentage() { if (currentProgressIndex < progressNumbers.length) { progressPercentageElement.textContent = progressNumbers[currentProgressIndex] + '%'; currentProgressIndex++; } } // Initialize intervals const statusInterval = setInterval(updateStatusMessage, 3000); const progressInterval = setInterval(updateProgressPercentage, 2500); // Cancel button with visual feedback cancelBtn.addEventListener('click', function() { this.textContent = "Cancelling..."; this.style.background = "rgba(152, 68, 71, 0.2)"; this.style.borderColor = "var(--secondary)"; this.style.color = "var(--secondary)"; setTimeout(() => { clearInterval(statusInterval); clearInterval(progressInterval); statusMessageElement.textContent = "Transaction cancelled"; statusMessageElement.style.color = "var(--secondary)"; statusMessageElement.style.opacity = "1"; statusMessageElement.style.animation = "none"; progressPercentageElement.textContent = "0%"; // Reset the spinner animation document.querySelector('.segment-1').style.animationPlayState = 'paused'; document.querySelector('.segment-2').style.animationPlayState = 'paused'; document.querySelector('.segment-3').style.animationPlayState = 'paused'; document.querySelector('.loader-inner').style.animationPlayState = 'paused'; // Update loader text document.querySelector('.loader-text').textContent = "Transaction Cancelled"; document.querySelector('.loader-text').style.color = "var(--secondary)"; }, 1000); }); // Add subtle hover effect to transaction info const transactionInfo = document.querySelector('.transaction-info'); transactionInfo.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-2px)'; this.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.2)'; this.style.transition = 'all 0.3s ease'; }); transactionInfo.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = 'none'; }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CartSpin - E-Commerce Loading Spinner</title> <style> :root { --primary: #FF4E50; --secondary: #FC913A; --tertiary: #F9D423; --accent: #5CDBBD; --accent-dark: #1F8A70; --bg: #FAFAFA; --text: #212121; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: var(--bg); color: var(--text); overflow: hidden; width: 700px; height: 700px; margin: 0 auto; } .spinner-container { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; padding: 2rem; position: relative; } .logo-container { position: relative; width: 100px; height: 100px; background-color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); z-index: 10; animation: pulse 2s infinite ease-in-out; } .logo { width: 60px; height: 60px; position: relative; } .cart-icon { font-size: 2.5rem; color: var(--accent-dark); } .spinner { position: absolute; width: 160px; height: 160px; border-radius: 50%; display: flex; align-items: center; justify-content: center; z-index: 1; } .spinner-ring { border-radius: 50%; position: absolute; border: 5px solid transparent; animation: spin 1.5s linear infinite; } .spinner-ring-1 { width: 160px; height: 160px; border-top-color: var(--primary); animation-delay: 0s; } .spinner-ring-2 { width: 140px; height: 140px; border-right-color: var(--secondary); animation-delay: 0.2s; animation-direction: reverse; } .spinner-ring-3 { width: 120px; height: 120px; border-bottom-color: var(--tertiary); animation-delay: 0.4s; } .spinner-ring-4 { width: 180px; height: 180px; border-left-color: var(--accent); animation-delay: 0.6s; animation-direction: reverse; } .loading-text { position: absolute; bottom: 1.5rem; font-size: 1.2rem; color: var(--text); text-align: center; max-width: 80%; animation: fadeInOut 2s infinite ease-in-out; } .particles { position: absolute; width: 100%; height: 100%; z-index: 0; } .particle { position: absolute; width: 8px; height: 8px; background-color: var(--tertiary); border-radius: 50%; opacity: 0; animation: particleAnimation 4s ease-in-out infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { transform: scale(1); box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); } 50% { transform: scale(1.1); box-shadow: 0 0 25px rgba(0, 0, 0, 0.2); } } @keyframes fadeInOut { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } } @keyframes particleAnimation { 0% { transform: translate(0, 0) scale(0); opacity: 0; } 50% { opacity: 0.8; } 100% { transform: translate(var(--endX), var(--endY)) scale(1); opacity: 0; } } .item-preview { position: absolute; width: 350px; height: 80px; background-color: white; border-radius: 10px; bottom: 6rem; display: flex; align-items: center; padding: 0.5rem; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); transform: translateY(20px); opacity: 0; animation: slideUp 5s infinite cubic-bezier(0.23, 1, 0.32, 1); animation-delay: 1s; } .item-preview img { width: 60px; height: 60px; border-radius: 8px; object-fit: cover; margin-right: 1rem; } .item-info { flex: 1; } .item-title { font-weight: 600; margin-bottom: 0.2rem; color: var(--text); font-size: 0.9rem; } .item-price { color: var(--primary); font-weight: 700; font-size: 1rem; } .item-progress { width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: conic-gradient(var(--primary) var(--progress), #eee var(--progress)); position: relative; margin-left: 0.5rem; } .item-progress::before { content: ''; position: absolute; width: 50px; height: 50px; border-radius: 50%; background-color: white; } .progress-text { position: relative; z-index: 1; font-weight: 700; font-size: 0.9rem; color: var(--accent-dark); } @keyframes slideUp { 0%, 20% { transform: translateY(20px); opacity: 0; } 25%, 75% { transform: translateY(0); opacity: 1; } 80%, 100% { transform: translateY(-20px); opacity: 0; } } /* Product previews */ .product-category { position: absolute; width: 160px; height: 160px; border-radius: 10px; background-color: white; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); opacity: 0; transform: scale(0.8); transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .product-category.visible { opacity: 1; transform: scale(1); } .product-category:nth-child(1) { top: 10%; left: 10%; } .product-category:nth-child(2) { top: 10%; right: 10%; } .product-category:nth-child(3) { bottom: 10%; left: 10%; } .product-category:nth-child(4) { bottom: 10%; right: 10%; } .category-icon { width: 60px; height: 60px; background-color: var(--bg); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 0.5rem; color: var(--accent-dark); font-size: 1.5rem; } .category-name { font-weight: 600; font-size: 0.9rem; } @media (max-width: 600px) { .product-category { width: 120px; height: 120px; } .category-icon { width: 50px; height: 50px; font-size: 1.2rem; } .category-name { font-size: 0.8rem; } .item-preview { width: 300px; } } /* State when spinner is "done" loading */ .done .spinner-ring { animation: none; border-color: transparent; } .done .logo-container { animation: doneAnimation 0.5s forwards cubic-bezier(0.175, 0.885, 0.32, 1.275); } @keyframes doneAnimation { 0% { transform: scale(1); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } /* Cart badge */ .cart-badge { position: absolute; top: -5px; right: -5px; width: 22px; height: 22px; background-color: var(--primary); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: bold; opacity: 0; transform: scale(0); transition: all 0.3s ease; } .cart-badge.show { opacity: 1; transform: scale(1); } </style> </head> <body> <div class="spinner-container" id="spinnerContainer"> <!-- Particles animation --> <div class="particles" id="particles"></div> <!-- Product categories that appear after loading --> <div class="product-category"> <div class="category-icon">👕</div> <div class="category-name">Fashion</div> </div> <div class="product-category"> <div class="category-icon">🖥️</div> <div class="category-name">Tech</div> </div> <div class="product-category"> <div class="category-icon">🏠</div> <div class="category-name">Home</div> </div> <div class="product-category"> <div class="category-icon">🎮</div> <div class="category-name">Gaming</div> </div> <!-- Main spinner --> <div class="spinner"> <div class="spinner-ring spinner-ring-1"></div> <div class="spinner-ring spinner-ring-2"></div> <div class="spinner-ring spinner-ring-3"></div> <div class="spinner-ring spinner-ring-4"></div> </div> <!-- Center logo --> <div class="logo-container"> <div class="logo"> <i class="cart-icon">🛒</i> <div class="cart-badge" id="cartBadge">0</div> </div> </div> <!-- Product preview that appears during loading --> <div class="item-preview"> <img src="data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60' fill='none'%3E%3Crect width='60' height='60' fill='%23F9D423'/%3E%3Cpath d='M30 25L40 35H20L30 25Z' fill='%235CDBBD'/%3E%3Ccircle cx='30' cy='45' r='5' fill='%23FF4E50'/%3E%3C/svg%3E" alt="Product"> <div class="item-info"> <div class="item-title">Limited Edition Bluetooth Earbuds</div> <div class="item-price">$129.99</div> </div> <div class="item-progress" style="--progress: 75%"> <span class="progress-text">75%</span> </div> </div> <!-- Loading text --> <div class="loading-text" id="loadingText">Preparing your personalized shopping experience...</div> </div> <script> // Create particle elements function createParticles() { const particlesContainer = document.getElementById('particles'); const numberOfParticles = 30; for (let i = 0; i < numberOfParticles; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); // Set random positions const angle = Math.random() * Math.PI * 2; const distance = 100 + Math.random() * 150; const endX = Math.cos(angle) * distance; const endY = Math.sin(angle) * distance; particle.style.setProperty('--endX', `${endX}px`); particle.style.setProperty('--endY', `${endY}px`); // Random colors const colors = ['#FF4E50', '#FC913A', '#F9D423', '#5CDBBD', '#1F8A70']; particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; // Random sizes const size = 4 + Math.random() * 8; particle.style.width = `${size}px`; particle.style.height = `${size}px`; // Random positions particle.style.left = '50%'; particle.style.top = '50%'; // Random animation duration const duration = 2 + Math.random() * 3; particle.style.animationDuration = `${duration}s`; // Random animation delay const delay = Math.random() * 2; particle.style.animationDelay = `${delay}s`; particlesContainer.appendChild(particle); } } // Loading messages const loadingMessages = [ "Preparing your personalized shopping experience...", "Finding the best deals just for you...", "Curating your personalized product recommendations...", "Almost there! Finalizing your shopping dashboard...", "Setting up secure payment options...", ]; let messageIndex = 0; let cartCount = 0; const cartBadge = document.getElementById('cartBadge'); const loadingText = document.getElementById('loadingText'); const spinnerContainer = document.getElementById('spinnerContainer'); // Change loading message every 2 seconds function changeLoadingMessage() { messageIndex = (messageIndex + 1) % loadingMessages.length; loadingText.textContent = loadingMessages[messageIndex]; } // Simulate adding items to cart during loading function simulateCartAddition() { cartCount++; cartBadge.textContent = cartCount; cartBadge.classList.add('show'); // Create a "pop" effect cartBadge.animate([ { transform: 'scale(1.3)' }, { transform: 'scale(1)' } ], { duration: 300, easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)' }); } // Complete loading animation function completeLoading() { spinnerContainer.classList.add('done'); loadingText.textContent = "Ready to shop!"; // Show product categories document.querySelectorAll('.product-category').forEach((category, index) => { setTimeout(() => { category.classList.add('visible'); }, index * 200); }); } // Initialize document.addEventListener('DOMContentLoaded', () => { createParticles(); // Change loading message setInterval(changeLoadingMessage, 2000); // Simulate adding items to cart during loading setTimeout(simulateCartAddition, 2000); setTimeout(simulateCartAddition, 4000); setTimeout(simulateCartAddition, 6000); // Complete loading after 7 seconds setTimeout(completeLoading, 7000); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Futuristic AR Interface Spinner</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } :root { --spinner-primary: rgba(64, 224, 208, 0.7); --spinner-secondary: rgba(0, 191, 255, 0.5); --spinner-accent: rgba(147, 112, 219, 0.6); --spinner-highlight: rgba(255, 255, 255, 0.8); --bg-gradient-start: #0f1526; --bg-gradient-end: #1e293b; } body { width: 100%; height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; background: linear-gradient(135deg, var(--bg-gradient-start), var(--bg-gradient-end)); font-family: 'Segoe UI', 'Roboto', sans-serif; overflow: hidden; perspective: 1000px; color: white; } .container { position: relative; width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; } .title { position: absolute; top: 40px; font-size: 1.8rem; font-weight: 300; letter-spacing: 2px; text-transform: uppercase; color: white; text-shadow: 0 0 10px rgba(64, 224, 208, 0.8); z-index: 10; } .subtitle { position: absolute; top: 80px; font-size: 1rem; font-weight: 300; max-width: 500px; text-align: center; color: rgba(255, 255, 255, 0.7); z-index: 10; } .spinner-container { position: relative; width: 400px; height: 400px; display: flex; justify-content: center; align-items: center; transform-style: preserve-3d; cursor: pointer; } .spinner-ring { position: absolute; border-radius: 50%; border: 2px solid; transform-style: preserve-3d; will-change: transform; transition: all 0.3s ease; box-shadow: 0 0 20px rgba(64, 224, 208, 0.3); backdrop-filter: blur(5px); } .ring-1 { width: 350px; height: 350px; border-color: var(--spinner-primary); animation: spin1 15s linear infinite; } .ring-2 { width: 280px; height: 280px; border-color: var(--spinner-secondary); animation: spin2 12s linear infinite; } .ring-3 { width: 210px; height: 210px; border-color: var(--spinner-accent); animation: spin3 10s linear infinite; } .core { position: absolute; width: 140px; height: 140px; border-radius: 50%; background: radial-gradient(circle at 30% 30%, var(--spinner-highlight), var(--spinner-primary)); box-shadow: 0 0 40px rgba(64, 224, 208, 0.6); display: flex; justify-content: center; align-items: center; color: white; font-size: 1.5rem; font-weight: 300; letter-spacing: 1px; animation: pulse 3s ease-in-out infinite; cursor: pointer; } @keyframes spin1 { 0% { transform: rotateZ(0deg) rotateX(20deg) rotateY(0deg); } 100% { transform: rotateZ(360deg) rotateX(20deg) rotateY(0deg); } } @keyframes spin2 { 0% { transform: rotateZ(0deg) rotateX(0deg) rotateY(20deg); } 100% { transform: rotateZ(-360deg) rotateX(0deg) rotateY(20deg); } } @keyframes spin3 { 0% { transform: rotateZ(0deg) rotateX(-20deg) rotateY(-20deg); } 100% { transform: rotateZ(360deg) rotateX(-20deg) rotateY(-20deg); } } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.9; } } .data-point { position: absolute; width: 12px; height: 12px; background-color: var(--spinner-highlight); border-radius: 50%; box-shadow: 0 0 10px var(--spinner-highlight); } .holographic-element { position: absolute; background-color: rgba(255, 255, 255, 0.1); backdrop-filter: blur(2px); border-radius: 4px; padding: 8px; font-size: 0.8rem; transition: all 0.3s ease; transform: scale(0); opacity: 0; box-shadow: 0 0 10px rgba(64, 224, 208, 0.3); border: 1px solid rgba(64, 224, 208, 0.3); pointer-events: none; z-index: 5; } .controls { position: absolute; bottom: 40px; display: flex; gap: 20px; z-index: 10; } .control-btn { background: rgba(255, 255, 255, 0.1); border: 1px solid var(--spinner-primary); color: white; padding: 8px 16px; border-radius: 20px; cursor: pointer; transition: all 0.3s ease; font-size: 0.9rem; backdrop-filter: blur(5px); } .control-btn:hover { background: rgba(64, 224, 208, 0.3); transform: translateY(-2px); box-shadow: 0 5px 15px rgba(64, 224, 208, 0.3); } .velocity-indicator { position: absolute; width: 400px; height: 5px; background: rgba(255, 255, 255, 0.1); border-radius: 3px; bottom: 100px; overflow: hidden; } .velocity-fill { height: 100%; width: 50%; background: linear-gradient(90deg, var(--spinner-primary), var(--spinner-accent)); border-radius: 3px; transition: width 0.5s ease; } /* Particle effects */ .particles { position: absolute; width: 100%; height: 100%; pointer-events: none; } .particle { position: absolute; width: 4px; height: 4px; background-color: var(--spinner-highlight); border-radius: 50%; opacity: 0; pointer-events: none; } /* Responsive adjustments */ @media (max-width: 700px) { .spinner-container { width: 300px; height: 300px; } .ring-1 { width: 260px; height: 260px; } .ring-2 { width: 200px; height: 200px; } .ring-3 { width: 140px; height: 140px; } .core { width: 80px; height: 80px; font-size: 1rem; } .title { font-size: 1.5rem; } .subtitle { font-size: 0.9rem; max-width: 300px; } .velocity-indicator { width: 300px; } .controls { flex-direction: column; gap: 10px; align-items: center; } } </style> </head> <body> <div class="container"> <h1 class="title">NeuroSync AR Interface</h1> <p class="subtitle">Holographic multi-layer interface with neural-driven gesture control. Explore spatial data visualization in augmented environments.</p> <div class="spinner-container" id="spinnerContainer"> <div class="spinner-ring ring-1" id="ring1"></div> <div class="spinner-ring ring-2" id="ring2"></div> <div class="spinner-ring ring-3" id="ring3"></div> <div class="core" id="core">ACTIVATE</div> </div> <div class="velocity-indicator"> <div class="velocity-fill" id="velocityFill"></div> </div> <div class="controls"> <button class="control-btn" id="speedupBtn">Increase Velocity</button> <button class="control-btn" id="slowdownBtn">Decrease Velocity</button> <button class="control-btn" id="toggleDimensionBtn">Toggle Dimension</button> </div> <div class="particles" id="particles"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // DOM Elements const spinnerContainer = document.getElementById('spinnerContainer'); const ring1 = document.getElementById('ring1'); const ring2 = document.getElementById('ring2'); const ring3 = document.getElementById('ring3'); const core = document.getElementById('core'); const velocityFill = document.getElementById('velocityFill'); const speedupBtn = document.getElementById('speedupBtn'); const slowdownBtn = document.getElementById('slowdownBtn'); const toggleDimensionBtn = document.getElementById('toggleDimensionBtn'); const particlesContainer = document.getElementById('particles'); // State let isActive = false; let velocity = 1; let dimension3D = true; let velocityPercent = 50; let spinnerData = [ { name: "Neural Interface", value: "98.2% synced" }, { name: "Spatial Mapping", value: "Active" }, { name: "Gesture Recognition", value: "Enabled" }, { name: "User Presence", value: "Detected" }, { name: "Environment Scan", value: "Complete" }, { name: "Haptic Feedback", value: "Calibrated" } ]; // Create data points on rings function createDataPoints() { // Clear existing data points document.querySelectorAll('.data-point').forEach(point => point.remove()); document.querySelectorAll('.holographic-element').forEach(el => el.remove()); // Create new data points const rings = [ring1, ring2, ring3]; rings.forEach((ring, ringIndex) => { const numPoints = 6 - ringIndex; const ringSize = parseInt(getComputedStyle(ring).width); for (let i = 0; i < numPoints; i++) { // Create data point const point = document.createElement('div'); point.classList.add('data-point'); // Position around the ring const angle = (i / numPoints) * Math.PI * 2; const radius = ringSize / 2; const x = Math.cos(angle) * radius; const y = Math.sin(angle) * radius; point.style.left = `calc(50% + ${x}px)`; point.style.top = `calc(50% + ${y}px)`; ring.appendChild(point); // Create holographic element if (ringIndex === 0) { const holoElement = document.createElement('div'); holoElement.classList.add('holographic-element'); const dataIndex = i % spinnerData.length; holoElement.innerHTML = ` <strong>${spinnerData[dataIndex].name}</strong><br> ${spinnerData[dataIndex].value} `; const offset = 40; holoElement.style.left = `calc(50% + ${x * 1.2}px - ${offset}px)`; holoElement.style.top = `calc(50% + ${y * 1.2}px - ${offset}px)`; spinnerContainer.appendChild(holoElement); // Associate data point with holographic element point.addEventListener('mouseenter', () => { holoElement.style.transform = 'scale(1)'; holoElement.style.opacity = '1'; }); point.addEventListener('mouseleave', () => { holoElement.style.transform = 'scale(0)'; holoElement.style.opacity = '0'; }); } } }); } // Create particles function createParticles(num) { for (let i = 0; i < num; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); particlesContainer.appendChild(particle); // Random position within the spinner container const x = Math.random() * 400 - 200; const y = Math.random() * 400 - 200; // Random animation const duration = 1 + Math.random() * 2; const delay = Math.random() * 2; const size = 2 + Math.random() * 3; particle.style.left = `calc(50% + ${x}px)`; particle.style.top = `calc(50% + ${y}px)`; particle.style.width = `${size}px`; particle.style.height = `${size}px`; // Animate the particle particle.animate([ { transform: `translate(0, 0)`, opacity: 0 }, { transform: `translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px)`, opacity: 0.8 }, { transform: `translate(${Math.random() * 200 - 100}px, ${Math.random() * 200 - 100}px)`, opacity: 0 } ], { duration: duration * 1000, delay: delay * 1000, iterations: Infinity }); } } // Update spinner animation based on velocity function updateSpinnerSpeed() { const speeds = [ { ring1: 15 / velocity, ring2: 12 / velocity, ring3: 10 / velocity } ]; document.documentElement.style.setProperty('--ring1-speed', `${speeds[0].ring1}s`); document.documentElement.style.setProperty('--ring2-speed', `${speeds[0].ring2}s`); document.documentElement.style.setProperty('--ring3-speed', `${speeds[0].ring3}s`); ring1.style.animationDuration = `${speeds[0].ring1}s`; ring2.style.animationDuration = `${speeds[0].ring2}s`; ring3.style.animationDuration = `${speeds[0].ring3}s`; velocityFill.style.width = `${velocityPercent}%`; } // Toggle 3D perspective function toggleDimension() { dimension3D = !dimension3D; if (dimension3D) { ring1.style.animation = "spin1 15s linear infinite"; ring2.style.animation = "spin2 12s linear infinite"; ring3.style.animation = "spin3 10s linear infinite"; toggleDimensionBtn.textContent = "Toggle 2D"; } else { ring1.style.animation = "spin1 15s linear infinite"; ring2.style.animation = "spin1 12s linear infinite reverse"; ring3.style.animation = "spin1 10s linear infinite"; toggleDimensionBtn.textContent = "Toggle 3D"; } updateSpinnerSpeed(); } // Activate the spinner function activateSpinner() { isActive = !isActive; if (isActive) { core.textContent = "ACTIVE"; core.style.boxShadow = "0 0 50px rgba(64, 224, 208, 0.8)"; spinnerContainer.style.transform = "translateZ(50px)"; // Show holographic elements document.querySelectorAll('.holographic-element').forEach(el => { setTimeout(() => { el.style.transform = 'scale(1)'; el.style.opacity = '1'; setTimeout(() => { el.style.transform = 'scale(0)'; el.style.opacity = '0'; }, 2000); }, Math.random() * 1000); }); // Create more particles createParticles(20); } else { core.textContent = "ACTIVATE"; core.style.boxShadow = "0 0 40px rgba(64, 224, 208, 0.6)"; spinnerContainer.style.transform = "translateZ(0)"; // Clear particles particlesContainer.innerHTML = ''; createParticles(5); } } // Increase velocity function increaseVelocity() { if (velocityPercent < 100) { velocityPercent += 10; velocity = 0.5 + (velocityPercent / 50); updateSpinnerSpeed(); } } // Decrease velocity function decreaseVelocity() { if (velocityPercent > 10) { velocityPercent -= 10; velocity = 0.5 + (velocityPercent / 50); updateSpinnerSpeed(); } } // Interactive spinner rotation let isDragging = false; let lastMouseX = 0; let lastMouseY = 0; let rotationX = 20; let rotationY = 0; spinnerContainer.addEventListener('mousedown', (e) => { isDragging = true; lastMouseX = e.clientX; lastMouseY = e.clientY; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const deltaX = e.clientX - lastMouseX; const deltaY = e.clientY - lastMouseY; rotationX -= deltaY * 0.5; rotationY += deltaX * 0.5; rotationX = Math.max(-45, Math.min(45, rotationX)); spinnerContainer.style.transform = `rotateX(${rotationX}deg) rotateY(${rotationY}deg)`; lastMouseX = e.clientX; lastMouseY = e.clientY; }); document.addEventListener('mouseup', () => { isDragging = false; }); // Touch events for mobile spinnerContainer.addEventListener('touchstart', (e) => { isDragging = true; lastMouseX = e.touches[0].clientX; lastMouseY = e.touches[0].clientY; }); document.addEventListener('touchmove', (e) => { if (!isDragging) return; const deltaX = e.touches[0].clientX - lastMouseX; const deltaY = e.touches[0].clientY - lastMouseY; rotationX -= deltaY * 0.5; rotationY += deltaX * 0.5; rotationX = Math.max(-45, Math.min(45, rotationX)); spinnerContainer.style.transform = `rotateX(${rotationX}deg) rotateY(${rotationY}deg)`; lastMouseX = e.touches[0].clientX; lastMouseY = e.touches[0].clientY; }); document.addEventListener('touchend', () => { isDragging = false; }); // Event listeners core.addEventListener('click', activateSpinner); speedupBtn.addEventListener('click', increaseVelocity); slowdownBtn.addEventListener('click', decreaseVelocity); toggleDimensionBtn.addEventListener('click', toggleDimension); // Initialize createDataPoints(); createParticles(5); updateSpinnerSpeed(); // Resize handler window.addEventListener('resize', createDataPoints); // Mouse movement effect for spinner document.addEventListener('mousemove', (e) => { if (isDragging) return; const rect = spinnerContainer.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const offsetX = (e.clientX - centerX) / 30; const offsetY = (e.clientY - centerY) / 30; if (!isDragging) { spinnerContainer.style.transform = `rotateX(${-offsetY}deg) rotateY(${offsetX}deg)`; } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Data Dashboard Ring Spinner</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 700px; width: 100%; max-width: 700px; background-color: #f8f9fa; margin: 0 auto; overflow: hidden; padding: 20px; } .dashboard-container { position: relative; width: 100%; max-width: 600px; height: 600px; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #ffffff; border-radius: 20px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); padding: 30px; overflow: hidden; } .spinner-container { position: relative; width: 280px; height: 280px; margin-bottom: 30px; } .control-panel { display: flex; gap: 15px; margin-bottom: 40px; } .control-btn { background: #f0f4f8; border: none; padding: 12px 20px; border-radius: 10px; color: #465866; font-weight: 600; font-size: 14px; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } .control-btn:hover { background: #e0e7ee; transform: translateY(-2px); } .control-btn.active { background: #3a86ff; color: white; } .data-insight { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; z-index: 10; width: 150px; } .data-value { font-size: 32px; font-weight: 700; color: #1d3557; margin-bottom: 5px; } .data-label { font-size: 14px; color: #6c757d; font-weight: 500; } .data-metrics { width: 100%; display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 30px; } .metric-card { background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 12px; padding: 15px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.02); transition: all 0.3s ease; } .metric-card:hover { transform: translateY(-5px); box-shadow: 0 8px 15px rgba(0, 0, 0, 0.05); } .metric-value { font-size: 20px; font-weight: 700; color: #1d3557; margin-bottom: 5px; } .metric-label { font-size: 13px; color: #6c757d; font-weight: 500; } .ring { position: absolute; border-radius: 50%; border: 4px solid transparent; opacity: 0.85; transition: all 0.5s ease; } .header { width: 100%; text-align: center; margin-bottom: 20px; } .title { font-size: 24px; font-weight: 700; color: #1d3557; margin-bottom: 8px; } .subtitle { font-size: 14px; color: #6c757d; } .tooltip { position: absolute; background: rgba(255, 255, 255, 0.95); border-radius: 8px; padding: 10px 15px; font-size: 12px; color: #333; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); pointer-events: none; opacity: 0; transition: opacity 0.3s ease; z-index: 100; white-space: nowrap; } @media (max-width: 600px) { .dashboard-container { padding: 20px; height: auto; } .spinner-container { width: 220px; height: 220px; } .data-value { font-size: 28px; } .data-label { font-size: 12px; } .data-metrics { grid-template-columns: repeat(2, 1fr); } .control-panel { flex-wrap: wrap; justify-content: center; } } @media (max-width: 400px) { .spinner-container { width: 180px; height: 180px; } .data-metrics { grid-template-columns: 1fr; } } </style> </head> <body> <div class="dashboard-container"> <div class="header"> <h1 class="title">Data Processing Pipeline</h1> <p class="subtitle">Real-time visualization of system performance metrics</p> </div> <div class="control-panel"> <button class="control-btn active" data-speed="1">Real-time</button> <button class="control-btn" data-speed="0.5">0.5x Speed</button> <button class="control-btn" data-speed="2">2x Speed</button> <button class="control-btn" id="pause-btn">Pause</button> </div> <div class="spinner-container"> <div class="data-insight"> <div class="data-value" id="central-value">86%</div> <div class="data-label">Processing Efficiency</div> </div> <!-- Rings will be added here by JS --> </div> <div class="data-metrics"> <div class="metric-card" data-tooltip="Average time to process data packets"> <div class="metric-value">142ms</div> <div class="metric-label">Response Time</div> </div> <div class="metric-card" data-tooltip="Active data streams being processed"> <div class="metric-value">24</div> <div class="metric-label">Active Streams</div> </div> <div class="metric-card" data-tooltip="Data accuracy after processing"> <div class="metric-value">99.8%</div> <div class="metric-label">Accuracy Rate</div> </div> </div> <div class="tooltip" id="tooltip"></div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Configuration const ringCount = 6; const maxRadius = 130; const minRadius = 50; const spinnerContainer = document.querySelector('.spinner-container'); const tooltip = document.getElementById('tooltip'); let animationSpeed = 1; let isPaused = false; let rings = []; let startAngles = Array(ringCount).fill(0); let endAngles = Array(ringCount).fill(0); let targetEndAngles = Array(ringCount).fill(0); let spinSpeed = Array(ringCount).fill(0); // Create gradient colors for rings const colorPalettes = [ { start: '#3a86ff', end: '#0066cc' }, // Blue { start: '#8338ec', end: '#5e23aa' }, // Purple { start: '#ff006e', end: '#c8004f' }, // Pink { start: '#fb5607', end: '#c84400' }, // Orange { start: '#ffbe0b', end: '#cc9700' }, // Yellow { start: '#06d6a0', end: '#039973' } // Teal ]; // Create rings for (let i = 0; i < ringCount; i++) { const radius = maxRadius - (i * ((maxRadius - minRadius) / ringCount)); const size = radius * 2; const thickness = 8 + (ringCount - i) * 0.5; const ring = document.createElement('div'); ring.className = 'ring'; ring.style.width = `${size}px`; ring.style.height = `${size}px`; ring.style.top = `${(280 - size) / 2}px`; ring.style.left = `${(280 - size) / 2}px`; // Apply gradient const { start, end } = colorPalettes[i % colorPalettes.length]; ring.style.borderWidth = `${thickness}px`; spinnerContainer.appendChild(ring); rings.push(ring); // Initialize ring animation properties startAngles[i] = (i * 30) % 360; // Stagger start angles endAngles[i] = startAngles[i]; spinSpeed[i] = 0.8 + Math.random() * 0.4; // Slightly different speeds // Set initial arc updateRingArc(i); } function updateRingArc(index) { const ring = rings[index]; const startAngle = startAngles[index]; const endAngle = endAngles[index]; const arcLength = ((endAngle - startAngle + 360) % 360); // Color palette for this ring const { start, end } = colorPalettes[index % colorPalettes.length]; // Set the gradient and dash pattern const circumference = Math.PI * 2 * (parseFloat(ring.style.width) / 2); const dashLength = (arcLength / 360) * circumference; const dashGap = circumference - dashLength; ring.style.borderColor = 'transparent'; ring.style.borderTopColor = start; ring.style.borderRightColor = end; ring.style.borderLeftColor = end; ring.style.transform = `rotate(${startAngle}deg)`; ring.style.borderRadius = '50%'; } // Animation loop function animate() { if (!isPaused) { for (let i = 0; i < ringCount; i++) { // Generate new target end angles randomly if (Math.random() < 0.01 * animationSpeed) { targetEndAngles[i] = (startAngles[i] + 30 + Math.random() * 300) % 360; } // Smoothly animate toward target end angles const diff = ((targetEndAngles[i] - endAngles[i] + 360) % 360); if (diff > 1) { endAngles[i] = (endAngles[i] + Math.min(diff * 0.05, 2) * animationSpeed * spinSpeed[i]) % 360; updateRingArc(i); } // Rotate start angle (giving spinning effect) startAngles[i] = (startAngles[i] + 0.25 * animationSpeed * spinSpeed[i]) % 360; updateRingArc(i); } // Randomly update central value if (Math.random() < 0.01 * animationSpeed) { const centralValue = document.getElementById('central-value'); const newValue = 75 + Math.floor(Math.random() * 20); centralValue.textContent = `${newValue}%`; // Update color based on value let color = '#1d3557'; // Default blue if (newValue >= 90) color = '#06d6a0'; // Green for high else if (newValue < 80) color = '#ff006e'; // Red for low centralValue.style.color = color; } } requestAnimationFrame(animate); } // Start animation animate(); // Set up controls document.querySelectorAll('.control-btn[data-speed]').forEach(button => { button.addEventListener('click', function() { document.querySelectorAll('.control-btn').forEach(btn => btn.classList.remove('active')); this.classList.add('active'); animationSpeed = parseFloat(this.getAttribute('data-speed')); isPaused = false; document.getElementById('pause-btn').textContent = 'Pause'; }); }); document.getElementById('pause-btn').addEventListener('click', function() { isPaused = !isPaused; this.textContent = isPaused ? 'Resume' : 'Pause'; }); // Tooltip for metric cards document.querySelectorAll('.metric-card').forEach(card => { card.addEventListener('mouseenter', function(e) { const tooltipText = this.getAttribute('data-tooltip'); tooltip.textContent = tooltipText; tooltip.style.opacity = '1'; const rect = this.getBoundingClientRect(); const containerRect = document.querySelector('.dashboard-container').getBoundingClientRect(); tooltip.style.left = `${rect.left - containerRect.left + rect.width/2 - tooltip.offsetWidth/2}px`; tooltip.style.top = `${rect.top - containerRect.top - tooltip.offsetHeight - 10}px`; }); card.addEventListener('mouseleave', function() { tooltip.style.opacity = '0'; }); }); // Simulate dynamic data updates for the metrics setInterval(() => { if (isPaused) return; document.querySelectorAll('.metric-card').forEach((card, index) => { const valueEl = card.querySelector('.metric-value'); const currentText = valueEl.textContent; // Different update logic based on the metric type if (index === 0) { // Response time let value = parseInt(currentText); value += Math.floor(Math.random() * 11) - 5; // -5 to +5 if (value < 120) value = 120; if (value > 180) value = 180; valueEl.textContent = `${value}ms`; } else if (index === 1) { // Active streams let value = parseInt(currentText); if (Math.random() < 0.3) { value += Math.random() < 0.5 ? 1 : -1; } if (value < 18) value = 18; if (value > 32) value = 32; valueEl.textContent = value; } else if (index === 2) { // Accuracy let value = parseFloat(currentText); value = Math.round((value + (Math.random() * 0.2 - 0.1)) * 10) / 10; if (value < 99.5) value = 99.5; if (value > 100) value = 100; valueEl.textContent = `${value.toFixed(1)}%`; } }); }, 3000); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Creative Portfolio Loader</title> <style> :root { --pink: #ffb6c1; --blue: #87ceeb; --lavender: #e6e6fa; --mint: #98fb98; --peach: #ffdab9; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Helvetica Neue', Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #f8f9fa; overflow: hidden; } .loader-container { position: relative; width: 100%; max-width: 700px; height: 100vh; max-height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 2rem; } .loader { position: relative; width: 200px; height: 200px; margin-bottom: 3rem; } .loader-element { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.1)); } .brush-stroke { width: 40px; height: 120px; border-radius: 50%; transform-origin: center center; opacity: 0.9; } .brush-1 { background-color: var(--pink); animation: rotate-brush 3s ease-in-out infinite; } .brush-2 { background-color: var(--blue); animation: rotate-brush 3s ease-in-out infinite 0.5s; width: 50px; height: 100px; } .brush-3 { background-color: var(--lavender); animation: rotate-brush 3s ease-in-out infinite 1s; width: 35px; height: 90px; } .brush-4 { background-color: var(--mint); animation: rotate-brush 3s ease-in-out infinite 1.5s; width: 45px; height: 110px; } .brush-5 { background-color: var(--peach); animation: rotate-brush 3s ease-in-out infinite 2s; width: 30px; height: 80px; } .abstract-shape { width: 80px; height: 80px; border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; background-color: #ffffff; opacity: 0.7; transform-origin: center center; animation: morph 8s linear infinite; } .loading-text { font-size: 1.25rem; color: #333; letter-spacing: 1px; margin-bottom: 1rem; font-weight: 500; opacity: 0; animation: fade-in 1.5s ease-in-out forwards; } .portfolio-title { font-size: 2.5rem; font-weight: 700; margin-bottom: 1rem; text-align: center; color: #333; opacity: 0; animation: fade-in 1.5s ease-in-out 0.5s forwards; } .portfolio-subtitle { font-size: 1.2rem; text-align: center; color: #666; max-width: 450px; line-height: 1.6; margin-bottom: 2rem; opacity: 0; animation: fade-in 1.5s ease-in-out 1s forwards; } .progress-container { width: 250px; height: 4px; background-color: rgba(0, 0, 0, 0.1); border-radius: 4px; overflow: hidden; opacity: 0; animation: fade-in 1.5s ease-in-out 1.5s forwards; } .progress-bar { height: 100%; width: 0; background-color: #333; border-radius: 4px; transition: width 0.3s ease; } .percentage { font-size: 0.9rem; color: #666; margin-top: 0.5rem; font-weight: 500; opacity: 0; animation: fade-in 1.5s ease-in-out 1.5s forwards; } @keyframes rotate-brush { 0% { transform: translate(-50%, -50%) rotate(0deg) translateX(40px) rotate(0deg); } 50% { transform: translate(-50%, -50%) rotate(180deg) translateX(40px) rotate(-180deg) scale(0.8); } 100% { transform: translate(-50%, -50%) rotate(360deg) translateX(40px) rotate(-360deg); } } @keyframes morph { 0% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } 25% { border-radius: 70% 30% 30% 70% / 70% 70% 30% 30%; } 50% { border-radius: 30% 30% 70% 70% / 60% 40% 60% 40%; } 75% { border-radius: 50% 50% 20% 80% / 25% 80% 20% 75%; } 100% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } } @keyframes fade-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* Interaction styles */ .loader-container:hover .brush-stroke { animation-duration: 2s; } .loader-container:hover .abstract-shape { animation-duration: 5s; } /* Responsive styles */ @media (max-width: 600px) { .loader { width: 150px; height: 150px; margin-bottom: 2rem; } .portfolio-title { font-size: 2rem; } .portfolio-subtitle { font-size: 1rem; padding: 0 1rem; } .brush-stroke { transform-origin: center center; transform: scale(0.8) translate(-60%, -60%); } } </style> </head> <body> <div class="loader-container"> <div class="loader"> <div class="loader-element abstract-shape"></div> <div class="loader-element brush-stroke brush-1"></div> <div class="loader-element brush-stroke brush-2"></div> <div class="loader-element brush-stroke brush-3"></div> <div class="loader-element brush-stroke brush-4"></div> <div class="loader-element brush-stroke brush-5"></div> </div> <div class="loading-text">CURATING CREATIVITY</div> <h1 class="portfolio-title">Studio Artisanal</h1> <p class="portfolio-subtitle">Crafting digital experiences with the thoughtfulness of an artisan and the vision of an artist. Prepare to enter a showcase of creative craftsmanship.</p> <div class="progress-container"> <div class="progress-bar" id="progressBar"></div> </div> <div class="percentage" id="percentage">0%</div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const progressBar = document.getElementById('progressBar'); const percentage = document.getElementById('percentage'); let progress = 0; // Simulate loading progress const interval = setInterval(() => { progress += Math.floor(Math.random() * 5) + 1; if (progress >= 100) { progress = 100; clearInterval(interval); // You would typically redirect or show content here // For this example, we'll just keep showing the loader setTimeout(() => { // Create a finish animation effect document.querySelectorAll('.brush-stroke').forEach(brush => { brush.style.animationPlayState = 'paused'; brush.style.transition = 'all 0.8s ease-out'; brush.style.opacity = '0'; brush.style.transform = 'translate(-50%, -50%) scale(1.5)'; }); document.querySelector('.abstract-shape').style.animation = 'morph 2s ease-out forwards'; }, 1000); } progressBar.style.width = `${progress}%`; percentage.textContent = `${progress}%`; }, 200); // Interactive elements const loaderContainer = document.querySelector('.loader-container'); loaderContainer.addEventListener('mousemove', (e) => { const { left, top, width, height } = loaderContainer.getBoundingClientRect(); const x = (e.clientX - left) / width - 0.5; const y = (e.clientY - top) / height - 0.5; const brushes = document.querySelectorAll('.brush-stroke'); brushes.forEach((brush, index) => { const factor = (index + 1) * 4; brush.style.transform = `translate(calc(-50% + ${x * factor}px), calc(-50% + ${y * factor}px)) rotate(${x * y * 360}deg) translateX(${40 + x * 10}px) rotate(${-x * y * 360}deg)`; }); const shape = document.querySelector('.abstract-shape'); shape.style.borderRadius = `${50 + x * 20}% ${50 - x * 20}% ${50 + y * 20}% ${50 - y * 20}% / ${50 - y * 20}% ${50 + y * 20}% ${50 - x * 20}% ${50 + x * 20}%`; }); loaderContainer.addEventListener('mouseleave', () => { const brushes = document.querySelectorAll('.brush-stroke'); brushes.forEach(brush => { brush.style.transform = ''; }); const shape = document.querySelector('.abstract-shape'); shape.style.borderRadius = ''; }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Healthcare App Spinner</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: #f5f9fb; width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 2rem; position: relative; overflow: hidden; } .background-pulse { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle, rgba(169, 227, 236, 0.2) 0%, rgba(255, 255, 255, 0) 70%); animation: pulse 4s infinite alternate; z-index: -1; } @keyframes pulse { 0% { transform: scale(1); opacity: 0.5; } 100% { transform: scale(1.1); opacity: 0.8; } } .spinner-container { position: relative; width: 200px; height: 200px; margin-bottom: 2rem; } .spinner { width: 100%; height: 100%; border-radius: 50%; position: relative; display: flex; justify-content: center; align-items: center; } .spinner-ring { position: absolute; border-radius: 50%; border: 4px solid transparent; } .spinner-ring-1 { width: 100%; height: 100%; border-top-color: #4ECDC4; animation: spin 2s linear infinite; } .spinner-ring-2 { width: 85%; height: 85%; border-right-color: #1A535C; animation: spin 1.5s linear infinite reverse; } .spinner-ring-3 { width: 70%; height: 70%; border-bottom-color: #5DA9E9; animation: spin 3s linear infinite; } .spinner-center { position: absolute; width: 50%; height: 50%; background: linear-gradient(135deg, #4ECDC4, #5DA9E9); border-radius: 50%; display: flex; justify-content: center; align-items: center; box-shadow: 0 0 20px rgba(93, 169, 233, 0.3); animation: pulse-center 2s infinite alternate; } .spinner-icon { color: white; font-size: 24px; } @keyframes pulse-center { 0% { transform: scale(1); box-shadow: 0 0 15px rgba(93, 169, 233, 0.3); } 100% { transform: scale(1.05); box-shadow: 0 0 25px rgba(93, 169, 233, 0.5); } } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .message-container { text-align: center; max-width: 400px; padding: 1.5rem; background-color: rgba(255, 255, 255, 0.9); border-radius: 12px; box-shadow: 0 4px 20px rgba(26, 83, 92, 0.1); transform: translateY(0); transition: all 0.5s ease; } .message-container:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(26, 83, 92, 0.15); } .status { font-size: 1.2rem; font-weight: 600; color: #1A535C; margin-bottom: 0.5rem; } .description { color: #5D6D7E; line-height: 1.6; margin-bottom: 1rem; } .time-estimation { background: linear-gradient(135deg, rgba(78, 205, 196, 0.1), rgba(93, 169, 233, 0.1)); padding: 0.8rem; border-radius: 8px; margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; } .time-label { font-size: 0.9rem; color: #5D6D7E; } .time-value { font-weight: 600; color: #1A535C; } .wellness-tip { font-size: 0.9rem; color: #4ECDC4; font-style: italic; margin-top: 1rem; padding: 0.5rem; border-top: 1px solid rgba(78, 205, 196, 0.2); } .breathing-guide { display: flex; align-items: center; justify-content: center; margin-top: 1.5rem; } .breathing-circle { width: 15px; height: 15px; background-color: #5DA9E9; border-radius: 50%; margin: 0 5px; animation: breathe 4s infinite alternate ease-in-out; opacity: 0.7; } .breathing-circle:nth-child(2) { animation-delay: 0.5s; } .breathing-circle:nth-child(3) { animation-delay: 1s; } .breathing-circle:nth-child(4) { animation-delay: 1.5s; } .breathing-text { font-size: 0.85rem; color: #5D6D7E; margin-left: 10px; } @keyframes breathe { 0% { transform: scale(0.8); opacity: 0.5; } 100% { transform: scale(1.2); opacity: 1; } } .progress-bar { width: 100%; height: 6px; background-color: rgba(93, 169, 233, 0.1); border-radius: 3px; margin-top: 1rem; overflow: hidden; position: relative; } .progress-fill { position: absolute; height: 100%; width: 0%; background: linear-gradient(to right, #4ECDC4, #5DA9E9); border-radius: 3px; transition: width 0.5s ease; } @media (max-width: 500px) { .spinner-container { width: 150px; height: 150px; } .message-container { padding: 1rem; max-width: 300px; } .status { font-size: 1rem; } .description { font-size: 0.9rem; } } .wave { position: absolute; bottom: 0; left: 0; width: 100%; height: 100px; background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%234ecdc4" fill-opacity="0.1" d="M0,96L48,112C96,128,192,160,288,165.3C384,171,480,149,576,138.7C672,128,768,128,864,144C960,160,1056,192,1152,197.3C1248,203,1344,181,1392,170.7L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>'); background-size: cover; background-repeat: no-repeat; opacity: 0.7; z-index: -1; } </style> </head> <body> <div class="container"> <div class="background-pulse"></div> <div class="wave"></div> <div class="spinner-container"> <div class="spinner"> <div class="spinner-ring spinner-ring-1"></div> <div class="spinner-ring spinner-ring-2"></div> <div class="spinner-ring spinner-ring-3"></div> <div class="spinner-center"> <div class="spinner-icon">+</div> </div> </div> </div> <div class="message-container"> <div class="status" id="status">Retrieving your medical records</div> <p class="description">Our system is securely accessing your information while maintaining HIPAA compliance standards.</p> <div class="time-estimation"> <span class="time-label">Estimated wait time:</span> <span class="time-value" id="timeEstimate">~2 minutes</span> </div> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> <div class="wellness-tip" id="wellnessTip"> Lowering your shoulders and taking deep breaths can help reduce blood pressure while you wait. </div> <div class="breathing-guide"> <div class="breathing-circle"></div> <div class="breathing-circle"></div> <div class="breathing-circle"></div> <div class="breathing-circle"></div> <span class="breathing-text">Try breathing with the rhythm</span> </div> </div> </div> <script> const statusMessages = [ "Retrieving your medical records", "Processing lab results", "Updating medication history", "Preparing your appointment details", "Setting up secure connection to your doctor" ]; const wellnessTips = [ "Lowering your shoulders and taking deep breaths can help reduce blood pressure while you wait.", "Staying hydrated can improve both mood and cognitive function throughout your day.", "A brief 5-minute walk every hour can help improve circulation and reduce stiffness.", "Focusing on a simple gratitude practice can boost your mental wellness during wait times.", "Regular sleep patterns support better immune function and overall health outcomes." ]; const timeEstimates = [ "~2 minutes", "~90 seconds", "~60 seconds", "~30 seconds", "Almost complete" ]; let currentStep = 0; const totalSteps = statusMessages.length; const statusElement = document.getElementById('status'); const timeEstimateElement = document.getElementById('timeEstimate'); const wellnessTipElement = document.getElementById('wellnessTip'); const progressFill = document.getElementById('progressFill'); function updateLoader() { if (currentStep < totalSteps) { // Update the status message statusElement.innerHTML = statusMessages[currentStep]; // Animate the status with a subtle fade statusElement.style.opacity = 0; setTimeout(() => { statusElement.style.opacity = 1; }, 300); // Update the time estimate timeEstimateElement.textContent = timeEstimates[currentStep]; // Update the wellness tip with a gentle transition wellnessTipElement.style.opacity = 0; setTimeout(() => { wellnessTipElement.textContent = wellnessTips[currentStep]; wellnessTipElement.style.opacity = 1; }, 300); // Update progress bar const progressPercentage = ((currentStep + 1) / totalSteps) * 100; progressFill.style.width = `${progressPercentage}%`; currentStep++; // Schedule the next update setTimeout(updateLoader, 3000); // Change message every 3 seconds } } // Restart the animation when it completes function restartAnimation() { if (currentStep >= totalSteps) { currentStep = 0; progressFill.style.width = '0%'; setTimeout(updateLoader, 1000); } else { setTimeout(restartAnimation, 1000); } } // Start the loader animation updateLoader(); setTimeout(restartAnimation, 15000); // Check to restart after the sequence should have completed // Add hover effect to spinner const spinnerContainer = document.querySelector('.spinner-container'); spinnerContainer.addEventListener('mouseenter', () => { const spinnerRings = document.querySelectorAll('.spinner-ring'); spinnerRings.forEach(ring => { ring.style.borderWidth = '5px'; }); }); spinnerContainer.addEventListener('mouseleave', () => { const spinnerRings = document.querySelectorAll('.spinner-ring'); spinnerRings.forEach(ring => { ring.style.borderWidth = '4px'; }); }); </script> </body> </html>
<html> <head> <style> :root { --primary: #FF6B6B; --secondary: #4ECDC4; --accent1: #FFE66D; --accent2: #7A77FF; --dark: #292F36; --light: #F7FFF7; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Arial Rounded MT Bold', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: var(--light); overflow: hidden; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; position: relative; padding: 20px; } .title { text-align: center; margin-bottom: 30px; color: var(--dark); z-index: 10; } h1 { font-size: 2.5rem; margin-bottom: 10px; } .subtitle { font-size: 1.2rem; opacity: 0.8; max-width: 80%; margin: 0 auto; } .spinner-container { position: relative; width: 320px; height: 320px; margin: 30px 0; } .spinner { width: 100%; height: 100%; position: relative; transition: transform 3s cubic-bezier(0.2, 0.8, 0.2, 1); } .spinner-center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80px; height: 80px; background-color: var(--dark); border-radius: 50%; z-index: 10; display: flex; justify-content: center; align-items: center; cursor: pointer; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } .spinner-center:hover { transform: translate(-50%, -50%) scale(1.1); box-shadow: 0 12px 36px rgba(0, 0, 0, 0.15); } .spinner-center:active { transform: translate(-50%, -50%) scale(0.95); } .spinner-center-text { color: white; font-size: 0.9rem; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; } .segment { position: absolute; width: 100%; height: 100%; clip-path: polygon(50% 50%, 50% 0%, 100% 0%, 100% 100%, 50% 100%); transform-origin: center; display: flex; justify-content: center; align-items: center; transition: all 0.5s ease; } .segment:hover { filter: brightness(1.1); } .segment-content { position: absolute; top: 25%; right: 30%; transform: rotate(45deg); z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 5px; } .segment-icon { width: 50px; height: 50px; display: flex; justify-content: center; align-items: center; border-radius: 12px; background-color: rgba(255, 255, 255, 0.85); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); font-size: 1.8rem; transition: transform 0.3s ease; } .segment-text { font-size: 0.8rem; color: rgba(255, 255, 255, 0.9); font-weight: bold; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } .controls { display: flex; justify-content: center; gap: 20px; margin-top: 20px; z-index: 10; } .control-btn { padding: 12px 24px; border: none; border-radius: 50px; background-color: var(--secondary); color: white; font-weight: bold; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); } .control-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); } .control-btn:active { transform: translateY(0); } .topic-display { margin-top: 30px; padding: 20px; background-color: white; border-radius: 15px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); text-align: center; max-width: 80%; transform: scale(0); transition: all 0.5s cubic-bezier(0.2, 0.8, 0.2, 1); position: relative; z-index: 5; } .topic-display.active { transform: scale(1); } .topic-title { font-size: 1.5rem; color: var(--dark); margin-bottom: 10px; } .topic-description { font-size: 0.9rem; color: #666; } .fun-facts { margin-top: 15px; font-size: 0.9rem; font-style: italic; color: var(--primary); } .particles { position: absolute; width: 100%; height: 100%; top: 0; left: 0; pointer-events: none; z-index: 1; } .particle { position: absolute; border-radius: 50%; opacity: 0; transform: scale(0); } @media (max-width: 600px) { .spinner-container { width: 280px; height: 280px; } .spinner-center { width: 70px; height: 70px; } .segment-icon { width: 40px; height: 40px; font-size: 1.4rem; } .segment-text { font-size: 0.7rem; } .title h1 { font-size: 1.8rem; } .subtitle { font-size: 1rem; } } /* Animations */ @keyframes float { 0%, 100% { transform: translateY(0) rotate(0); } 50% { transform: translateY(-10px) rotate(5deg); } } @keyframes pulsate { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.9; } } @keyframes particleBurst { 0% { transform: scale(0); opacity: 0; } 50% { transform: scale(1); opacity: 0.8; } 100% { transform: scale(1.5); opacity: 0; } } .float-animation { animation: float 6s ease-in-out infinite; } .pulsate-animation { animation: pulsate 3s ease-in-out infinite; } /* Shape morphing */ .shape-morph { position: absolute; width: 140px; height: 140px; background-color: var(--accent1); border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; z-index: -1; opacity: 0.2; transition: all 2s ease; } .shape-morph:nth-child(1) { top: 10%; left: 10%; background-color: var(--primary); animation: morphAnimation1 15s ease-in-out infinite alternate; } .shape-morph:nth-child(2) { bottom: 10%; right: 10%; background-color: var(--secondary); animation: morphAnimation2 18s ease-in-out infinite alternate; } .shape-morph:nth-child(3) { top: 40%; right: 20%; background-color: var(--accent2); animation: morphAnimation3 20s ease-in-out infinite alternate; } @keyframes morphAnimation1 { 0% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } 25% { border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%; } 50% { border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%; } 75% { border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%; } 100% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } } @keyframes morphAnimation2 { 0% { border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%; } 25% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } 50% { border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%; } 75% { border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%; } 100% { border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%; } } @keyframes morphAnimation3 { 0% { border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%; } 25% { border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%; } 50% { border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; } 75% { border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%; } 100% { border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%; } } </style> </head> <body> <div class="container"> <div class="shape-morph"></div> <div class="shape-morph"></div> <div class="shape-morph"></div> <div class="title"> <h1>Knowledge Spinner</h1> <p class="subtitle">Spin to discover fascinating subjects! Each color leads to a new learning adventure.</p> </div> <div class="spinner-container"> <div class="spinner" id="spinner"> <!-- Segments will be added by JavaScript --> </div> <div class="spinner-center" id="spinButton"> <span class="spinner-center-text">SPIN</span> </div> </div> <div class="controls"> <button class="control-btn" id="resetButton">Reset</button> <button class="control-btn" id="hintButton">Hint</button> </div> <div class="topic-display" id="topicDisplay"> <h3 class="topic-title" id="topicTitle">Mathematics</h3> <p class="topic-description" id="topicDescription">Discover patterns and solve problems with numbers.</p> <p class="fun-facts" id="topicFacts">Did you know? Zero wasn't always a number!</p> </div> <div class="particles" id="particles"></div> </div> <script> // Define our educational topics const topics = [ { name: "Mathematics", icon: "➗", color: "#FF6B6B", description: "Explore the magical world of numbers, shapes, and patterns!", facts: "Did you know? The Fibonacci sequence appears throughout nature in flowers and shells!" }, { name: "Science", icon: "🔬", color: "#4ECDC4", description: "Discover how our world works through observation and experiments.", facts: "Fun fact: A bolt of lightning is five times hotter than the surface of the sun!" }, { name: "Reading", icon: "📚", color: "#FFE66D", description: "Adventure through stories and boost your vocabulary skills.", facts: "Amazing! Your brain processes reading by creating a mental movie." }, { name: "History", icon: "⏳", color: "#7A77FF", description: "Travel back in time to learn how our world became what it is today.", facts: "Wow! The Great Pyramids were built when woolly mammoths still roamed Earth." }, { name: "Art", icon: "🎨", color: "#FF9F1C", description: "Express yourself through colors, shapes, and creative thinking.", facts: "Did you know? The Mona Lisa has no visible eyebrows or eyelashes!" }, { name: "Music", icon: "🎵", color: "#A5FFD6", description: "Learn rhythm, melody, and the joy of creating beautiful sounds.", facts: "Amazing! Music can help you remember things better and improve your mood." }, { name: "Geography", icon: "🌍", color: "#C23B22", description: "Explore countries, landforms, and how humans interact with Earth.", facts: "Wow! There's a waterfall underwater in the Denmark Strait between Iceland and Greenland." }, { name: "Coding", icon: "💻", color: "#03CEA4", description: "Learn to speak the language of computers and create your own games!", facts: "Cool! The first computer programmer was a woman named Ada Lovelace." } ]; // DOM Elements const spinner = document.getElementById('spinner'); const spinButton = document.getElementById('spinButton'); const resetButton = document.getElementById('resetButton'); const hintButton = document.getElementById('hintButton'); const topicDisplay = document.getElementById('topicDisplay'); const topicTitle = document.getElementById('topicTitle'); const topicDescription = document.getElementById('topicDescription'); const topicFacts = document.getElementById('topicFacts'); const particlesContainer = document.getElementById('particles'); let isSpinning = false; let currentRotation = 0; let selectedTopic = null; // Create spinner segments function createSpinner() { // Clear existing segments spinner.innerHTML = ''; // Create segments based on topics topics.forEach((topic, index) => { const angle = (360 / topics.length) * index; const segment = document.createElement('div'); segment.className = 'segment'; segment.style.backgroundColor = topic.color; segment.style.transform = `rotate(${angle}deg)`; const segmentContent = document.createElement('div'); segmentContent.className = 'segment-content'; const iconElement = document.createElement('div'); iconElement.className = 'segment-icon pulsate-animation'; iconElement.textContent = topic.icon; const textElement = document.createElement('div'); textElement.className = 'segment-text'; textElement.textContent = topic.name; segmentContent.appendChild(iconElement); segmentContent.appendChild(textElement); segment.appendChild(segmentContent); spinner.appendChild(segment); }); } // Spin the wheel function spinWheel() { if (isSpinning) return; isSpinning = true; topicDisplay.classList.remove('active'); // Random number of full rotations (3-5) plus a random segment const spinAngle = 3600 + Math.floor(Math.random() * 720); const totalRotation = currentRotation + spinAngle; // Animate the spinner spinner.style.transform = `rotate(${totalRotation}deg)`; currentRotation = totalRotation; // Create particle burst effect createParticles(); // Show result after spinning is complete setTimeout(() => { isSpinning = false; determineResult(); }, 3000); } // Determine which topic was selected function determineResult() { // Calculate which segment is at the top position const segmentAngle = 360 / topics.length; let normalizedRotation = currentRotation % 360; // We need to invert the rotation value because the spinner rotates clockwise normalizedRotation = 360 - normalizedRotation; const segmentIndex = Math.floor(normalizedRotation / segmentAngle) % topics.length; selectedTopic = topics[segmentIndex]; // Display the selected topic topicTitle.textContent = selectedTopic.name; topicDescription.textContent = selectedTopic.description; topicFacts.textContent = selectedTopic.facts; topicDisplay.style.backgroundColor = selectedTopic.color + '33'; // Add 33 for transparency topicDisplay.classList.add('active'); } // Reset the spinner function resetSpinner() { isSpinning = false; currentRotation = 0; spinner.style.transform = 'rotate(0deg)'; topicDisplay.classList.remove('active'); selectedTopic = null; } // Show a hint function showHint() { if (selectedTopic) { alert(`Here's an activity idea for ${selectedTopic.name}: ${getActivityForTopic(selectedTopic.name)}`); } else { alert("Spin the wheel first to select a topic!"); } } // Get a specific activity suggestion based on topic function getActivityForTopic(topicName) { const activities = { "Mathematics": "Create a nature scavenger hunt to find Fibonacci sequences in flowers and pinecones!", "Science": "Make a mini volcano using baking soda and vinegar to see chemical reactions in action.", "Reading": "Create your own comic book by drawing pictures and adding dialogue bubbles.", "History": "Build a time capsule with items that represent today's world.", "Art": "Try blind contour drawing - draw an object without looking at your paper!", "Music": "Create a homemade instrument using household items and play a tune.", "Geography": "Use playdough to create a 3D map of your neighborhood or country.", "Coding": "Try to create a simple animation using block-based coding at code.org" }; return activities[topicName] || "Explore this topic through creative play!"; } // Create particle burst effect function createParticles() { particlesContainer.innerHTML = ''; const colors = topics.map(topic => topic.color); for (let i = 0; i < 30; i++) { const particle = document.createElement('div'); particle.className = 'particle'; const size = Math.random() * 10 + 5; const color = colors[Math.floor(Math.random() * colors.length)]; particle.style.width = `${size}px`; particle.style.height = `${size}px`; particle.style.backgroundColor = color; // Position around the center of the spinner const centerX = spinner.offsetWidth / 2 + spinner.offsetLeft; const centerY = spinner.offsetHeight / 2 + spinner.offsetTop; const angle = Math.random() * Math.PI * 2; const distance = 50 + Math.random() * 100; const x = centerX + Math.cos(angle) * distance; const y = centerY + Math.sin(angle) * distance; particle.style.left = `${x}px`; particle.style.top = `${y}px`; // Set animation properties particle.style.animation = `particleBurst ${0.5 + Math.random()}s forwards`; particlesContainer.appendChild(particle); } } // Initialize the spinner createSpinner(); // Event listeners spinButton.addEventListener('click', spinWheel); resetButton.addEventListener('click', resetSpinner); hintButton.addEventListener('click', () => { if (selectedTopic) { // Create a custom tooltip instead of an alert const tooltip = document.createElement('div'); tooltip.className = 'topic-display active'; tooltip.style.position = 'absolute'; tooltip.style.zIndex = '100'; tooltip.style.backgroundColor = selectedTopic.color + '33'; tooltip.style.maxWidth = '90%'; tooltip.style.top = '50%'; tooltip.style.left = '50%'; tooltip.style.transform = 'translate(-50%, -50%)'; tooltip.style.padding = '20px'; tooltip.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.15)'; tooltip.style.borderRadius = '15px'; tooltip.innerHTML = ` <h3 class="topic-title">Activity for ${selectedTopic.name}</h3> <p class="topic-description">${getActivityForTopic(selectedTopic.name)}</p> <button class="control-btn" style="margin-top: 15px;">Close</button> `; document.querySelector('.container').appendChild(tooltip); tooltip.querySelector('button').addEventListener('click', () => { tooltip.remove(); }); // Auto remove after 5 seconds setTimeout(() => { if (document.body.contains(tooltip)) { tooltip.remove(); } }, 5000); } else { topicDisplay.innerHTML = `<h3 class="topic-title">Hint</h3><p class="topic-description">Spin the wheel first to select a topic!</p>`; topicDisplay.style.backgroundColor = '#f1f1f1'; topicDisplay.classList.add('active'); setTimeout(() => { topicDisplay.classList.remove('active'); }, 2000); } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Travel Booking Globe Spinner</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f2ed; overflow: hidden; perspective: 1000px; } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; position: relative; } .globe-container { position: relative; width: 300px; height: 300px; margin: 30px 0; transform-style: preserve-3d; } .globe { position: absolute; width: 100%; height: 100%; border-radius: 50%; background: linear-gradient(145deg, #8a6f4d, #d4b995); box-shadow: 0 0 60px rgba(210, 180, 140, 0.4); transform-style: preserve-3d; animation: float 6s ease-in-out infinite; } .globe::before { content: ''; position: absolute; top: -10px; left: -10px; right: -10px; bottom: -10px; border-radius: 50%; border: 2px solid rgba(210, 180, 140, 0.3); box-shadow: inset 0 0 20px rgba(210, 180, 140, 0.4); animation: pulse 4s linear infinite; } .latitude, .longitude { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 0 10px rgba(255, 255, 255, 0.1); } .latitude { transform: rotateX(90deg); } .longitude { transform: rotateY(90deg); } .latitude-lines { position: absolute; width: 100%; height: 100%; border-radius: 50%; transform-style: preserve-3d; } .latitude-line { position: absolute; width: 100%; height: 1px; background-color: rgba(255, 255, 255, 0.2); transform-origin: center; } .longitude-lines { position: absolute; width: 100%; height: 100%; border-radius: 50%; transform-style: preserve-3d; } .longitude-line { position: absolute; top: 0; width: 1px; height: 100%; background-color: rgba(255, 255, 255, 0.2); transform-origin: center; } .spinner { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 3px solid transparent; border-top-color: #d4b995; border-bottom-color: #8a6f4d; animation: spin 3s linear infinite; } .spinner::before { content: ''; position: absolute; top: 5px; left: 5px; right: 5px; bottom: 5px; border-radius: 50%; border: 3px solid transparent; border-left-color: #d4b995; border-right-color: #8a6f4d; animation: spin 2s linear infinite reverse; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.05); opacity: 0.9; } } @keyframes float { 0%, 100% { transform: translateY(0) rotateY(0); } 50% { transform: translateY(-15px) rotateY(180deg); } } .destination-search { width: 100%; max-width: 500px; background-color: white; padding: 25px; border-radius: 15px; box-shadow: 0 10px 30px rgba(138, 111, 77, 0.2); margin-top: 30px; transform: translateY(20px); opacity: 0; animation: appear 1s forwards 0.5s; z-index: 10; } @keyframes appear { to { transform: translateY(0); opacity: 1; } } h1 { color: #5a4827; font-size: 2rem; margin-bottom: 10px; text-align: center; } p { color: #8a6f4d; margin-bottom: 20px; text-align: center; line-height: 1.5; } .search-form { display: flex; flex-direction: column; gap: 15px; } .search-row { display: flex; gap: 15px; } .search-field { flex: 1; position: relative; } label { display: block; color: #8a6f4d; margin-bottom: 5px; font-weight: 500; } input, select { width: 100%; padding: 12px 15px; border: 2px solid #d4b995; border-radius: 8px; background-color: #fff; color: #5a4827; font-size: 1rem; transition: all 0.3s ease; } input:focus, select:focus { outline: none; border-color: #8a6f4d; box-shadow: 0 0 0 3px rgba(138, 111, 77, 0.25); } button { background: linear-gradient(145deg, #8a6f4d, #d4b995); color: white; border: none; padding: 15px; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; margin-top: 10px; box-shadow: 0 4px 15px rgba(138, 111, 77, 0.3); } button:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(138, 111, 77, 0.4); } button:active { transform: translateY(0); } .destination-dots { position: absolute; width: 10px; height: 10px; background-color: rgba(210, 180, 140, 0.8); border-radius: 50%; box-shadow: 0 0 10px rgba(210, 180, 140, 0.6); z-index: 5; transform-style: preserve-3d; } .destination-label { position: absolute; color: white; font-size: 0.7rem; text-shadow: 0 0 3px rgba(0, 0, 0, 0.7); transform: translateZ(5px); pointer-events: none; white-space: nowrap; } .globe-pulse { position: absolute; width: 100%; height: 100%; border-radius: 50%; background: radial-gradient(circle, rgba(210, 180, 140, 0.1) 0%, rgba(210, 180, 140, 0) 70%); transform: scale(1.2); animation: globePulse 3s infinite ease-in-out; } @keyframes globePulse { 0%, 100% { opacity: 0.3; transform: scale(1.2); } 50% { opacity: 0.7; transform: scale(1.3); } } @media (max-width: 700px) { .container { height: auto; padding: 15px; } .globe-container { width: 200px; height: 200px; margin: 20px 0; } .destination-search { padding: 20px; max-width: 90%; } h1 { font-size: 1.5rem; } .search-row { flex-direction: column; gap: 10px; } input, select, button { padding: 10px; } } @media (max-width: 400px) { .globe-container { width: 150px; height: 150px; } h1 { font-size: 1.3rem; } } /* Custom input fields styling */ .search-field.date-field { position: relative; } .date-field input { padding-right: 40px; } .date-field::after { content: '📅'; position: absolute; right: 15px; top: 40px; pointer-events: none; } .location-field::after { content: '📍'; position: absolute; right: 15px; top: 40px; pointer-events: none; } .traveler-field::after { content: '👥'; position: absolute; right: 15px; top: 40px; pointer-events: none; } /* Popular destinations feature */ .popular-destinations { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 15px; justify-content: center; } .destination-tag { background-color: rgba(210, 180, 140, 0.2); color: #5a4827; padding: 5px 10px; border-radius: 20px; font-size: 0.8rem; cursor: pointer; transition: all 0.3s ease; border: 1px solid transparent; } .destination-tag:hover { background-color: rgba(210, 180, 140, 0.4); border-color: #8a6f4d; transform: translateY(-2px); } </style> </head> <body> <div class="container"> <div class="globe-container" id="globe-container"> <div class="globe"> <div class="latitude"></div> <div class="longitude"></div> <div class="latitude-lines" id="latitude-lines"></div> <div class="longitude-lines" id="longitude-lines"></div> <div class="spinner"></div> </div> <div class="globe-pulse"></div> </div> <div class="destination-search"> <h1>Discover Your Next Adventure</h1> <p>Spin the globe and find unexplored destinations waiting for your footsteps.</p> <div class="search-form"> <div class="search-row"> <div class="search-field location-field"> <label for="destination">Destination</label> <input type="text" id="destination" placeholder="Where to?"> </div> <div class="search-field date-field"> <label for="departure">Departure</label> <input type="text" id="departure" placeholder="Select date"> </div> </div> <div class="search-row"> <div class="search-field date-field"> <label for="return">Return</label> <input type="text" id="return" placeholder="Select date"> </div> <div class="search-field traveler-field"> <label for="travelers">Travelers</label> <select id="travelers"> <option value="1">1 Adult</option> <option value="2">2 Adults</option> <option value="3">3 Adults</option> <option value="4">2 Adults, 2 Children</option> </select> </div> </div> <button id="search-btn">Find My Journey</button> </div> <div class="popular-destinations"> <span class="destination-tag">Bali</span> <span class="destination-tag">Santorini</span> <span class="destination-tag">Tokyo</span> <span class="destination-tag">Machu Picchu</span> <span class="destination-tag">Maldives</span> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const globeContainer = document.getElementById('globe-container'); const latitudeLines = document.getElementById('latitude-lines'); const longitudeLines = document.getElementById('longitude-lines'); const searchBtn = document.getElementById('search-btn'); const destinationInput = document.getElementById('destination'); const departureInput = document.getElementById('departure'); const returnInput = document.getElementById('return'); const destinationTags = document.querySelectorAll('.destination-tag'); // Create latitude lines for (let i = 0; i < 9; i++) { const line = document.createElement('div'); line.className = 'latitude-line'; line.style.top = `${i * 33.3}px`; latitudeLines.appendChild(line); } // Create longitude lines for (let i = 0; i < 12; i++) { const line = document.createElement('div'); line.className = 'longitude-line'; line.style.left = `${150}px`; line.style.transform = `rotateY(${i * 15}deg)`; longitudeLines.appendChild(line); } // Sample destinations with coordinates (simplified for the demo) const destinations = [ { name: 'New York', x: 74, y: 40 }, { name: 'Paris', x: 48, y: 2 }, { name: 'Tokyo', x: 35, y: 139 }, { name: 'Sydney', x: -33, y: 151 }, { name: 'Rio', x: -22, y: -43 }, { name: 'Cairo', x: 30, y: 31 } ]; // Place destinations dots on the globe destinations.forEach(dest => { const dot = document.createElement('div'); dot.className = 'destination-dots'; // Convert lat/long to 3D position on sphere (simplified) const phi = (90 - dest.x) * (Math.PI / 180); const theta = (dest.y + 180) * (Math.PI / 180); const x = -150 * Math.sin(phi) * Math.cos(theta); const y = 150 * Math.cos(phi); const z = 150 * Math.sin(phi) * Math.sin(theta); dot.style.transform = `translate3d(${x + 150}px, ${y + 150}px, ${z}px)`; const label = document.createElement('div'); label.className = 'destination-label'; label.textContent = dest.name; label.style.transform = `translate3d(${x + 155}px, ${y + 150}px, ${z + 5}px)`; globeContainer.appendChild(dot); globeContainer.appendChild(label); }); // Interactive globe rotation let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; let rotation = { x: 0, y: 0 }; const globe = document.querySelector('.globe'); globeContainer.addEventListener('mousedown', (e) => { isDragging = true; previousMousePosition = { x: e.clientX, y: e.clientY }; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const deltaMove = { x: e.clientX - previousMousePosition.x, y: e.clientY - previousMousePosition.y }; rotation.y += deltaMove.x * 0.5; rotation.x -= deltaMove.y * 0.5; globe.style.transform = `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`; previousMousePosition = { x: e.clientX, y: e.clientY }; }); document.addEventListener('mouseup', () => { isDragging = false; }); // Simulate date picker functionality (simplified for the demo) departureInput.addEventListener('focus', () => { const today = new Date(); const formattedDate = `${today.getMonth() + 1}/${today.getDate()}/${today.getFullYear()}`; departureInput.value = formattedDate; }); returnInput.addEventListener('focus', () => { const today = new Date(); today.setDate(today.getDate() + 7); // Default to a week from now const formattedDate = `${today.getMonth() + 1}/${today.getDate()}/${today.getFullYear()}`; returnInput.value = formattedDate; }); // Popular destinations tag click destinationTags.forEach(tag => { tag.addEventListener('click', () => { destinationInput.value = tag.textContent; // Visual feedback tag.style.backgroundColor = 'rgba(138, 111, 77, 0.4)'; setTimeout(() => { tag.style.backgroundColor = 'rgba(210, 180, 140, 0.2)'; }, 500); // Find the destination on the globe (simplified) const dest = destinations.find(d => d.name.includes(tag.textContent)); if (dest) { // Highlight effect on the globe (not exact matching but suggestive) const dots = document.querySelectorAll('.destination-dots'); dots.forEach((dot, index) => { if (index % destinations.length === destinations.indexOf(dest)) { dot.style.background = '#d4b995'; dot.style.boxShadow = '0 0 15px #d4b995'; dot.style.transform += ' scale(1.5)'; setTimeout(() => { dot.style.background = 'rgba(210, 180, 140, 0.8)'; dot.style.boxShadow = '0 0 10px rgba(210, 180, 140, 0.6)'; dot.style.transform = dot.style.transform.replace(' scale(1.5)', ''); }, 2000); } }); } }); }); // Search button animation and functionality searchBtn.addEventListener('click', () => { if (!destinationInput.value) { destinationInput.style.borderColor = '#ff6b6b'; setTimeout(() => { destinationInput.style.borderColor = '#d4b995'; }, 800); return; } searchBtn.innerHTML = 'Searching...'; searchBtn.disabled = true; // Simulate search processing setTimeout(() => { searchBtn.innerHTML = 'Journey Found!'; searchBtn.style.backgroundColor = '#5a8a4f'; // Show success pulse on the globe const pulse = document.createElement('div'); pulse.style.position = 'absolute'; pulse.style.width = '100%'; pulse.style.height = '100%'; pulse.style.borderRadius = '50%'; pulse.style.background = 'rgba(90, 138, 79, 0.2)'; pulse.style.animation = 'pulse 1s ease-out'; globe.appendChild(pulse); setTimeout(() => { searchBtn.innerHTML = 'Find My Journey'; searchBtn.style.backgroundColor = ''; searchBtn.disabled = false; globe.removeChild(pulse); }, 2000); }, 1500); }); // Auto rotation for the globe (gentle movement when not interacted with) let autoRotate = true; let autoRotateInterval; function startAutoRotation() { if (autoRotateInterval) clearInterval(autoRotateInterval); autoRotateInterval = setInterval(() => { if (!isDragging && autoRotate) { rotation.y += 0.2; globe.style.transform = `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`; } }, 50); } startAutoRotation(); // Pause auto-rotation when hovering over the globe globeContainer.addEventListener('mouseenter', () => { autoRotate = false; }); globeContainer.addEventListener('mouseleave', () => { autoRotate = true; }); // Touch events for mobile devices globeContainer.addEventListener('touchstart', (e) => { isDragging = true; previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; autoRotate = false; }); document.addEventListener('touchmove', (e) => { if (!isDragging) return; const deltaMove = { x: e.touches[0].clientX - previousMousePosition.x, y: e.touches[0].clientY - previousMousePosition.y }; rotation.y += deltaMove.x * 0.5; rotation.x -= deltaMove.y * 0.5; globe.style.transform = `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`; previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; }); document.addEventListener('touchend', () => { isDragging = false; setTimeout(() => { autoRotate = true; }, 3000); }); }); </script> </body> </html>