In the world of web development, masks are essential tools for enhancing user experience and ensuring data accuracy. They provide a seamless way to format input fields, guiding users to enter information correctly.
This article explores 10 practical mask examples that can be easily implemented in your projects. From date pickers to phone number formats, these examples will help you create intuitive and user-friendly interfaces.
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
Subframe's drag-and-drop interface and intuitive, responsive canvas make it effortless to design pixel-perfect masks. Loved by designers and developers alike, it ensures your UI is always stunning and functional.
Ready to elevate your design game? Start for free 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
Unlock the power of Subframe and design pixel-perfect UIs with ease. Whether you're creating masks or entire interfaces, Subframe's drag-and-drop editor ensures efficiency and precision.
Start creating stunning, production-ready designs immediately. Start for free today and elevate your design game!
<html> <head> <style> :root { --primary: #2c3e50; --secondary: #3498db; --accent: #1abc9c; --light: #ecf0f1; --dark: #121212; --success: #2ecc71; --warning: #f39c12; --error: #e74c3c; --info: #34495e; --transition-timing: cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } body { width: 100%; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 20px; overflow: hidden; color: var(--dark); } .container { width: 100%; max-width: 700px; height: 700px; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; overflow: hidden; border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); background-color: white; } .content { padding: 2rem; text-align: center; z-index: 1; } h1 { font-size: 2rem; margin-bottom: 1rem; color: var(--primary); font-weight: 700; } p { margin-bottom: 2rem; line-height: 1.6; color: var(--info); max-width: 500px; } button { cursor: pointer; padding: 0.75rem 1.5rem; border: none; border-radius: 8px; font-weight: 600; font-size: 1rem; transition: all 0.3s var(--transition-timing); position: relative; overflow: hidden; z-index: 1; } button:focus { outline: none; } .btn-primary { background-color: var(--secondary); color: white; box-shadow: 0 4px 14px rgba(52, 152, 219, 0.3); } .btn-primary:hover { background-color: #2980b9; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4); } .btn-secondary { background-color: var(--light); color: var(--primary); margin-right: 1rem; } .btn-secondary:hover { background-color: #bdc3c7; transform: translateY(-2px); } .btn-with-icon { display: flex; align-items: center; justify-content: center; gap: 0.5rem; } .btn-with-icon i { font-size: 1.2rem; } .button-ripple { position: absolute; background: rgba(255, 255, 255, 0.3); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } @keyframes ripple { to { transform: scale(4); opacity: 0; } } .notification-types { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 2rem; } .notification-type { padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; transition: all 0.2s var(--transition-timing); font-weight: 500; font-size: 0.9rem; border: 2px solid transparent; } .type-info { background-color: rgba(52, 73, 94, 0.1); color: var(--info); border-color: var(--info); } .type-success { background-color: rgba(46, 204, 113, 0.1); color: var(--success); border-color: var(--success); } .type-warning { background-color: rgba(243, 156, 18, 0.1); color: var(--warning); border-color: var(--warning); } .type-error { background-color: rgba(231, 76, 60, 0.1); color: var(--error); border-color: var(--error); } .notification-type:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); } .notification-type.active { background-color: rgba(52, 73, 94, 0.2); transform: scale(1.05); } /* Modal mask styles */ .modal-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(17, 24, 39, 0.7); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; opacity: 0; visibility: hidden; transition: all 0.3s var(--transition-timing); z-index: 1000; } .modal-mask.active { opacity: 1; visibility: visible; } .modal-container { background-color: white; padding: 2rem; border-radius: 16px; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); width: 90%; max-width: 450px; transform: translateY(20px) scale(0.95); opacity: 0; transition: all 0.4s var(--transition-timing); position: relative; overflow: hidden; } .modal-mask.active .modal-container { transform: translateY(0) scale(1); opacity: 1; } .modal-header { display: flex; align-items: center; margin-bottom: 1.5rem; position: relative; } .modal-icon { width: 40px; height: 40px; border-radius: 50%; background-color: rgba(52, 152, 219, 0.1); display: flex; align-items: center; justify-content: center; margin-right: 1rem; flex-shrink: 0; } .modal-icon i { color: var(--secondary); font-size: 1.25rem; } .modal-title { flex-grow: 1; font-size: 1.25rem; font-weight: 600; color: var(--primary); margin: 0; } .modal-close { position: absolute; top: 0; right: 0; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; background-color: rgba(236, 240, 241, 0.5); transition: all 0.2s var(--transition-timing); border: none; } .modal-close:hover { background-color: rgba(236, 240, 241, 1); transform: rotate(90deg); } .modal-close i { color: var(--dark); font-size: 1rem; } .modal-body { margin-bottom: 1.5rem; } .modal-body p { margin-bottom: 1rem; line-height: 1.5; color: #4a5568; } .modal-body .highlight { background-color: rgba(26, 188, 156, 0.1); color: var(--accent); padding: 0.1rem 0.3rem; border-radius: 4px; font-weight: 500; } .modal-footer { display: flex; justify-content: flex-end; margin-top: 1rem; } .modal-footer button { margin-left: 0.75rem; } /* Modal types */ .modal-info .modal-icon { background-color: rgba(52, 73, 94, 0.1); } .modal-info .modal-icon i { color: var(--info); } .modal-success .modal-icon { background-color: rgba(46, 204, 113, 0.1); } .modal-success .modal-icon i { color: var(--success); } .modal-warning .modal-icon { background-color: rgba(243, 156, 18, 0.1); } .modal-warning .modal-icon i { color: var(--warning); } .modal-error .modal-icon { background-color: rgba(231, 76, 60, 0.1); } .modal-error .modal-icon i { color: var(--error); } /* Modal accent line */ .modal-accent { position: absolute; top: 0; left: 0; height: 4px; width: 100%; background-color: var(--secondary); } .modal-info .modal-accent { background-color: var(--info); } .modal-success .modal-accent { background-color: var(--success); } .modal-warning .modal-accent { background-color: var(--warning); } .modal-error .modal-accent { background-color: var(--error); } /* Progress bar for auto-dismiss */ .modal-progress { position: absolute; bottom: 0; left: 0; height: 3px; width: 100%; background-color: rgba(236, 240, 241, 0.5); overflow: hidden; } .modal-progress-bar { height: 100%; width: 100%; background-color: var(--secondary); transform: translateX(-100%); transition: transform linear; } .demo-actions { position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%); display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; } /* Patterns */ .pattern { position: absolute; z-index: 0; opacity: 0.05; } .pattern-dots { top: 10%; right: 5%; width: 150px; height: 150px; background-image: radial-gradient(var(--dark) 1px, transparent 1px); background-size: 10px 10px; } .pattern-circles { bottom: 10%; left: 5%; width: 100px; height: 100px; border: 2px solid var(--dark); border-radius: 50%; } .pattern-circles::before { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 60%; height: 60%; border: 2px solid var(--dark); border-radius: 50%; } /* Responsive */ @media (max-width: 600px) { h1 { font-size: 1.75rem; } .notification-types { gap: 6px; } .notification-type { padding: 0.4rem 0.8rem; font-size: 0.8rem; } .modal-container { padding: 1.5rem; max-width: 340px; } .modal-header { margin-bottom: 1rem; } .modal-icon { width: 34px; height: 34px; } .modal-title { font-size: 1.1rem; } } /* Animation for the notification badge */ @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(52, 152, 219, 0); } 100% { box-shadow: 0 0 0 0 rgba(52, 152, 219, 0); } } .pulse { animation: pulse 2s infinite; } /* Shake animation for error modals */ @keyframes shake { 0%, 100% {transform: translateX(0);} 10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 20%, 40%, 60%, 80% {transform: translateX(5px);} } .shake { animation: shake 0.6s; } /* Floating animation for success modals */ @keyframes float { 0% {transform: translateY(0px);} 50% {transform: translateY(-5px);} 100% {transform: translateY(0px);} } .float { animation: float 3s ease-in-out infinite; } </style> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css"> </head> <body> <div class="container"> <div class="pattern pattern-dots"></div> <div class="pattern pattern-circles"></div> <div class="content"> <h1>Modal Dialog Mask System</h1> <p>A sleek notification system that dims the background with a translucent overlay, providing focus on important content while minimizing distractions.</p> <div class="notification-types"> <div class="notification-type type-info" data-type="info"> <i class="bi bi-info-circle"></i> Informational </div> <div class="notification-type type-success" data-type="success"> <i class="bi bi-check-circle"></i> Success </div> <div class="notification-type type-warning" data-type="warning"> <i class="bi bi-exclamation-triangle"></i> Warning </div> <div class="notification-type type-error" data-type="error"> <i class="bi bi-x-octagon"></i> Error </div> </div> <button class="btn-primary btn-with-icon" id="triggerModal"> <i class="bi bi-bell"></i> Show Notification </button> </div> <div class="demo-actions"> <button class="btn-secondary" id="toggleAutoDismiss"> <i class="bi bi-clock"></i> Auto-dismiss: OFF </button> <button class="btn-secondary" id="toggleAnimation"> <i class="bi bi-stars"></i> Extra animations: OFF </button> </div> </div> <!-- Modal mask --> <div class="modal-mask" id="modalMask"> <div class="modal-container" id="modalContainer"> <div class="modal-accent"></div> <div class="modal-header"> <div class="modal-icon"> <i class="bi bi-info-circle"></i> </div> <h3 class="modal-title">Information Update</h3> <button class="modal-close" id="closeModal"> <i class="bi bi-x"></i> </button> </div> <div class="modal-body"> <p>This translucent modal mask system gently <span class="highlight">dims the background</span> to bring focus to important information while maintaining context.</p> <p>The smooth fade transition creates a non-disruptive experience for your users.</p> </div> <div class="modal-footer"> <button class="btn-secondary" id="cancelModal">Dismiss</button> <button class="btn-primary btn-with-icon" id="confirmModal"> <i class="bi bi-check-lg"></i> Confirm </button> </div> <div class="modal-progress"> <div class="modal-progress-bar" id="progressBar"></div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Elements const modalMask = document.getElementById('modalMask'); const modalContainer = document.getElementById('modalContainer'); const triggerModalBtn = document.getElementById('triggerModal'); const closeModalBtn = document.getElementById('closeModal'); const cancelModalBtn = document.getElementById('cancelModal'); const confirmModalBtn = document.getElementById('confirmModal'); const toggleAutoDismissBtn = document.getElementById('toggleAutoDismiss'); const toggleAnimationBtn = document.getElementById('toggleAnimation'); const progressBar = document.getElementById('progressBar'); const notificationTypes = document.querySelectorAll('.notification-type'); // Settings let currentType = 'info'; let autoDismiss = false; let extraAnimations = false; let dismissTimeout; let progressBarAnimation; // Content for different notification types const modalContent = { info: { title: "Information Update", icon: "bi-info-circle", body: `<p>This translucent modal mask system gently <span class="highlight">dims the background</span> to bring focus to important information while maintaining context.</p> <p>The smooth fade transition creates a non-disruptive experience for your users.</p>`, confirmText: "Confirm" }, success: { title: "Operation Successful", icon: "bi-check-circle", body: `<p>Your action has been <span class="highlight">successfully completed</span>. The system has processed your request and updated all relevant data.</p> <p>You can continue working with the updated information.</p>`, confirmText: "Continue" }, warning: { title: "Attention Required", icon: "bi-exclamation-triangle", body: `<p>This action may have <span class="highlight">unexpected consequences</span>. Please review the details before proceeding.</p> <p>You can either continue with caution or cancel to review your options.</p>`, confirmText: "Proceed Anyway" }, error: { title: "Error Encountered", icon: "bi-x-octagon", body: `<p>We've encountered an <span class="highlight">error processing</span> your request. This might be due to network issues or invalid input.</p> <p>Please try again or contact support if the problem persists.</p>`, confirmText: "Try Again" } }; // Button ripple effect document.querySelectorAll('button').forEach(button => { button.addEventListener('mousedown', function(e) { const x = e.clientX - e.target.getBoundingClientRect().left; const y = e.clientY - e.target.getBoundingClientRect().top; const ripple = document.createElement('span'); ripple.classList.add('button-ripple'); ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; this.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); }); }); // Update modal content based on notification type function updateModalContent(type) { const content = modalContent[type]; document.querySelector('.modal-title').textContent = content.title; document.querySelector('.modal-icon i').className = `bi ${content.icon}`; document.querySelector('.modal-body').innerHTML = content.body; document.querySelector('#confirmModal').innerHTML = `<i class="bi bi-check-lg"></i> ${content.confirmText}`; // Remove previous type classes modalContainer.classList.remove('modal-info', 'modal-success', 'modal-warning', 'modal-error'); // Add new type class modalContainer.classList.add(`modal-${type}`); } // Show modal with fade in animation function showModal() { updateModalContent(currentType); modalMask.classList.add('active'); // Add extra animations based on type if (extraAnimations) { if (currentType === 'error') { setTimeout(() => { modalContainer.classList.add('shake'); setTimeout(() => modalContainer.classList.remove('shake'), 600); }, 400); } else if (currentType === 'success') { modalContainer.classList.add('float'); } else { modalContainer.classList.remove('float'); } } // Setup auto-dismiss if enabled if (autoDismiss) { // Reset progress bar progressBar.style.transition = 'none'; progressBar.style.transform = 'translateX(-100%)'; // Force reflow void progressBar.offsetWidth; // Start progress animation progressBar.style.transition = 'transform 5s linear'; progressBar.style.transform = 'translateX(0)'; // Set timeout to close dismissTimeout = setTimeout(hideModal, 5000); } } // Hide modal with fade out animation function hideModal() { modalMask.classList.remove('active'); modalContainer.classList.remove('float'); // Clear any pending auto-dismiss clearTimeout(dismissTimeout); // Reset progress bar progressBar.style.transition = 'none'; progressBar.style.transform = 'translateX(-100%)'; } // Event listeners triggerModalBtn.addEventListener('click', showModal); closeModalBtn.addEventListener('click', hideModal); cancelModalBtn.addEventListener('click', hideModal); confirmModalBtn.addEventListener('click', hideModal); // Also close when clicking outside of modal content modalMask.addEventListener('click', function(e) { if (e.target === modalMask) { hideModal(); } }); // Toggle auto-dismiss toggleAutoDismissBtn.addEventListener('click', function() { autoDismiss = !autoDismiss; this.innerHTML = autoDismiss ? '<i class="bi bi-clock-fill"></i> Auto-dismiss: ON' : '<i class="bi bi-clock"></i> Auto-dismiss: OFF'; if (autoDismiss) { this.style.backgroundColor = 'var(--secondary)'; this.style.color = 'white'; } else { this.style.backgroundColor = 'var(--light)'; this.style.color = 'var(--primary)'; } }); // Toggle extra animations toggleAnimationBtn.addEventListener('click', function() { extraAnimations = !extraAnimations; this.innerHTML = extraAnimations ? '<i class="bi bi-stars"></i> Extra animations: ON' : '<i class="bi bi-stars"></i> Extra animations: OFF'; if (extraAnimations) { this.style.backgroundColor = 'var(--secondary)'; this.style.color = 'white'; } else { this.style.backgroundColor = 'var(--light)'; this.style.color = 'var(--primary)'; } }); // Notification type selection notificationTypes.forEach(type => { type.addEventListener('click', function() { // Update active state visual notificationTypes.forEach(t => t.classList.remove('active')); this.classList.add('active'); // Update current type currentType = this.dataset.type; // Update trigger button to match the type triggerModalBtn.className = 'btn-primary btn-with-icon'; if (currentType === 'success') { triggerModalBtn.style.backgroundColor = 'var(--success)'; triggerModalBtn.style.boxShadow = '0 4px 14px rgba(46, 204, 113, 0.3)'; } else if (currentType === 'warning') { triggerModalBtn.style.backgroundColor = 'var(--warning)'; triggerModalBtn.style.boxShadow = '0 4px 14px rgba(243, 156, 18, 0.3)'; } else if (currentType === 'error') { triggerModalBtn.style.backgroundColor = 'var(--error)'; triggerModalBtn.style.boxShadow = '0 4px 14px rgba(231, 76, 60, 0.3)'; } else { triggerModalBtn.style.backgroundColor = 'var(--secondary)'; triggerModalBtn.style.boxShadow = '0 4px 14px rgba(52, 152, 219, 0.3)'; } }); }); // Keyboard accessibility document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && modalMask.classList.contains('active')) { hideModal(); } }); // Initialize with default type document.querySelector('[data-type="info"]').classList.add('active'); }); </script> </body> </html>
<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { width: 100%; height: 100vh; background-color: #f5f7fa; display: flex; justify-content: center; align-items: center; overflow: hidden; } .mobile-container { width: 320px; height: 640px; background-color: #ffffff; border-radius: 30px; position: relative; overflow: hidden; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); border: 8px solid #333; transition: transform 0.5s ease; } .app-header { height: 60px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; display: flex; align-items: center; padding: 0 20px; position: relative; } .app-title { font-size: 18px; font-weight: 600; margin-left: 10px; } .app-icon { width: 32px; height: 32px; background: rgba(255, 255, 255, 0.3); border-radius: 8px; display: flex; justify-content: center; align-items: center; } .app-content { height: calc(100% - 60px); position: relative; overflow: hidden; } .menu-item { padding: 16px 20px; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; background-color: white; position: relative; z-index: 1; transition: all 0.3s ease; } .menu-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; justify-content: center; align-items: center; margin-right: 16px; } .menu-icon.blue { background-color: #e6f2ff; color: #2575fc; } .menu-icon.purple { background-color: #f0e6ff; color: #6a11cb; } .menu-icon.green { background-color: #e6fff0; color: #10b981; } .menu-icon.orange { background-color: #fff3e6; color: #f59e0b; } .menu-text { flex: 1; } .menu-title { font-size: 16px; font-weight: 500; color: #333; margin-bottom: 4px; } .menu-description { font-size: 13px; color: #666; } .fab { position: absolute; bottom: 24px; right: 24px; width: 60px; height: 60px; border-radius: 30px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); display: flex; justify-content: center; align-items: center; color: white; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); z-index: 10; cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease; } .fab:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); } .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient( circle at var(--x) var(--y), transparent 0px, rgba(0, 0, 0, 0.8) 150px ); pointer-events: none; opacity: 0; transition: opacity 0.5s ease; z-index: 20; backdrop-filter: blur(2px); } .tutorial-content { position: absolute; z-index: 30; color: white; text-align: center; width: 200px; opacity: 0; transition: opacity 0.5s ease, transform 0.5s ease; } .tutorial-title { font-size: 18px; font-weight: 600; margin-bottom: 8px; } .tutorial-description { font-size: 14px; line-height: 1.4; } .tutorial-dot { width: 8px; height: 8px; background-color: white; border-radius: 4px; display: inline-block; margin: 0 4px; opacity: 0.5; transition: all 0.3s ease; } .tutorial-dot.active { opacity: 1; width: 20px; } .dots-container { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 30; display: flex; opacity: 0; transition: opacity 0.5s ease; } .btn-next { background-color: white; color: #2575fc; border: none; padding: 8px 16px; border-radius: 20px; font-weight: 600; margin-top: 16px; cursor: pointer; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } .btn-next:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); } .btn-skip { position: absolute; top: 16px; right: 16px; background: rgba(255, 255, 255, 0.2); border: none; padding: 6px 12px; border-radius: 16px; color: white; font-size: 12px; cursor: pointer; z-index: 30; opacity: 0; transition: opacity 0.5s ease, background 0.3s ease; } .btn-skip:hover { background: rgba(255, 255, 255, 0.3); } .pulse { position: absolute; border-radius: 50%; background: rgba(255, 255, 255, 0.4); transform: scale(0); animation: pulse 2s infinite; z-index: 25; pointer-events: none; } @keyframes pulse { 0% { transform: scale(0); opacity: 0.8; } 100% { transform: scale(3); opacity: 0; } } @media (max-width: 700px) { .mobile-container { width: 90%; height: 90vh; max-height: 640px; } } </style> </head> <body> <div class="mobile-container"> <div class="app-header"> <div class="app-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> </div> <div class="app-title">HomeConnect</div> </div> <div class="app-content"> <div class="menu-item" id="item1"> <div class="menu-icon blue"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="10"></circle> <polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon> </svg> </div> <div class="menu-text"> <div class="menu-title">Smart Navigation</div> <div class="menu-description">Control all connected devices</div> </div> </div> <div class="menu-item" id="item2"> <div class="menu-icon purple"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path> </svg> </div> <div class="menu-text"> <div class="menu-title">Energy Stats</div> <div class="menu-description">Monitor usage patterns</div> </div> </div> <div class="menu-item" id="item3"> <div class="menu-icon green"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"></path> </svg> </div> <div class="menu-text"> <div class="menu-title">Climate Control</div> <div class="menu-description">Adjust temperature settings</div> </div> </div> <div class="menu-item" id="item4"> <div class="menu-icon orange"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path> <path d="M13.73 21a2 2 0 0 1-3.46 0"></path> </svg> </div> <div class="menu-text"> <div class="menu-title">Notifications</div> <div class="menu-description">Stay updated with alerts</div> </div> </div> <div class="fab" id="fab"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="12" y1="5" x2="12" y2="19"></line> <line x1="5" y1="12" x2="19" y2="12"></line> </svg> </div> </div> <div class="overlay" id="overlay"></div> <div class="tutorial-content" id="tutorial1"> <div class="tutorial-title">Smart Dashboard</div> <div class="tutorial-description">Access all your connected home devices from this intuitive dashboard.</div> <button class="btn-next" id="btn-next1">Got it</button> </div> <div class="tutorial-content" id="tutorial2"> <div class="tutorial-title">Real-time Stats</div> <div class="tutorial-description">Monitor your energy consumption with detailed analytics and insights.</div> <button class="btn-next" id="btn-next2">Next</button> </div> <div class="tutorial-content" id="tutorial3"> <div class="tutorial-title">Quick Actions</div> <div class="tutorial-description">Add new devices or create custom automation routines with a single tap.</div> <button class="btn-next" id="btn-next3">Finish</button> </div> <div class="dots-container" id="dots-container"> <div class="tutorial-dot active" id="dot1"></div> <div class="tutorial-dot" id="dot2"></div> <div class="tutorial-dot" id="dot3"></div> </div> <button class="btn-skip" id="btn-skip">Skip tour</button> </div> <script> document.addEventListener('DOMContentLoaded', function() { const overlay = document.getElementById('overlay'); const tutorial1 = document.getElementById('tutorial1'); const tutorial2 = document.getElementById('tutorial2'); const tutorial3 = document.getElementById('tutorial3'); const btnNext1 = document.getElementById('btn-next1'); const btnNext2 = document.getElementById('btn-next2'); const btnNext3 = document.getElementById('btn-next3'); const btnSkip = document.getElementById('btn-skip'); const dotsContainer = document.getElementById('dots-container'); const dot1 = document.getElementById('dot1'); const dot2 = document.getElementById('dot2'); const dot3 = document.getElementById('dot3'); const item1 = document.getElementById('item1'); const fab = document.getElementById('fab'); let currentStep = 0; // Start the tutorial after a short delay setTimeout(() => { startTutorial(); }, 800); function startTutorial() { currentStep = 1; showOverlay(item1); overlay.style.opacity = '1'; btnSkip.style.opacity = '1'; dotsContainer.style.opacity = '1'; positionTutorial(tutorial1, item1, 'bottom', 20); tutorial1.style.opacity = '1'; // Create pulse effect createPulseEffect(item1); } function positionTutorial(tutorialEl, targetEl, position, offset = 10) { const targetRect = targetEl.getBoundingClientRect(); const containerRect = document.querySelector('.mobile-container').getBoundingClientRect(); // Calculate relative position within the container const relativeTop = targetRect.top - containerRect.top; const relativeLeft = targetRect.left - containerRect.left; switch(position) { case 'top': tutorialEl.style.top = `${relativeTop - tutorialEl.offsetHeight - offset}px`; tutorialEl.style.left = `${relativeLeft + targetRect.width / 2 - tutorialEl.offsetWidth / 2}px`; break; case 'bottom': tutorialEl.style.top = `${relativeTop + targetRect.height + offset}px`; tutorialEl.style.left = `${relativeLeft + targetRect.width / 2 - tutorialEl.offsetWidth / 2}px`; break; case 'left': tutorialEl.style.top = `${relativeTop + targetRect.height / 2 - tutorialEl.offsetHeight / 2}px`; tutorialEl.style.left = `${relativeLeft - tutorialEl.offsetWidth - offset}px`; break; case 'right': tutorialEl.style.top = `${relativeTop + targetRect.height / 2 - tutorialEl.offsetHeight / 2}px`; tutorialEl.style.left = `${relativeLeft + targetRect.width + offset}px`; break; } } function showOverlay(targetEl) { const rect = targetEl.getBoundingClientRect(); const containerRect = document.querySelector('.mobile-container').getBoundingClientRect(); // Calculate center point of the target element const centerX = rect.left + rect.width / 2 - containerRect.left; const centerY = rect.top + rect.height / 2 - containerRect.top; // Set the spotlight position overlay.style.setProperty('--x', `${centerX}px`); overlay.style.setProperty('--y', `${centerY}px`); } function createPulseEffect(targetEl) { const rect = targetEl.getBoundingClientRect(); const containerRect = document.querySelector('.mobile-container').getBoundingClientRect(); // Calculate center point of the target element const centerX = rect.left + rect.width / 2 - containerRect.left; const centerY = rect.top + rect.height / 2 - containerRect.top; const pulse = document.createElement('div'); pulse.classList.add('pulse'); pulse.style.top = `${centerY}px`; pulse.style.left = `${centerX}px`; pulse.style.width = `${Math.max(rect.width, rect.height) * 0.5}px`; pulse.style.height = `${Math.max(rect.width, rect.height) * 0.5}px`; document.querySelector('.mobile-container').appendChild(pulse); // Remove pulse after animation completes setTimeout(() => { pulse.remove(); }, 2000); } function goToNextStep() { // Hide current tutorial if (currentStep === 1) { tutorial1.style.opacity = '0'; dot1.classList.remove('active'); dot2.classList.add('active'); setTimeout(() => { showOverlay(item1); positionTutorial(tutorial2, item1, 'bottom', 20); tutorial2.style.opacity = '1'; createPulseEffect(item1); }, 500); } else if (currentStep === 2) { tutorial2.style.opacity = '0'; dot2.classList.remove('active'); dot3.classList.add('active'); setTimeout(() => { showOverlay(fab); positionTutorial(tutorial3, fab, 'top', 15); tutorial3.style.opacity = '1'; createPulseEffect(fab); }, 500); } else if (currentStep === 3) { endTutorial(); } currentStep++; } function endTutorial() { tutorial1.style.opacity = '0'; tutorial2.style.opacity = '0'; tutorial3.style.opacity = '0'; overlay.style.opacity = '0'; btnSkip.style.opacity = '0'; dotsContainer.style.opacity = '0'; // Add a subtle animation to indicate the tutorial is complete document.querySelector('.mobile-container').style.transform = 'scale(1.02)'; setTimeout(() => { document.querySelector('.mobile-container').style.transform = 'scale(1)'; }, 300); } // Event listeners btnNext1.addEventListener('click', goToNextStep); btnNext2.addEventListener('click', goToNextStep); btnNext3.addEventListener('click', goToNextStep); btnSkip.addEventListener('click', endTutorial); // Make the dots clickable to navigate dot1.addEventListener('click', () => { if (currentStep !== 1) { tutorial1.style.opacity = '0'; tutorial2.style.opacity = '0'; tutorial3.style.opacity = '0'; dot1.classList.add('active'); dot2.classList.remove('active'); dot3.classList.remove('active'); currentStep = 1; setTimeout(() => { showOverlay(item1); positionTutorial(tutorial1, item1, 'bottom', 20); tutorial1.style.opacity = '1'; createPulseEffect(item1); }, 500); } }); dot2.addEventListener('click', () => { if (currentStep !== 2) { tutorial1.style.opacity = '0'; tutorial2.style.opacity = '0'; tutorial3.style.opacity = '0'; dot1.classList.remove('active'); dot2.classList.add('active'); dot3.classList.remove('active'); currentStep = 2; setTimeout(() => { showOverlay(item1); positionTutorial(tutorial2, item1, 'bottom', 20); tutorial2.style.opacity = '1'; createPulseEffect(item1); }, 500); } }); dot3.addEventListener('click', () => { if (currentStep !== 3) { tutorial1.style.opacity = '0'; tutorial2.style.opacity = '0'; tutorial3.style.opacity = '0'; dot1.classList.remove('active'); dot2.classList.remove('active'); dot3.classList.add('active'); currentStep = 3; setTimeout(() => { showOverlay(fab); positionTutorial(tutorial3, fab, 'top', 15); tutorial3.style.opacity = '1'; createPulseEffect(fab); }, 500); } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Immersive Media Player</title> <style> :root { --primary-color: #3a86ff; --overlay-color: rgba(0, 0, 0, 0.7); --text-color: #ffffff; --accent-color: #ff006e; --secondary-color: #8338ec; --tertiary-color: #ffbe0b; --progress-height: 6px; --control-size: 42px; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; height: 100%; width: 100%; overflow: hidden; color: var(--text-color); background-color: #121212; } .player-container { position: relative; width: 100%; max-width: 700px; height: 700px; margin: 0 auto; overflow: hidden; border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); } .video-element { width: 100%; height: 100%; object-fit: cover; display: block; } .control-mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(0deg, var(--overlay-color) 0%, transparent 35%, transparent 65%, var(--overlay-color) 100%); opacity: 0; transition: opacity 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); display: flex; flex-direction: column; justify-content: space-between; z-index: 10; } .player-container:hover .control-mask, .control-mask.active { opacity: 1; } .top-controls { padding: 24px; display: flex; justify-content: space-between; align-items: center; } .title-area { max-width: 80%; } .video-title { font-size: 20px; font-weight: 600; margin-bottom: 4px; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } .video-subtitle { font-size: 14px; opacity: 0.8; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } .settings-button { background: none; border: none; color: var(--text-color); cursor: pointer; width: var(--control-size); height: var(--control-size); border-radius: 50%; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; transition: transform 0.3s ease; } .settings-button:hover { transform: rotate(30deg); } .settings-button svg { width: 24px; height: 24px; fill: currentColor; } .bottom-controls { padding: 24px; } .progress-container { height: 20px; display: flex; align-items: center; cursor: pointer; position: relative; margin-bottom: 16px; } .progress-bar { height: var(--progress-height); width: 100%; background-color: rgba(255, 255, 255, 0.2); border-radius: 6px; position: relative; overflow: hidden; transition: height 0.2s ease; } .progress-container:hover .progress-bar { height: calc(var(--progress-height) + 2px); } .progress-fill { position: absolute; left: 0; top: 0; height: 100%; width: 50%; background: linear-gradient(90deg, var(--primary-color) 0%, var(--secondary-color) 100%); border-radius: 6px; transition: width 0.1s linear; } .progress-handle { position: absolute; width: 16px; height: 16px; background-color: var(--text-color); border-radius: 50%; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0); transition: transform 0.2s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); z-index: 2; } .progress-container:hover .progress-handle { transform: translate(-50%, -50%) scale(1); } .preview-thumbnail { position: absolute; bottom: 30px; transform: translateX(-50%); background-color: #000; border: 2px solid white; border-radius: 8px; width: 160px; height: 90px; object-fit: cover; opacity: 0; transition: opacity 0.2s ease; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); pointer-events: none; z-index: 100; } .progress-container:hover .preview-thumbnail { opacity: 1; } .time-display { margin-bottom: 16px; font-size: 14px; display: flex; justify-content: space-between; font-variant-numeric: tabular-nums; } .main-controls { display: flex; align-items: center; justify-content: space-between; } .left-controls, .right-controls { display: flex; align-items: center; gap: 16px; } .control-button { background: none; border: none; color: var(--text-color); cursor: pointer; width: var(--control-size); height: var(--control-size); border-radius: 50%; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; } .control-button svg { width: 24px; height: 24px; fill: currentColor; z-index: 2; transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .control-button:hover svg { transform: scale(1.1); } .control-button::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background-color: rgba(255, 255, 255, 0.2); border-radius: 50%; transform: translate(-50%, -50%); transition: width 0.4s ease, height 0.4s ease; } .control-button:hover::before { width: 100%; height: 100%; } .play-button { width: 54px; height: 54px; background-color: rgba(255, 255, 255, 0.1); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), background-color 0.3s ease; } .play-button:hover { transform: scale(1.1); background-color: rgba(255, 255, 255, 0.2); } .play-button svg { width: 30px; height: 30px; } .pulse-effect { position: absolute; width: 100%; height: 100%; border-radius: 50%; background-color: rgba(255, 255, 255, 0.4); z-index: 1; animation: pulse 0.8s cubic-bezier(0, 0.55, 0.45, 1) forwards; opacity: 0; transform: scale(0.5); } @keyframes pulse { 0% { opacity: 0.8; transform: scale(0.5); } 100% { opacity: 0; transform: scale(1.5); } } .volume-control { display: flex; align-items: center; position: relative; } .volume-slider { width: 0; height: 4px; background-color: rgba(255, 255, 255, 0.2); border-radius: 4px; overflow: hidden; transition: width 0.3s ease; margin-left: 8px; position: relative; } .volume-control:hover .volume-slider { width: 80px; } .volume-level { position: absolute; left: 0; top: 0; height: 100%; width: 70%; background-color: var(--text-color); border-radius: 4px; } .caption-button.active { color: var(--accent-color); } .fullscreen-button.active svg { transform: scale(0.8); } .immersive-indicator { position: absolute; top: 24px; right: 24px; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); padding: 6px 12px; border-radius: 20px; display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); z-index: 11; } .immersive-indicator svg { width: 16px; height: 16px; fill: var(--tertiary-color); } .immersive-text { background: linear-gradient(90deg, #ffbe0b, #fb5607); -webkit-background-clip: text; background-clip: text; color: transparent; } .skip-intro { position: absolute; right: 24px; bottom: 120px; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); padding: 10px 16px; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: transform 0.2s ease, background-color 0.2s ease; border: 1px solid rgba(255, 255, 255, 0.1); z-index: 11; } .skip-intro:hover { transform: translateY(-2px); background-color: rgba(0, 0, 0, 0.7); } .ripple { position: absolute; border-radius: 50%; background-color: rgba(255, 255, 255, 0.4); transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; } @keyframes ripple { to { transform: scale(2); opacity: 0; } } /* Responsive Adjustments */ @media (max-width: 500px) { .player-container { border-radius: 0; } .top-controls, .bottom-controls { padding: 16px; } .video-title { font-size: 16px; } .video-subtitle { font-size: 12px; } .control-button, .settings-button { --control-size: 36px; } .play-button { width: 48px; height: 48px; } .right-controls { gap: 8px; } .immersive-indicator { top: 16px; right: 16px; font-size: 11px; padding: 4px 8px; } .immersive-indicator svg { width: 12px; height: 12px; } .skip-intro { right: 16px; bottom: 100px; font-size: 12px; padding: 8px 12px; } } @media (max-height: 500px) { .top-controls, .bottom-controls { padding: 12px; } } </style> </head> <body> <div class="player-container"> <video class="video-element" poster="https://images.unsplash.com/photo-1680266113910-b13636e6dc28?q=80&w=2834&auto=format&fit=crop"> <!-- Video source would be here in production --> </video> <div class="immersive-indicator"> <svg viewBox="0 0 24 24"> <path d="M21 4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H3V6h18v12z"/> <path d="M6.5 9H8v6H6.5zM16 9h1.5v6H16zM9.5 9H10c.8 0 1.5.7 1.5 1.5v3c0 .8-.7 1.5-1.5 1.5H9.5V9zM11 13.5v-3c0-.3-.2-.5-.5-.5H11v4h-.5c.3 0 .5-.2.5-.5zM14.5 9c.8 0 1.5.7 1.5 1.5v3c0 .8-.7 1.5-1.5 1.5h-2V9h2zm0 4.5c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5h-1v4h1z"/> </svg> <span class="immersive-text">360Β° Immersive</span> </div> <div class="skip-intro">Skip Intro (0:45)</div> <div class="control-mask"> <div class="top-controls"> <div class="title-area"> <h1 class="video-title">Midnight in Tokyo: A Virtual Journey</h1> <p class="video-subtitle">VR Documentary Series β’ Episode 3</p> </div> <button class="settings-button"> <svg viewBox="0 0 24 24"> <path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"></path> </svg> </button> </div> <div class="bottom-controls"> <div class="progress-container"> <div class="progress-bar"> <div class="progress-fill"></div> </div> <div class="progress-handle"></div> <img class="preview-thumbnail" src="https://images.unsplash.com/photo-1551766460-1e9da70d2ccb?q=80&w=2874&auto=format&fit=crop" alt="Preview"> </div> <div class="time-display"> <span class="current-time">8:24</span> <span class="total-time">23:45</span> </div> <div class="main-controls"> <div class="left-controls"> <button class="control-button play-button"> <svg viewBox="0 0 24 24"> <path d="M8 5v14l11-7z"></path> </svg> </button> <button class="control-button rewind-button"> <svg viewBox="0 0 24 24"> <path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"></path> </svg> </button> <button class="control-button forward-button"> <svg viewBox="0 0 24 24"> <path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"></path> </svg> </button> <div class="volume-control"> <button class="control-button volume-button"> <svg viewBox="0 0 24 24"> <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path> </svg> </button> <div class="volume-slider"> <div class="volume-level"></div> </div> </div> </div> <div class="right-controls"> <button class="control-button caption-button"> <svg viewBox="0 0 24 24"> <path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"></path> </svg> </button> <button class="control-button quality-button"> <svg viewBox="0 0 24 24"> <path d="M14.5 13.5h2v-3h-2M18 14a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4a1 1 0 011-1h4a1 1 0 011 1M7.5 15H9v-4.5H6v1.25h1.5M14 8.5a3.54 3.54 0 012.44 1l1.06-1.06A5 5 0 0014 7a5 5 0 00-5 5 5 5 0 005 5 5 5 0 003.5-1.5l-1.06-1.06A3.54 3.54 0 0114 15.5a3.5 3.5 0 01-3.5-3.5 3.5 3.5 0 013.5-3.5z"></path> </svg> </button> <button class="control-button fullscreen-button"> <svg viewBox="0 0 24 24"> <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path> </svg> </button> </div> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const playerContainer = document.querySelector('.player-container'); const controlMask = document.querySelector('.control-mask'); const playButton = document.querySelector('.play-button'); const video = document.querySelector('.video-element'); const progressFill = document.querySelector('.progress-fill'); const progressBar = document.querySelector('.progress-bar'); const progressHandle = document.querySelector('.progress-handle'); const previewThumbnail = document.querySelector('.preview-thumbnail'); const fullscreenButton = document.querySelector('.fullscreen-button'); const captionButton = document.querySelector('.caption-button'); const volumeButton = document.querySelector('.volume-button'); const progressContainer = document.querySelector('.progress-container'); const skipIntro = document.querySelector('.skip-intro'); const controlButtons = document.querySelectorAll('.control-button'); const videoElement = document.querySelector('.video-element'); // Simulate video autoplay with poster as placeholder videoElement.poster = 'https://images.unsplash.com/photo-1680266113910-b13636e6dc28?q=80&w=2834&auto=format&fit=crop'; let isPlaying = false; let controlTimeout; // Toggle play/pause function togglePlay() { isPlaying = !isPlaying; if (isPlaying) { playButton.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path> </svg> `; // Simulate video starting from poster image if (!video.src) { // In a real implementation, this would be setting the video source video.poster = ''; video.style.backgroundColor = '#000'; } } else { playButton.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M8 5v14l11-7z"></path> </svg> `; } createPulseEffect(playButton); } // Handle progress bar interaction progressContainer.addEventListener('mousemove', (e) => { const rect = progressContainer.getBoundingClientRect(); const position = (e.clientX - rect.left) / rect.width; // Update thumbnail preview position previewThumbnail.style.left = `${position * 100}%`; // Only change progress visually on click/drag, not hover if (e.buttons !== 1) return; progressFill.style.width = `${position * 100}%`; progressHandle.style.left = `${position * 100}%`; }); progressContainer.addEventListener('click', (e) => { const rect = progressContainer.getBoundingClientRect(); const position = (e.clientX - rect.left) / rect.width; progressFill.style.width = `${position * 100}%`; progressHandle.style.left = `${position * 100}%`; // Here you would set video.currentTime }); // Create pulse effect on control buttons function createPulseEffect(element) { const pulse = document.createElement('div'); pulse.classList.add('pulse-effect'); element.appendChild(pulse); setTimeout(() => { pulse.remove(); }, 800); } // Create ripple effect on button clicks function createRipple(event) { const button = event.currentTarget; const ripple = document.createElement('span'); const rect = button.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); const x = event.clientX - rect.left - size / 2; const y = event.clientY - rect.top - size / 2; ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; ripple.classList.add('ripple'); button.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); } // Hide controls after inactivity function hideControls() { clearTimeout(controlTimeout); controlTimeout = setTimeout(() => { if (isPlaying) { controlMask.classList.remove('active'); } }, 3000); } // Show controls on mouse movement playerContainer.addEventListener('mousemove', () => { controlMask.classList.add('active'); hideControls(); }); // Event listeners playButton.addEventListener('click', togglePlay); fullscreenButton.addEventListener('click', (e) => { createRipple(e); fullscreenButton.classList.toggle('active'); createPulseEffect(fullscreenButton); // Simulate fullscreen toggle if (document.fullscreenElement) { document.exitFullscreen(); } else { playerContainer.requestFullscreen().catch(err => { console.log('Error attempting to enable fullscreen:', err); }); } }); captionButton.addEventListener('click', (e) => { createRipple(e); captionButton.classList.toggle('active'); createPulseEffect(captionButton); }); volumeButton.addEventListener('click', (e) => { createRipple(e); createPulseEffect(volumeButton); // Toggle between muted and unmuted const isMuted = volumeButton.classList.toggle('muted'); if (isMuted) { volumeButton.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"></path> </svg> `; document.querySelector('.volume-level').style.width = '0%'; } else { volumeButton.innerHTML = ` <svg viewBox="0 0 24 24"> <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path> </svg> `; document.querySelector('.volume-level').style.width = '70%'; } }); // Add ripple effect to all control buttons controlButtons.forEach(button => { button.addEventListener('click', createRipple); }); // Skip intro button skipIntro.addEventListener('click', () => { progressFill.style.width = '20%'; skipIntro.style.opacity = '0'; setTimeout(() => { skipIntro.style.display = 'none'; }, 300); }); // Simulate video progress over time (for demo purposes) if (!isPlaying) { let progress = 0.1; setInterval(() => { if (isPlaying && progress < 1) { progress += 0.001; progressFill.style.width = `${progress * 100}%`; progressHandle.style.left = `${progress * 100}%`; // Hide skip intro after "skipping" if (progress > 0.2 && skipIntro.style.display !== 'none') { skipIntro.style.opacity = '0'; setTimeout(() => { skipIntro.style.display = 'none'; }, 300); } } }, 100); } // Initialize with visible controls controlMask.classList.add('active'); hideControls(); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Gradient Fading Carousel Gallery</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } :root { --primary-color: #2c3e50; --secondary-color: #3498db; --accent-color: #e74c3c; --text-light: #ecf0f1; --text-dark: #2c3e50; --transition-speed: 0.6s; --shadow: 0 4px 20px rgba(0, 0, 0, 0.15); } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background-color: #f9f9f9; display: flex; justify-content: center; align-items: center; min-height: 100vh; color: var(--text-dark); overflow-x: hidden; } .carousel-container { position: relative; width: 100%; max-width: 700px; height: 700px; overflow: hidden; border-radius: 16px; box-shadow: var(--shadow); background-color: white; } .gallery-title { position: absolute; top: 30px; left: 40px; z-index: 10; max-width: 80%; } .gallery-title h1 { font-size: 2rem; font-weight: 700; margin-bottom: 8px; color: var(--text-light); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); transition: opacity 0.3s; } .gallery-title p { font-size: 1rem; line-height: 1.5; color: var(--text-light); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); opacity: 0.9; transition: opacity 0.3s; } .carousel-track { display: flex; transition: transform var(--transition-speed) cubic-bezier(0.25, 1, 0.5, 1); height: 100%; } .carousel-slide { flex: 0 0 100%; position: relative; overflow: hidden; } .slide-image { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; } .slide-content { position: absolute; bottom: 40px; left: 40px; max-width: 60%; z-index: 10; transition: transform 0.4s ease, opacity 0.4s ease; } .slide-content h2 { font-size: 1.6rem; font-weight: 600; margin-bottom: 10px; color: var(--text-light); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } .slide-content p { font-size: 1rem; line-height: 1.5; color: var(--text-light); margin-bottom: 15px; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } .slide-button { display: inline-block; padding: 10px 20px; background-color: var(--secondary-color); color: white; border: none; border-radius: 30px; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 10px rgba(52, 152, 219, 0.3); } .slide-button:hover { background-color: #2980b9; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4); } .slide-gradient { position: absolute; inset: 0; pointer-events: none; z-index: 5; opacity: 0.7; transition: opacity 0.4s ease; } .slide-active .slide-gradient { opacity: 0.7; background: linear-gradient( to right, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.5) 40%, rgba(0, 0, 0, 0.2) 60%, rgba(0, 0, 0, 0) 100% ); } .slide-prev .slide-gradient, .slide-next .slide-gradient { opacity: 0.9; background: rgba(0, 0, 0, 0.6); } .carousel-controls { position: absolute; bottom: 40px; right: 40px; display: flex; gap: 15px; z-index: 20; } .control-button { width: 46px; height: 46px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.2); border: 2px solid rgba(255, 255, 255, 0.4); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(5px); color: white; } .control-button:hover { background-color: rgba(255, 255, 255, 0.3); transform: scale(1.05); } .control-button:active { transform: scale(0.98); } .control-button svg { width: 20px; height: 20px; stroke: currentColor; stroke-width: 2; fill: none; } .indicators { position: absolute; bottom: 0; left: 0; width: 100%; height: 5px; display: flex; z-index: 10; } .indicator { flex: 1; height: 100%; background-color: rgba(255, 255, 255, 0.3); position: relative; overflow: hidden; } .indicator::after { content: ''; position: absolute; width: 100%; height: 100%; background-color: var(--secondary-color); transform: translateX(-100%); transition: transform 0.3s ease; } .indicator.active::after { transform: translateX(0); } @media (max-width: 700px) { .gallery-title { top: 20px; left: 20px; } .gallery-title h1 { font-size: 1.6rem; } .gallery-title p { font-size: 0.9rem; } .slide-content { bottom: 80px; left: 20px; max-width: 80%; } .slide-content h2 { font-size: 1.4rem; } .carousel-controls { bottom: 20px; right: 20px; } .control-button { width: 40px; height: 40px; } } /* Animation classes */ .slide-active .slide-content { transform: translateY(0); opacity: 1; } .slide-next .slide-content, .slide-prev .slide-content { transform: translateY(30px); opacity: 0; } .slide-active .slide-image { transform: scale(1); } .slide-next .slide-image, .slide-prev .slide-image { transform: scale(1.1); } /* Hover effect for active slide */ .slide-active:hover .slide-image { transform: scale(1.03); transition: transform 10s ease; } /* Fade-in animation for loading */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .carousel-container { animation: fadeIn 0.8s ease-out; } /* Auto-playing progress bar animation */ @keyframes progressAnimation { from { transform: scaleX(0); } to { transform: scaleX(1); } } .progress-bar { position: absolute; bottom: 5px; left: 0; height: 5px; width: 100%; background-color: rgba(255, 255, 255, 0.2); z-index: 15; transform-origin: left; transform: scaleX(0); } .progress-bar.animate { animation: progressAnimation 5s linear forwards; } /* Custom cursor on carousel */ .carousel-container { cursor: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='12' cy='12' r='11' stroke='white' stroke-width='2'/%3E%3C/svg%3E") 12 12, auto; } /* Tooltip styling */ .tooltip { position: relative; } .tooltip::after { content: attr(data-tooltip); position: absolute; top: -40px; left: 50%; transform: translateX(-50%); background-color: rgba(0, 0, 0, 0.8); color: white; padding: 5px 10px; border-radius: 5px; font-size: 0.8rem; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 0.3s; } .tooltip:hover::after { opacity: 1; } </style> </head> <body> <div class="carousel-container"> <div class="gallery-title"> <h1>Gradient Framed Perspectives</h1> <p>Experience images through the lens of subtle focus and context</p> </div> <div class="carousel-track"> <!-- Slide 1 --> <div class="carousel-slide slide-active"> <img src="https://images.unsplash.com/photo-1682687220509-61b8a906ca19?q=80&w=1470&auto=format&fit=crop" alt="Modern architecture with glass facade" class="slide-image"> <div class="slide-gradient"></div> <div class="slide-content"> <h2>Architectural Clarity</h2> <p>The gradient mask draws attention to structural details while softly blending peripheral elements, creating a focused viewing experience.</p> <button class="slide-button">Explore Architecture</button> </div> </div> <!-- Slide 2 --> <div class="carousel-slide slide-next"> <img src="https://images.unsplash.com/photo-1682687982029-edb9aecf5f89?q=80&w=1471&auto=format&fit=crop" alt="Urban landscape with dramatic lighting" class="slide-image"> <div class="slide-gradient"></div> <div class="slide-content"> <h2>Urban Narrative</h2> <p>The transition effect creates a visual rhythm, allowing each composition to gradually reveal its story as you navigate through the gallery.</p> <button class="slide-button">View Collection</button> </div> </div> <!-- Slide 3 --> <div class="carousel-slide"> <img src="https://images.unsplash.com/photo-1682695795255-b236b1f1267d?q=80&w=1470&auto=format&fit=crop" alt="Portrait with dramatic shadows" class="slide-image"> <div class="slide-gradient"></div> <div class="slide-content"> <h2>Shadow & Light</h2> <p>Adjustable opacity levels enhance the visual hierarchy, bringing balance between the featured subject and the surrounding environment.</p> <button class="slide-button">Discover Techniques</button> </div> </div> <!-- Slide 4 --> <div class="carousel-slide"> <img src="https://images.unsplash.com/photo-1682687220063-4742bd7fd538?q=80&w=1470&auto=format&fit=crop" alt="Abstract composition with geometric shapes" class="slide-image"> <div class="slide-gradient"></div> <div class="slide-content"> <h2>Geometric Abstractions</h2> <p>The gradient framing technique emphasizes form and composition, while the fading transitions create a continuous visual flow between images.</p> <button class="slide-button">See Gallery</button> </div> </div> <!-- Slide 5 --> <div class="carousel-slide"> <img src="https://images.unsplash.com/photo-1682685795557-976f03abb3be?q=80&w=1471&auto=format&fit=crop" alt="Natural landscape with dramatic lighting" class="slide-image"> <div class="slide-gradient"></div> <div class="slide-content"> <h2>Landscape Perspectives</h2> <p>The peripheral de-emphasis creates a natural vignette effect, drawing the viewer's eye into the depth and scale of the landscape.</p> <button class="slide-button">Explore Landscapes</button> </div> </div> </div> <div class="carousel-controls"> <button class="control-button prev tooltip" data-tooltip="Previous"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M15 6l-6 6 6 6" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <button class="control-button next tooltip" data-tooltip="Next"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M9 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> <div class="indicators"> <div class="indicator active"></div> <div class="indicator"></div> <div class="indicator"></div> <div class="indicator"></div> <div class="indicator"></div> </div> <div class="progress-bar animate"></div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const track = document.querySelector('.carousel-track'); const slides = Array.from(document.querySelectorAll('.carousel-slide')); const nextButton = document.querySelector('.next'); const prevButton = document.querySelector('.prev'); const indicators = Array.from(document.querySelectorAll('.indicator')); const progressBar = document.querySelector('.progress-bar'); let currentIndex = 0; let slideWidth = slides[0].getBoundingClientRect().width; let autoplayInterval; let touchStartX = 0; let touchEndX = 0; const slideCount = slides.length; // Initialize autoplay startAutoplay(); // Update slide width on window resize window.addEventListener('resize', () => { slideWidth = slides[0].getBoundingClientRect().width; updateSlidePosition(); }); // Next button click handler nextButton.addEventListener('click', () => { clearAutoplay(); nextSlide(); startAutoplay(); }); // Previous button click handler prevButton.addEventListener('click', () => { clearAutoplay(); prevSlide(); startAutoplay(); }); // Touch events for mobile swiping track.addEventListener('touchstart', (e) => { clearAutoplay(); touchStartX = e.touches[0].clientX; }); track.addEventListener('touchmove', (e) => { touchEndX = e.touches[0].clientX; }); track.addEventListener('touchend', () => { handleSwipe(); startAutoplay(); }); // Handle keyboard navigation document.addEventListener('keydown', (e) => { if (e.key === 'ArrowRight') { clearAutoplay(); nextSlide(); startAutoplay(); } else if (e.key === 'ArrowLeft') { clearAutoplay(); prevSlide(); startAutoplay(); } }); // Pause autoplay when hovering over the carousel document.querySelector('.carousel-container').addEventListener('mouseenter', clearAutoplay); document.querySelector('.carousel-container').addEventListener('mouseleave', startAutoplay); // Click indicator to go to specific slide indicators.forEach((indicator, index) => { indicator.addEventListener('click', () => { clearAutoplay(); goToSlide(index); startAutoplay(); }); }); // Functions function updateSlidePosition() { track.style.transform = `translateX(-${currentIndex * slideWidth}px)`; // Update slide classes slides.forEach((slide, index) => { slide.classList.remove('slide-active', 'slide-prev', 'slide-next'); if (index === currentIndex) { slide.classList.add('slide-active'); } else if (index === getPrevIndex()) { slide.classList.add('slide-prev'); } else if (index === getNextIndex()) { slide.classList.add('slide-next'); } }); // Update indicators indicators.forEach((indicator, index) => { indicator.classList.toggle('active', index === currentIndex); }); // Reset and start progress bar animation progressBar.classList.remove('animate'); void progressBar.offsetWidth; // Force reflow progressBar.classList.add('animate'); } function nextSlide() { currentIndex = (currentIndex + 1) % slideCount; updateSlidePosition(); } function prevSlide() { currentIndex = (currentIndex - 1 + slideCount) % slideCount; updateSlidePosition(); } function goToSlide(index) { currentIndex = index; updateSlidePosition(); } function getPrevIndex() { return (currentIndex - 1 + slideCount) % slideCount; } function getNextIndex() { return (currentIndex + 1) % slideCount; } function handleSwipe() { const swipeThreshold = 50; if (touchStartX - touchEndX > swipeThreshold) { nextSlide(); // Swipe left, go to next } else if (touchEndX - touchStartX > swipeThreshold) { prevSlide(); // Swipe right, go to previous } } function startAutoplay() { clearAutoplay(); progressBar.classList.add('animate'); autoplayInterval = setInterval(() => { nextSlide(); }, 5000); } function clearAutoplay() { clearInterval(autoplayInterval); progressBar.classList.remove('animate'); } // Add click interaction to visible slides slides.forEach((slide) => { slide.addEventListener('click', (e) => { // If clicking on an inactive slide, make it active if (!slide.classList.contains('slide-active')) { clearAutoplay(); const clickedIndex = slides.indexOf(slide); goToSlide(clickedIndex); startAutoplay(); } }); }); // Initialize slide positions updateSlidePosition(); // Add a custom cursor trail effect const container = document.querySelector('.carousel-container'); container.addEventListener('mousemove', (e) => { // Get mouse position relative to container const rect = container.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Calculate the gradient direction based on mouse position const activeSlide = document.querySelector('.slide-active'); const gradient = activeSlide.querySelector('.slide-gradient'); // Only apply custom gradient if hovering over the active slide if (activeSlide && x > 0 && x < rect.width && y > 0 && y < rect.height) { const centerX = rect.width / 2; const ratio = (x - centerX) / centerX; // -1 to 1 value // Adjust the gradient based on mouse position gradient.style.background = ` radial-gradient( circle at ${x}px ${y}px, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.2) 20%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.7) 100% ), linear-gradient( to right, rgba(0, 0, 0, ${0.8 + ratio * 0.2}) 0%, rgba(0, 0, 0, ${0.5 + ratio * 0.1}) 40%, rgba(0, 0, 0, ${0.2 - ratio * 0.1}) 60%, rgba(0, 0, 0, ${ratio > 0 ? 0.3 : 0}) 100% ) `; } }); // Reset gradient on mouse leave container.addEventListener('mouseleave', () => { const activeSlide = document.querySelector('.slide-active'); const gradient = activeSlide.querySelector('.slide-gradient'); gradient.style.background = ` linear-gradient( to right, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.5) 40%, rgba(0, 0, 0, 0.2) 60%, rgba(0, 0, 0, 0) 100% ) `; }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Upload Mask</title> <style> :root { --primary: #4361ee; --primary-light: #4896ff; --success: #2ecc71; --background: #f8fafc; --text: #333; --overlay: rgba(255, 255, 255, 0.9); --shadow: 0 4px 12px rgba(0, 0, 0, 0.08); --border-radius: 12px; --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: var(--background); padding: 20px; overflow-x: hidden; } .upload-container { width: 100%; max-width: 500px; background-color: white; border-radius: var(--border-radius); box-shadow: var(--shadow); padding: 30px; position: relative; overflow: hidden; transition: var(--transition); } .upload-header { margin-bottom: 20px; } .upload-header h2 { color: var(--text); font-size: 24px; margin-bottom: 8px; } .upload-header p { color: #666; font-size: 14px; line-height: 1.5; } .upload-area { border: 2px dashed #ddd; border-radius: var(--border-radius); padding: 40px 20px; text-align: center; transition: var(--transition); cursor: pointer; margin-bottom: 20px; position: relative; } .upload-area:hover, .upload-area.drag-over { border-color: var(--primary); background-color: rgba(67, 97, 238, 0.05); } .upload-icon { font-size: 48px; color: var(--primary); margin-bottom: 16px; } .upload-area h3 { color: var(--text); margin-bottom: 8px; font-weight: 500; } .upload-area p { color: #666; font-size: 14px; margin-bottom: 16px; } .browse-btn { display: inline-block; padding: 8px 16px; background-color: var(--primary); color: white; border-radius: 6px; font-size: 14px; cursor: pointer; transition: var(--transition); } .browse-btn:hover { background-color: var(--primary-light); transform: translateY(-1px); } #file-input { display: none; } .files-list { margin-top: 20px; } .file-item { display: flex; align-items: center; padding: 12px; background: #f5f7fa; border-radius: 8px; margin-bottom: 10px; } .file-icon { margin-right: 12px; color: var(--primary); font-size: 20px; } .file-details { flex: 1; } .file-name { font-size: 14px; font-weight: 500; color: var(--text); margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; } .file-size { font-size: 12px; color: #666; } .file-actions { margin-left: 8px; } .remove-btn { background: none; border: none; color: #ff4757; cursor: pointer; font-size: 16px; padding: 4px; opacity: 0.7; transition: var(--transition); } .remove-btn:hover { opacity: 1; } .upload-mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--overlay); backdrop-filter: blur(4px); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 10; opacity: 0; visibility: hidden; transition: var(--transition); padding: 40px; } .upload-mask.active { opacity: 1; visibility: visible; } .progress-container { width: 100%; max-width: 300px; margin-bottom: 16px; } .progress-bar { height: 8px; background: #e9ecef; border-radius: 20px; overflow: hidden; position: relative; } .progress-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--primary), var(--primary-light)); border-radius: 20px; transition: width 0.3s ease; position: relative; } .progress-glow { position: absolute; top: 0; right: 0; height: 100%; width: 20px; background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.8), rgba(255,255,255,0)); animation: glow 1.5s infinite; opacity: 0; } @keyframes glow { 0% { transform: translateX(-100%); } 100% { transform: translateX(500%); } } .progress-pulsate { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80px; height: 80px; border-radius: 50%; background: rgba(67, 97, 238, 0.1); z-index: -1; animation: pulsate 2s ease-out infinite; } @keyframes pulsate { 0% { transform: translate(-50%, -50%) scale(0.95); opacity: 0.7; } 50% { transform: translate(-50%, -50%) scale(1); opacity: 0.3; } 100% { transform: translate(-50%, -50%) scale(0.95); opacity: 0.7; } } .progress-status { font-size: 14px; color: var(--text); margin-top: 12px; text-align: center; font-weight: 500; } .progress-details { display: flex; justify-content: space-between; font-size: 12px; color: #666; margin-top: 8px; } .cancel-upload { margin-top: 20px; padding: 8px 16px; border: none; background: transparent; color: #666; border-radius: 6px; cursor: pointer; font-size: 14px; transition: var(--transition); border: 1px solid #ddd; } .cancel-upload:hover { background-color: #f5f5f5; color: #ff4757; } .upload-complete { display: flex; flex-direction: column; align-items: center; opacity: 0; visibility: hidden; position: absolute; transition: var(--transition); } .upload-complete.active { opacity: 1; visibility: visible; } .success-icon { width: 60px; height: 60px; background-color: var(--success); border-radius: 50%; margin-bottom: 16px; position: relative; display: flex; justify-content: center; align-items: center; animation: scaleIn 0.5s ease forwards; } @keyframes scaleIn { 0% { transform: scale(0); } 70% { transform: scale(1.1); } 100% { transform: scale(1); } } .checkmark { width: 24px; height: 12px; border-bottom: 3px solid white; border-right: 3px solid white; transform: rotate(45deg) translate(-2px, -2px); animation: checkmark 0.3s ease-in-out 0.5s forwards; opacity: 0; } @keyframes checkmark { 0% { opacity: 0; transform: rotate(45deg) translate(-2px, -2px) scale(0.8); } 100% { opacity: 1; transform: rotate(45deg) translate(-2px, -2px) scale(1); } } .success-message { font-size: 18px; color: var(--text); margin-bottom: 8px; font-weight: 500; } .success-details { font-size: 14px; color: #666; text-align: center; margin-bottom: 20px; } .done-btn { padding: 8px 20px; background-color: var(--primary); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; transition: var(--transition); } .done-btn:hover { background-color: var(--primary-light); transform: translateY(-1px); } .upload-progress, .upload-complete { width: 100%; display: flex; flex-direction: column; align-items: center; } @media (max-width: 480px) { .upload-container { padding: 20px; } .upload-area { padding: 30px 15px; } .file-name { max-width: 150px; } } </style> </head> <body> <div class="upload-container"> <div class="upload-header"> <h2>Project Assets Upload</h2> <p>Upload design files, documents, or media assets to share with your team.</p> </div> <div class="upload-area" id="upload-area"> <div class="upload-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" viewBox="0 0 16 16"> <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/> <path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/> </svg> </div> <h3>Drag files here or click to browse</h3> <p>Support for JPG, PNG, PDF, SVG, AI, DOC, MP4 (max 25MB)</p> <span class="browse-btn">Browse Files</span> <input type="file" id="file-input" multiple> </div> <div class="files-list" id="files-list"></div> <div class="upload-mask" id="upload-mask"> <div class="upload-progress" id="upload-progress"> <div class="progress-pulsate"></div> <div class="progress-container"> <div class="progress-bar"> <div class="progress-fill" id="progress-fill"> <div class="progress-glow"></div> </div> </div> <div class="progress-details"> <span id="progress-percentage">0%</span> <span id="progress-speed">0 KB/s</span> </div> </div> <div class="progress-status" id="progress-status">Uploading 1 of 3 files...</div> <button class="cancel-upload" id="cancel-upload">Cancel</button> </div> <div class="upload-complete" id="upload-complete"> <div class="success-icon"> <div class="checkmark"></div> </div> <div class="success-message">Upload Complete!</div> <div class="success-details">All your files have been successfully uploaded and are ready to use.</div> <button class="done-btn" id="done-btn">Done</button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const uploadArea = document.getElementById('upload-area'); const fileInput = document.getElementById('file-input'); const filesList = document.getElementById('files-list'); const uploadMask = document.getElementById('upload-mask'); const uploadProgress = document.getElementById('upload-progress'); const uploadComplete = document.getElementById('upload-complete'); const progressFill = document.getElementById('progress-fill'); const progressPercentage = document.getElementById('progress-percentage'); const progressStatus = document.getElementById('progress-status'); const progressSpeed = document.getElementById('progress-speed'); const cancelUpload = document.getElementById('cancel-upload'); const doneBtn = document.getElementById('done-btn'); let filesArray = []; let uploadCancelled = false; let progressGlow = document.querySelector('.progress-glow'); // Handle click to browse uploadArea.addEventListener('click', () => { fileInput.click(); }); // Handle drag events ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { uploadArea.addEventListener(eventName, preventDefaults, false); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } ['dragenter', 'dragover'].forEach(eventName => { uploadArea.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { uploadArea.addEventListener(eventName, unhighlight, false); }); function highlight() { uploadArea.classList.add('drag-over'); } function unhighlight() { uploadArea.classList.remove('drag-over'); } // Handle file drop uploadArea.addEventListener('drop', handleDrop, false); function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; handleFiles(files); } // Handle file selection via input fileInput.addEventListener('change', function() { handleFiles(this.files); }); function handleFiles(files) { if (files.length > 0) { Array.from(files).forEach(file => { // Only add if the file is not already in the list if (!filesArray.some(f => f.name === file.name && f.size === file.size)) { filesArray.push(file); displayFile(file); } }); } } function displayFile(file) { const fileItem = document.createElement('div'); fileItem.className = 'file-item'; // Get file extension const extension = file.name.split('.').pop().toLowerCase(); // Format file size const size = formatSize(file.size); fileItem.innerHTML = ` <div class="file-icon"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16"> <path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/> </svg> </div> <div class="file-details"> <div class="file-name">${file.name}</div> <div class="file-size">${size} β’ ${extension.toUpperCase()}</div> </div> <div class="file-actions"> <button class="remove-btn" data-name="${file.name}"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/> </svg> </button> </div> `; filesList.appendChild(fileItem); // Add event listener to remove button const removeBtn = fileItem.querySelector('.remove-btn'); removeBtn.addEventListener('click', function() { const fileName = this.getAttribute('data-name'); removeFile(fileName, fileItem); }); // If this is the first file added, initiate upload after a small delay if (filesArray.length === 1) { setTimeout(() => { simulateUpload(); }, 500); } } function removeFile(fileName, fileItem) { filesArray = filesArray.filter(file => file.name !== fileName); fileItem.remove(); } function formatSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } function simulateUpload() { if (filesArray.length === 0) return; uploadMask.classList.add('active'); uploadProgress.style.display = 'flex'; uploadComplete.classList.remove('active'); let progress = 0; let currentFileIndex = 0; const totalFiles = filesArray.length; progressStatus.textContent = `Uploading ${currentFileIndex + 1} of ${totalFiles} files...`; uploadCancelled = false; progressGlow.style.opacity = '1'; const interval = setInterval(() => { if (uploadCancelled) { clearInterval(interval); uploadMask.classList.remove('active'); progressGlow.style.opacity = '0'; return; } progress += 1; progressFill.style.width = `${progress}%`; progressPercentage.textContent = `${progress}%`; // Update speed randomly const speed = Math.floor(Math.random() * 900) + 100; progressSpeed.textContent = `${speed} KB/s`; if (progress >= 100) { clearInterval(interval); progressGlow.style.opacity = '0'; currentFileIndex++; if (currentFileIndex < totalFiles) { // Move to next file progress = 0; progressStatus.textContent = `Uploading ${currentFileIndex + 1} of ${totalFiles} files...`; progressFill.style.width = '0%'; progressPercentage.textContent = '0%'; progressGlow.style.opacity = '1'; setTimeout(() => { simulateFileUpload(interval, currentFileIndex, totalFiles); }, 500); } else { // All files uploaded setTimeout(() => { uploadProgress.style.display = 'none'; uploadComplete.classList.add('active'); }, 500); } } }, 50); cancelUpload.addEventListener('click', () => { uploadCancelled = true; }); doneBtn.addEventListener('click', () => { uploadMask.classList.remove('active'); // Clear files list and internal array filesList.innerHTML = ''; filesArray = []; }); } function simulateFileUpload(prevInterval, fileIndex, totalFiles) { if (uploadCancelled) { clearInterval(prevInterval); uploadMask.classList.remove('active'); return; } let progress = 0; progressStatus.textContent = `Uploading ${fileIndex + 1} of ${totalFiles} files...`; const interval = setInterval(() => { if (uploadCancelled) { clearInterval(interval); uploadMask.classList.remove('active'); return; } progress += 1; progressFill.style.width = `${progress}%`; progressPercentage.textContent = `${progress}%`; // Update speed randomly const speed = Math.floor(Math.random() * 900) + 100; progressSpeed.textContent = `${speed} KB/s`; if (progress >= 100) { clearInterval(interval); fileIndex++; if (fileIndex < totalFiles) { // Move to next file progress = 0; progressStatus.textContent = `Uploading ${fileIndex + 1} of ${totalFiles} files...`; progressFill.style.width = '0%'; progressPercentage.textContent = '0%'; setTimeout(() => { simulateFileUpload(interval, fileIndex, totalFiles); }, 500); } else { // All files uploaded setTimeout(() => { uploadProgress.style.display = 'none'; uploadComplete.classList.add('active'); }, 500); } } }, 50); } }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive Travel Map</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f7fa; overflow: hidden; color: #333; } .travel-map-container { position: relative; width: 100%; max-width: 700px; height: 700px; background-color: #fff; border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; flex-direction: column; } .map-header { padding: 24px 24px 12px; border-bottom: 1px solid #f0f0f0; z-index: 10; } .map-title { font-size: 22px; font-weight: 700; margin-bottom: 10px; color: #1a1a2c; } .map-subtitle { font-size: 14px; color: #667085; margin-bottom: 16px; line-height: 1.5; } .search-container { display: flex; align-items: center; background-color: #f9fafb; border-radius: 8px; padding: 12px 16px; margin-bottom: 12px; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05); } .search-icon { color: #667085; margin-right: 10px; } .search-input { flex: 1; border: none; background: transparent; font-size: 15px; color: #1a1a2c; outline: none; height: 24px; } .search-input::placeholder { color: #94a3b8; } .map-content { position: relative; flex: 1; overflow: hidden; } .world-map { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; transition: transform 0.5s ease-out; } .map-container { position: relative; width: 100%; height: 100%; overflow: hidden; } .map-svg { width: 100%; height: 100%; object-fit: cover; transition: filter 0.4s ease; filter: saturate(0.6) brightness(0.9); } .map-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); transition: background-color 0.4s ease; pointer-events: none; } .region { cursor: pointer; stroke: rgba(255, 255, 255, 0.3); stroke-width: 1; transition: all 0.3s ease; fill: #3056D3; fill-opacity: 0.2; } .region:hover { fill-opacity: 0.6; stroke: #fff; stroke-width: 2; } .active-region { fill: #3056D3; fill-opacity: 0.8; stroke: #fff; stroke-width: 2; filter: drop-shadow(0 0 8px rgba(48, 86, 211, 0.6)); } .region-label { position: absolute; padding: 8px 12px; background-color: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); font-size: 14px; font-weight: 600; color: #1a1a2c; transform: translate(-50%, -50%); z-index: 100; pointer-events: none; opacity: 0; transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .spotlight { position: absolute; width: 150px; height: 150px; border-radius: 50%; background: radial-gradient( circle, rgba(255, 255, 255, 0) 0%, rgba(48, 86, 211, 0.3) 70%, rgba(48, 86, 211, 0) 100% ); pointer-events: none; transform: translate(-50%, -50%); opacity: 0; z-index: 5; filter: blur(10px); mix-blend-mode: screen; } .destination-card { position: absolute; bottom: -100%; left: 50%; transform: translateX(-50%); width: 90%; background-color: white; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); padding: 20px; z-index: 20; transition: bottom 0.5s cubic-bezier(0.23, 1, 0.32, 1); } .destination-card.active { bottom: 20px; } .destination-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .destination-name { font-size: 18px; font-weight: 700; color: #1a1a2c; } .close-btn { background: none; border: none; color: #94a3b8; cursor: pointer; font-size: 18px; padding: 5px; } .destination-info { display: flex; gap: 12px; margin-bottom: 16px; } .destination-img { width: 80px; height: 80px; border-radius: 8px; object-fit: cover; } .destination-details { flex: 1; } .detail-row { display: flex; align-items: center; margin-bottom: 6px; font-size: 13px; color: #667085; } .detail-row i { margin-right: 8px; color: #3056D3; font-size: 14px; } .explore-btn { width: 100%; padding: 12px; background-color: #3056D3; color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 14px; cursor: pointer; transition: background-color 0.3s ease; } .explore-btn:hover { background-color: #2a4bba; } .legend { position: absolute; top: 20px; right: 20px; background-color: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); padding: 10px; z-index: 15; font-size: 12px; } .legend-item { display: flex; align-items: center; margin-bottom: 6px; } .legend-color { width: 12px; height: 12px; border-radius: 3px; margin-right: 8px; } .legend-active { background-color: rgba(48, 86, 211, 0.8); } .legend-available { background-color: rgba(48, 86, 211, 0.2); } .legend-unavailable { background-color: rgba(0, 0, 0, 0.2); } .zoom-controls { position: absolute; bottom: 20px; right: 20px; display: flex; flex-direction: column; gap: 5px; z-index: 15; } .zoom-btn { width: 36px; height: 36px; background-color: white; border: 1px solid #e5e7eb; border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 18px; color: #667085; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); transition: all 0.2s ease; } .zoom-btn:hover { background-color: #f9fafb; color: #3056D3; } @keyframes pulse { 0% { transform: scale(1); opacity: 0.6; } 50% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); opacity: 0.6; } } .pulse { animation: pulse 2s infinite ease-in-out; } /* Responsive adjustments */ @media (max-width: 600px) { .map-title { font-size: 18px; } .map-subtitle { font-size: 13px; } .destination-card { padding: 15px; } .destination-img { width: 60px; height: 60px; } .destination-name { font-size: 16px; } .legend { top: 10px; right: 10px; font-size: 10px; padding: 8px; } .legend-color { width: 8px; height: 8px; } .zoom-controls { bottom: 10px; right: 10px; } .zoom-btn { width: 30px; height: 30px; font-size: 14px; } } /* For smaller heights */ @media (max-height: 700px) { .map-header { padding: 12px 24px 8px; } .map-title { margin-bottom: 5px; } .map-subtitle { margin-bottom: 8px; } .search-container { padding: 8px 16px; margin-bottom: 8px; } } /* FontAwesome-style icons */ .icon { display: inline-block; width: 1em; height: 1em; fill: currentColor; } </style> </head> <body> <div class="travel-map-container"> <div class="map-header"> <h1 class="map-title">Travel Spotlight Explorer</h1> <p class="map-subtitle">Discover travel possibilities with our interactive map. Click on regions to see detailed information and travel options.</p> <div class="search-container"> <svg class="search-icon icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/> </svg> <input type="text" class="search-input" placeholder="Search destinations or regions..."> </div> </div> <div class="map-content"> <div class="map-container"> <svg class="map-svg" viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg"> <!-- North America --> <path id="north-america" class="region" d="M170,120 L120,170 L150,210 L200,230 L210,270 L230,280 L240,260 L270,240 L300,220 L330,240 L350,230 L330,190 L290,160 L280,120 L250,110 L220,120 L190,110 Z"> </path> <!-- South America --> <path id="south-america" class="region" d="M230,290 L220,320 L240,350 L260,380 L280,390 L300,370 L280,350 L290,330 L280,310 L260,300 L240,280 Z"> </path> <!-- Europe --> <path id="europe" class="region" d="M420,120 L400,140 L380,130 L370,150 L380,170 L400,180 L420,170 L440,180 L460,160 L450,140 L430,130 Z"> </path> <!-- Africa --> <path id="africa" class="region" d="M420,210 L400,230 L390,260 L400,290 L420,320 L440,330 L460,310 L470,280 L460,250 L450,220 L430,200 Z"> </path> <!-- Asia --> <path id="asia" class="region" d="M470,120 L500,130 L540,120 L580,130 L600,150 L620,170 L600,190 L580,210 L550,220 L530,210 L510,220 L490,210 L470,220 L450,200 L460,180 L480,170 L500,150 Z"> </path> <!-- Australia --> <path id="australia" class="region" d="M650,300 L630,320 L620,350 L640,370 L670,360 L690,340 L680,310 L660,290 Z"> </path> <!-- Antarctica --> <path id="antarctica" class="region" d="M350,430 L450,410 L550,430 L500,450 L400,450 Z"> </path> <!-- Other islands --> <circle id="greenland" class="region" cx="350" cy="80" r="25"></circle> <circle id="iceland" class="region" cx="380" cy="110" r="8"></circle> <circle id="uk" class="region" cx="390" cy="130" r="10"></circle> </svg> <div class="map-overlay"></div> <div class="spotlight"></div> </div> <div class="region-label"></div> <div class="destination-card"> <div class="destination-header"> <h3 class="destination-name">Southeast Asia</h3> <button class="close-btn">Γ</button> </div> <div class="destination-info"> <img class="destination-img" src="https://images.unsplash.com/photo-1552465011-b4e21bf6e79a?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80" alt="Destination"> <div class="destination-details"> <div class="detail-row"> <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"> <path d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/> </svg> 6 countries, 24+ destinations </div> <div class="detail-row"> <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"/> </svg> Best time: Nov - March </div> <div class="detail-row"> <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"> <path d="M312 24V34.5c6.4 1.2 12.6 2.7 18.6 4.6c10.9 3.4 22.6-2.3 26.1-13.2s-2.3-22.6-13.2-26.1C334.5 1.9 324 .8 313.3 .2C291.3-.8 268.7 5.5 250.8 18.1c-8.9 6.3-14.7 16.4-16.3 27.3c-1.6 10.8 2.5 21.8 11.2 28.8c9.4 7.6 18.7 15.9 27.7 24.6c4.4 4.3 9.6 6.3 15.1 6.3c5.6 0 11.3-2.1 15.8-6.6c8-8.1 7.9-21.1-.1-29.1c-5.1-5-10.5-9.9-15.9-14.7c.4-.4 .9-.9 1.4-1.3c7.4-6.3 16.7-9.6 26.3-9.6c4.5 0 9 .7 13.5 2c1.7 .5 3.5 .8 5.3 .8c9.7 0 18.5-6.3 21.4-15.8c3.5-11.8-3.1-24.1-14.9-27.6zm-95.1 92.2c-16.7-3.6-31.5-12.5-43.2-25.2c-15.5-16.8-22.6-38.9-20-61.2c2.7-22.3 15.5-41.7 34.3-52.6C208.9-34.5 250-32.8 278.3-5.5c5 4.8 10.2 9.6 15.4 14.3c-3.8-1-7.7-1.5-11.7-1.5c-14.4 0-28.4 5.2-39.5 14.7c-2.2 1.9-4.3 3.8-6.2 6c-10.3-9.1-21.1-17.6-32.4-26.6c-16.3-13.1-40.2-1.2-43 21.9c-1.4 11.8-9.7 21.9-21 26c-12.5 4.5-26.2 1.7-35.7-7.3c-10.2-9.7-25.8-11.5-37.8-3.8c-13.9 8.9-21.8 25.5-19.8 42.3c2.3 18.8 17 33.6 36 36.1c15.1 2 30.6-.4 43.5-6.9c11.3-5.6 24.2-8.6 37.3-8.6h24c13.1 0 25.9 3 37.3 8.6c12.5 6.2 21.1 14 29.5 20.7c-20.3 18.2-32 44.2-32 71.3c0 10.1 1.6 20.1 4.8 29.6L176 311c-7.3 14.6-12.5 30-15.7 45.9l-6.1 30.7c-3.4 16.8 9.2 32.5 26.3 32.5H240V464c0 26.5 21.5 48 48 48c26.5 0 48-21.5 48-48V420.1c.7 .7 1.5 1.4 2.3 2.1c10.8 8.1 25.9 5.9 34-5s6-25.9-4.9-34C335.2 358 288 384 288 384v16h36.9l6.8-34.2c4.7-23.6 15.2-45.3 30.4-63.5l18-21.4c8.3-9.9 16.2-14.9 36.8-14.9c27.2 0 48-20.8 48-48s-20.8-48-48-48c-14.9 0-28.4 6.7-37.4 17.2c-12.5-10.2-26.2-18.2-40.9-24c-7.5-3-15.5-4.7-23.6-5.2V160h32c17.7 0 32-14.3 32-32s-14.3-32-32-32H288 256 216.8c-6.1 0-12.1 1.5-17.7 4.4c-11.4 5.9-17.4 10.2-24.9 16.2c-6.8 5.3-14.5 9.8-22.9 12.9c-5.3 2-10.8 3.4-16.4 4.1z"/> </svg> Cultural highlights, beaches, hiking </div> </div> </div> <button class="explore-btn">Explore Destinations</button> </div> <div class="legend"> <div class="legend-item"> <div class="legend-color legend-active"></div> <span>Selected Region</span> </div> <div class="legend-item"> <div class="legend-color legend-available"></div> <span>Available Regions</span> </div> <div class="legend-item"> <div class="legend-color legend-unavailable"></div> <span>Unavailable</span> </div> </div> <div class="zoom-controls"> <button class="zoom-btn zoom-in">+</button> <button class="zoom-btn zoom-out">β</button> <button class="zoom-btn zoom-reset"> <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M142.9 142.9c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2s12.5 14.8 22.2 14.8H463.5c0 0 0 0 0 0H472c13.3 0 24-10.7 24-24V72c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2L413.4 96.6c-87.6-86.5-228.7-86.2-315.8 1C73.2 122 55.6 150.7 44.8 181.4c-5.9 16.7 2.9 34.9 19.5 40.8s34.9-2.9 40.8-19.5c7.7-21.8 20.2-42.3 37.8-59.8zM16 312v7.6 .7V440c0 9.7 5.8 18.5 14.8 22.2s19.3 1.7 26.2-5.2l41.6-41.6c87.6 86.5 228.7 86.2 315.8-1c24.4-24.4 42.1-53.1 52.9-83.7c5.9-16.7-2.9-34.9-19.5-40.8s-34.9 2.9-40.8 19.5c-7.7 21.8-20.2 42.3-37.8 59.8c-62.2 62.2-162.7 62.5-225.3 1L185 329c6.9-6.9 8.9-17.2 5.2-26.2s-12.5-14.8-22.2-14.8H48.4h-.7H40c-13.3 0-24 10.7-24 24z"/> </svg> </button> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Elements const mapContainer = document.querySelector('.map-container'); const regions = document.querySelectorAll('.region'); const spotlight = document.querySelector('.spotlight'); const regionLabel = document.querySelector('.region-label'); const destinationCard = document.querySelector('.destination-card'); const closeBtn = document.querySelector('.close-btn'); const destinationName = document.querySelector('.destination-name'); const destinationImg = document.querySelector('.destination-img'); const destinationDetails = document.querySelectorAll('.detail-row'); const zoomIn = document.querySelector('.zoom-in'); const zoomOut = document.querySelector('.zoom-out'); const zoomReset = document.querySelector('.zoom-reset'); const mapOverlay = document.querySelector('.map-overlay'); const searchInput = document.querySelector('.search-input'); const exploreBtn = document.querySelector('.explore-btn'); // Variables let activeRegion = null; let scale = 1; let translateX = 0; let translateY = 0; let isDragging = false; let startX, startY; let startTranslateX = 0; let startTranslateY = 0; // Region data const regionData = { 'north-america': { name: 'North America', img: 'https://images.unsplash.com/photo-1501594907352-04cda38ebc29?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '8 countries, 52+ destinations', 'Best time: May - October', 'National parks, cities, mountains' ], position: { x: 200, y: 180 } }, 'south-america': { name: 'South America', img: 'https://images.unsplash.com/photo-1516306580123-e6e52b1b7b5f?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '13 countries, 34+ destinations', 'Best time: Oct - March (varies)', 'Amazon, Andes, cultural diversity' ], position: { x: 260, y: 330 } }, 'europe': { name: 'Europe', img: 'https://images.unsplash.com/photo-1467269204594-9661b134dd2b?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '44 countries, 100+ destinations', 'Best time: April - October', 'Historic cities, cuisine, art' ], position: { x: 420, y: 150 } }, 'africa': { name: 'Africa', img: 'https://images.unsplash.com/photo-1547471080-7cc2caa01a7e?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '54 countries, 60+ destinations', 'Best time: Varies by region', 'Safaris, deserts, cultural immersion' ], position: { x: 420, y: 260 } }, 'asia': { name: 'Asia', img: 'https://images.unsplash.com/photo-1493780474015-ba834fd0ce2f?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '48 countries, 80+ destinations', 'Best time: Oct - April (varies)', 'Ancient temples, cuisine, landscapes' ], position: { x: 550, y: 180 } }, 'australia': { name: 'Australia & Oceania', img: 'https://images.unsplash.com/photo-1523482580672-f109ba8cb9be?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '14 countries, 25+ destinations', 'Best time: May - November', 'Beaches, reefs, unique wildlife' ], position: { x: 660, y: 330 } }, 'antarctica': { name: 'Antarctica', img: 'https://images.unsplash.com/photo-1531594653956-2d0c3180a5cf?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ 'Expedition cruises only', 'Best time: Nov - March', 'Glaciers, wildlife, polar expeditions' ], position: { x: 450, y: 430 } }, 'greenland': { name: 'Greenland', img: 'https://images.unsplash.com/photo-1611273426858-450e7b08dd94?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '5 main regions, 15+ destinations', 'Best time: June - September', 'Northern lights, glaciers, hiking' ], position: { x: 350, y: 80 } }, 'iceland': { name: 'Iceland', img: 'https://images.unsplash.com/photo-1504829857797-ddff29c27927?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '8 regions, 20+ destinations', 'Best time: June - August', 'Volcanoes, hot springs, waterfalls' ], position: { x: 380, y: 110 } }, 'uk': { name: 'United Kingdom', img: 'https://images.unsplash.com/photo-1513635269975-59663e0ac1ad?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80', details: [ '4 countries, 30+ destinations', 'Best time: May - September', 'Historic sites, countryside, culture' ], position: { x: 390, y: 130 } } }; // Functions function updateTransform() { const worldMap = document.querySelector('.world-map'); if (!worldMap) return; worldMap.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`; } function showSpotlight(x, y) { spotlight.style.left = `${x}px`; spotlight.style.top = `${y}px`; spotlight.style.opacity = '1'; // Animate the spotlight spotlight.style.transition = 'opacity 0.3s ease'; spotlight.classList.add('pulse'); } function hideSpotlight() { spotlight.style.opacity = '0'; spotlight.classList.remove('pulse'); } function showRegionLabel(regionId, x, y) { if (!regionData[regionId]) return; regionLabel.textContent = regionData[regionId].name; regionLabel.style.left = `${x}px`; regionLabel.style.top = `${y - 30}px`; regionLabel.style.opacity = '1'; regionLabel.style.transform = 'translate(-50%, -100%)'; } function hideRegionLabel() { regionLabel.style.opacity = '0'; } function showDestinationCard(regionId) { if (!regionData[regionId]) return; const data = regionData[regionId]; destinationName.textContent = data.name; destinationImg.src = data.img; destinationImg.alt = data.name; // Update details data.details.forEach((detail, index) => { if (destinationDetails[index]) { const textNode = destinationDetails[index].childNodes[1]; if (textNode) { textNode.nodeValue = detail; } } }); destinationCard.classList.add('active'); } function hideDestinationCard() { destinationCard.classList.remove('active'); } function dimMap(exceptRegionId = null) { mapOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; regions.forEach(region => { if (region.id === exceptRegionId) { region.classList.add('active-region'); } else { region.classList.remove('active-region'); } }); } function resetMap() { mapOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; regions.forEach(region => { region.classList.remove('active-region'); }); } function highlightRegion(regionId) { dimMap(regionId); if (regionData[regionId] && regionData[regionId].position) { const pos = regionData[regionId].position; showSpotlight(pos.x, pos.y); } } // Event listeners regions.forEach(region => { region.addEventListener('mouseenter', function(e) { if (activeRegion !== region.id) { const rect = mapContainer.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; showRegionLabel(region.id, x, y); } }); region.addEventListener('mouseleave', function() { if (activeRegion !== region.id) { hideRegionLabel(); } }); region.addEventListener('click', function() { const regionId = region.id;
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Financial Dashboard Mask</title> <style> /* CSS Reset & Base Styles */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } :root { --bg-color: #0e1117; --mask-color: rgba(20, 22, 30, 0.8); --highlight-color: #6b8afd; --text-primary: #f5f5f7; --text-secondary: #9ca3af; --card-bg: rgba(35, 38, 45, 0.8); --card-hover: rgba(45, 49, 58, 0.9); --success-color: #34d399; --danger-color: #f87171; --neutral-color: #d8b4fe; --border-color: rgba(75, 85, 99, 0.3); } body { background-color: var(--bg-color); color: var(--text-primary); width: 100%; height: 100vh; overflow: hidden; display: flex; justify-content: center; align-items: center; padding: 10px; } /* Dashboard Container */ .dashboard-container { width: 100%; max-width: 680px; height: 680px; background-color: var(--bg-color); border-radius: 16px; position: relative; overflow: hidden; } /* Background Grid Pattern */ .bg-grid { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-size: 30px 30px; background-image: linear-gradient(to right, rgba(75, 85, 99, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(75, 85, 99, 0.1) 1px, transparent 1px); opacity: 0.3; z-index: 1; } /* Dashboard Mask */ .dashboard-mask { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--mask-color); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); z-index: 2; padding: 20px; display: flex; flex-direction: column; gap: 20px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--highlight-color) var(--bg-color); } .dashboard-mask::-webkit-scrollbar { width: 6px; } .dashboard-mask::-webkit-scrollbar-track { background: transparent; } .dashboard-mask::-webkit-scrollbar-thumb { background-color: rgba(107, 138, 253, 0.3); border-radius: 20px; } /* Dashboard Header */ .header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 1px solid var(--border-color); } .logo { font-weight: 700; font-size: 1.5rem; display: flex; align-items: center; } .logo-dot { display: inline-block; width: 8px; height: 8px; background-color: var(--highlight-color); border-radius: 50%; margin-left: 4px; margin-right: 2px; } .profile { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; } .profile-avatar { width: 36px; height: 36px; border-radius: 50%; background: linear-gradient(135deg, var(--highlight-color), #a78bfa); display: flex; justify-content: center; align-items: center; color: white; font-weight: 600; font-size: 0.9rem; } /* Dashboard Stats */ .stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; } .stat-card { background-color: var(--card-bg); border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; border: 1px solid transparent; cursor: pointer; } .stat-card:hover { transform: translateY(-4px); background-color: var(--card-hover); border-color: var(--border-color); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); } .stat-title { font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 8px; } .stat-value { font-size: 1.25rem; font-weight: 600; margin-bottom: 5px; } .stat-change { display: flex; align-items: center; font-size: 0.75rem; gap: 3px; } .up { color: var(--success-color); } .down { color: var(--danger-color); } .neutral { color: var(--neutral-color); } /* Chart Section */ .chart-section { display: grid; grid-template-columns: 1.5fr 1fr; gap: 15px; } .chart-container { background-color: var(--card-bg); border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); height: 250px; position: relative; } .chart-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .chart-title { font-weight: 600; font-size: 1rem; } .chart-actions { display: flex; gap: 8px; } .chart-btn { background-color: rgba(75, 85, 99, 0.2); border: none; color: var(--text-secondary); border-radius: 6px; padding: 6px 10px; font-size: 0.75rem; cursor: pointer; transition: all 0.2s ease; } .chart-btn:hover, .chart-btn.active { background-color: var(--highlight-color); color: white; } .chart-placeholder { width: 100%; height: 180px; position: relative; } /* Recent Activity */ .recent-activity { background-color: var(--card-bg); border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); } .activity-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .activity-list { display: flex; flex-direction: column; gap: 12px; max-height: 400px; overflow-y: auto; } .activity-item { padding: 10px; border-radius: 8px; background-color: rgba(35, 38, 45, 0.5); display: flex; align-items: center; gap: 12px; cursor: pointer; transition: all 0.2s ease; border-left: 3px solid transparent; } .activity-item:hover { background-color: rgba(45, 49, 58, 0.8); border-left-color: var(--highlight-color); } .activity-icon { width: 38px; height: 38px; border-radius: 50%; display: flex; justify-content: center; align-items: center; background-color: rgba(107, 138, 253, 0.2); color: var(--highlight-color); } .activity-details { flex: 1; } .activity-title { font-size: 0.9rem; font-weight: 500; } .activity-time { font-size: 0.75rem; color: var(--text-secondary); } .activity-value { font-weight: 600; font-size: 0.9rem; text-align: right; } /* Quick Actions Section */ .quick-actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; } .action-card { background-color: var(--card-bg); border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 10px; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; } .action-card::before { content: ''; position: absolute; top: -10px; left: -10px; right: -10px; bottom: -10px; background: linear-gradient(135deg, var(--highlight-color), transparent); z-index: -1; opacity: 0; transition: opacity 0.3s ease; border-radius: 18px; } .action-card:hover::before { opacity: 0.15; } .action-card:hover { transform: translateY(-4px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); } .action-icon { background-color: rgba(107, 138, 253, 0.15); width: 40px; height: 40px; border-radius: 10px; display: flex; justify-content: center; align-items: center; font-size: 1.2rem; color: var(--highlight-color); transition: all 0.3s ease; } .action-card:hover .action-icon { background-color: var(--highlight-color); color: white; transform: scale(1.1); } .action-title { font-size: 0.85rem; font-weight: 500; text-align: center; } /* Responsive Adjustments */ @media (max-width: 650px) { .stats-row, .quick-actions { grid-template-columns: repeat(2, 1fr); } .chart-section { grid-template-columns: 1fr; } .chart-container { height: 200px; } .chart-placeholder { height: 130px; } } @media (max-width: 480px) { .stats-row, .quick-actions { grid-template-columns: 1fr; } .header { flex-direction: column; align-items: flex-start; gap: 10px; } .profile { align-self: flex-end; } } /* Loading and pulsing animation */ @keyframes pulse { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } } .pulse { animation: pulse 2s infinite ease-in-out; } /* Tooltip style */ .tooltip { position: absolute; background-color: rgba(35, 38, 45, 0.95); color: white; padding: 8px 12px; border-radius: 6px; font-size: 0.8rem; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); pointer-events: none; z-index: 100; opacity: 0; transition: opacity 0.2s; max-width: 200px; white-space: nowrap; } </style> </head> <body> <div class="dashboard-container"> <div class="bg-grid"></div> <div class="dashboard-mask"> <div class="header"> <div class="logo"> FinTrack<span class="logo-dot"></span>io </div> <div class="profile"> <span>Alex Morgan</span> <div class="profile-avatar">AM</div> </div> </div> <div class="stats-row"> <div class="stat-card" data-tooltip="Current portfolio value across all accounts"> <div class="stat-title">Portfolio Value</div> <div class="stat-value">$143,721</div> <div class="stat-change up"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 19V5M12 5L5 12M12 5L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> 2.4% vs last week </div> </div> <div class="stat-card" data-tooltip="Total monthly recurring income"> <div class="stat-title">Monthly Income</div> <div class="stat-value">$8,459</div> <div class="stat-change up"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 19V5M12 5L5 12M12 5L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> 3.1% vs last month </div> </div> <div class="stat-card" data-tooltip="Your monthly spending across all accounts"> <div class="stat-title">Monthly Spending</div> <div class="stat-value">$5,842</div> <div class="stat-change down"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M12 5V19M12 19L5 12M12 19L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> 1.8% vs last month </div> </div> </div> <div class="chart-section"> <div class="chart-container"> <div class="chart-header"> <div class="chart-title">Portfolio Performance</div> <div class="chart-actions"> <button class="chart-btn active" data-period="1w">1W</button> <button class="chart-btn" data-period="1m">1M</button> <button class="chart-btn" data-period="3m">3M</button> <button class="chart-btn" data-period="1y">1Y</button> </div> </div> <div class="chart-placeholder" id="performance-chart"></div> </div> <div class="recent-activity"> <div class="activity-header"> <div class="chart-title">Recent Activity</div> </div> <div class="activity-list"> <div class="activity-item"> <div class="activity-icon"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 6L12 2L8 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 2V15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M22 11H18C16.8954 11 16 11.8954 16 13V22H22V11Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8 22H16V13C16 11.8954 15.1046 11 14 11H10C8.89543 11 8 11.8954 8 13V22Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M2 22H8V13C8 11.8954 7.10457 11 6 11H2V22Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="activity-details"> <div class="activity-title">AAPL Stock Purchase</div> <div class="activity-time">Today, 10:24 AM</div> </div> <div class="activity-value down">-$2,450</div> </div> <div class="activity-item"> <div class="activity-icon"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="2" y="6" width="20" height="12" rx="2" stroke="currentColor" stroke-width="2"/> <path d="M22 10H2" stroke="currentColor" stroke-width="2"/> <path d="M6 14H10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> </svg> </div> <div class="activity-details"> <div class="activity-title">Salary Deposit</div> <div class="activity-time">Yesterday, 9:30 AM</div> </div> <div class="activity-value up">+$4,250</div> </div> <div class="activity-item"> <div class="activity-icon"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/> <path d="M12 6V12L16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="activity-details"> <div class="activity-title">Recurring Investment</div> <div class="activity-time">Feb 22, 8:15 AM</div> </div> <div class="activity-value down">-$500</div> </div> </div> </div> </div> <div class="quick-actions"> <div class="action-card" data-tooltip="Instantly transfer funds between accounts"> <div class="action-icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 1L21 5L17 9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M3 11V9C3 7.89543 3.89543 7 5 7H21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 23L3 19L7 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M21 13V15C21 16.1046 20.1046 17 19 17H3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="action-title">Quick Transfer</div> </div> <div class="action-card" data-tooltip="Create a scheduled payment or transaction"> <div class="action-icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="4" y="5" width="16" height="16" rx="2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M16 3V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M8 3V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4 11H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M10 16H14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="action-title">Schedule Payment</div> </div> <div class="action-card" data-tooltip="View investment opportunities based on your profile"> <div class="action-icon"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M21 15V19C21 20.1046 20.1046 21 19 21H5C3.89543 21 3 20.1046 3 19V15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 10L12 15L17 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M12 15V3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div class="action-title">Investment Opportunities</div> </div> </div> </div> </div> <div class="tooltip" id="tooltip"></div> <script> document.addEventListener('DOMContentLoaded', function() { // Chart initialization const chartElement = document.getElementById('performance-chart'); const chartData = { '1w': createRandomData(7), '1m': createRandomData(30), '3m': createRandomData(90), '1y': createRandomData(365) }; let currentPeriod = '1w'; drawChart(chartElement, chartData[currentPeriod]); // Period selector handling const periodButtons = document.querySelectorAll('.chart-btn'); periodButtons.forEach(btn => { btn.addEventListener('click', function() { periodButtons.forEach(b => b.classList.remove('active')); this.classList.add('active'); currentPeriod = this.getAttribute('data-period'); drawChart(chartElement, chartData[currentPeriod]); }); }); // Tooltip functionality const tooltip = document.getElementById('tooltip'); const tooltipElements = document.querySelectorAll('[data-tooltip]'); tooltipElements.forEach(el => { el.addEventListener('mouseenter', function(e) { const tooltipText = this.getAttribute('data-tooltip'); tooltip.textContent = tooltipText; tooltip.style.opacity = 1; const rect = this.getBoundingClientRect(); const tooltipRect = tooltip.getBoundingClientRect(); let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2); let top = rect.top - tooltipRect.height - 10; // Keep tooltip within viewport if (left < 10) left = 10; if (left + tooltipRect.width > window.innerWidth - 10) left = window.innerWidth - tooltipRect.width - 10; // If tooltip would go above viewport, show it below element if (top < 10) { top = rect.bottom + 10; } tooltip.style.left = `${left}px`; tooltip.style.top = `${top}px`; }); el.addEventListener('mouseleave', function() { tooltip.style.opacity = 0; }); }); // Handle mouse movement to reposition tooltip document.addEventListener('mousemove', function(e) { const activeTooltip = document.querySelector(':hover[data-tooltip]'); if (activeTooltip) { const tooltipText = activeTooltip.getAttribute('data-tooltip'); tooltip.textContent = tooltipText; const rect = activeTooltip.getBoundingClientRect(); const tooltipRect = tooltip.getBoundingClientRect(); let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2); let top = rect.top - tooltipRect.height - 10; // Keep tooltip within viewport if (left < 10) left = 10; if (left + tooltipRect.width > window.innerWidth - 10) left = window.innerWidth - tooltipRect.width - 10; // If tooltip would go above viewport, show it below element if (top < 10) { top = rect.bottom + 10; } tooltip.style.left = `${left}px`; tooltip.style.top = `${top}px`; } }); // Add ripple effect to action cards const actionCards = document.querySelectorAll('.action-card'); actionCards.forEach(card => { card.addEventListener('click', function(e) { const ripple = document.createElement('div'); ripple.className = 'ripple'; ripple.style.position = 'absolute'; ripple.style.borderRadius = '50%'; ripple.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'; ripple.style.pointerEvents = 'none'; const rect = this.getBoundingClientRect(); const size = Math.max(rect.width, rect.height) * 2; ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${e.clientX - rect.left - size/2}px`; ripple.style.top = `${e.clientY - rect.top - size/2}px`; ripple.style.transform = 'scale(0)'; ripple.style.transition = 'transform 0.6s, opacity 0.6s'; this.appendChild(ripple); setTimeout(() => { ripple.style.transform = 'scale(1)'; ripple.style.opacity = '0'; }, 10); setTimeout(() => { ripple.remove(); }, 600); }); }); }); // Function to create random data points function createRandomData(points) { const data = []; let baseValue = 130 + Math.random() * 20; for (let i = 0; i < points; i++) { baseValue += (Math.random() - 0.5) * 5; data.push({ x: i, y: baseValue }); } return data; } // Function to draw chart function drawChart(element, data) { // Clear previous chart element.innerHTML = ''; // Calculate min and max for scaling const values = data.map(point => point.y); const min = Math.min(...values) * 0.9; const max = Math.max(...values) * 1.1; const range = max - min; // Create SVG element const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '100%'); svg.setAttribute('height', '100%'); svg.setAttribute('viewBox', `0 0 ${data.length - 1} 100`); svg.style.overflow = 'visible'; // Create path for the line const pathLine = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // Create path for the area below the line const pathArea = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // Generate path data let linePathData = ''; let areaPathData = ''; data.forEach((point, index) => { // Normalize y value to fit in viewBox const normalizedY = 100 - ((point.y - min) / range * 100); if (index === 0) { linePathData += `M${point.x},${normalizedY}`; areaPathData += `M${point.x},100 L${point.x},${normalizedY}`; } else { linePathData += ` L${point.x},${normalizedY}`; areaPathData += ` L${point.x},${normalizedY}`; } }); // Close the area path areaPathData += ` L${data[data.length-1].x},100 L${data[0].x},100 Z`; // Set path attributes pathLine.setAttribute('d', linePathData); pathLine.setAttribute('fill', 'none'); pathLine.setAttribute('stroke', '#6b8afd'); pathLine.setAttribute('stroke-width', '2'); pathLine.setAttribute('stroke-linecap', 'round'); pathLine.setAttribute('stroke-linejoin', 'round'); pathArea.setAttribute('d', areaPathData); pathArea.setAttribute('fill', 'url(#gradient)'); pathArea.setAttribute('opacity', '0.3'); // Create gradient const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient'); gradient.setAttribute('id', 'gradient'); gradient.setAttribute('x1', '0%'); gradient.setAttribute('y1', '0%'); gradient.setAttribute('x2', '0%'); gradient.setAttribute('y2', '100%'); const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); stop1.setAttribute('offset', '0%'); stop1.setAttribute('stop-color', '#6b8afd'); stop1.setAttribute('stop-opacity', '0.7'); const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); stop2.setAttribute('offset', '100%'); stop2.setAttribute('stop-color', '#6b8afd'); stop2.setAttribute('stop-opacity', '0.1'); gradient.appendChild(stop1); gradient.appendChild(stop2); defs.appendChild(gradient); // Append elements to SVG svg.appendChild(defs); svg.appendChild(pathArea); svg.appendChild(pathLine); // Add dots for data points data.forEach((point, index) => { // Only add dots for some points to avoid clutter if (index % Math.ceil(data.length / 10) === 0 || index === data.length - 1) { const normalizedY = 100 - ((point.y - min) / range * 100); const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', point.x); circle.setAttribute('cy', normalizedY); circle.setAttribute('r', '3'); circle.setAttribute('fill', '#6b8afd'); circle.setAttribute('stroke', '#151925'); circle.setAttribute('stroke-width', '1.5'); svg.appendChild(circle); } }); // Add hover line and tooltip const hoverLine = document.createElementNS('http://www.w3.org/2000/svg', 'line'); hoverLine.setAttribute('x1', '0'); hoverLine.setAttribute('y1', '0'); hoverLine.setAttribute('x2', '0'); hoverLine.setAttribute('y2', '100'); hoverLine.setAttribute('stroke', 'rgba(255,255,255,0.2)'); hoverLine.setAttribute('stroke-width', '1'); hoverLine.setAttribute('stroke-dasharray', '3,3'); hoverLine.style.display = 'none'; svg.appendChild(hoverLine); const tooltip = document.createElement('div'); tooltip.style.position = 'absolute'; tooltip.style.backgroundColor = 'rgba(35,38,45,0.95)'; tooltip.style.color = 'white'; tooltip.style.padding = '5px 8px'; tooltip.style.borderRadius = '4px'; tooltip.style.fontSize = '0.75rem'; tooltip.style.pointerEvents = 'none'; tooltip.style.display = 'none'; tooltip.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; tooltip.style.zIndex = '10'; element.appendChild(tooltip); // Add mouse move handler for interactive tooltip svg.addEventListener('mousemove', function(e) { const svgRect = svg.getBoundingClientRect(); const mouseX = e.clientX - svgRect.left; const xRatio = mouseX / svgRect.width; const dataIndex = Math.min( Math.floor(xRatio * data.length), data.length - 1 ); const point = data[dataIndex]; const normalizedY = 100 - ((point.y - min) / range * 100); hoverLine.setAttribute('x1', point.x); hoverLine.setAttribute('x2', point.x); hoverLine.style.display = 'block'; const value = point.y.toFixed(2); tooltip.textContent = `$${value}`; tooltip.style.display = 'block'; const tooltipX = (point.x / (data.length - 1)) * svgRect.width + svgRect.left; let tooltipY = (normalizedY / 100) * svgRect.height + svgRect.top - 30; // Ensure tooltip stays within window bounds const tooltipRect = tooltip.getBoundingClientRect(); if (tooltipX + tooltipRect.width > window.innerWidth) { tooltip.style.left = `${tooltipX - tooltipRect.width}px`; } else { tooltip.style.left = `${tooltipX}px`; } tooltip.style.top = `${tooltipY}px`; }); svg.addEventListener
<html> <head> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', sans-serif; } body { width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #f5f5f7; overflow: hidden; } .container { width: 700px; height: 700px; position: relative; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; border-radius: 24px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); background-color: white; } .product-view { position: relative; width: 100%; height: 80%; display: flex; justify-content: center; align-items: center; overflow: hidden; } .product-image-container { position: relative; width: 90%; height: 90%; display: flex; justify-content: center; align-items: center; } .product-image { width: 100%; height: 100%; object-fit: contain; transition: transform 0.3s ease; } .zoom-mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); opacity: 0; transition: opacity 0.3s ease; pointer-events: none; } .zoom-highlight { position: absolute; width: 150px; height: 150px; border-radius: 50%; background: radial-gradient( circle, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.7) 80% ); transform: translate(-50%, -50%); mix-blend-mode: overlay; pointer-events: none; opacity: 0; filter: blur(15px); } .magnifier { position: absolute; width: 120px; height: 120px; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50%; transform: translate(-50%, -50%) scale(0); transition: transform 0.2s ease; box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); pointer-events: none; } .magnifier::before { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(1px); } .info-panel { width: 100%; height: 20%; padding: 20px; background: #ffffff; display: flex; flex-direction: column; justify-content: space-between; border-top: 1px solid #f1f1f1; } .product-details { display: flex; justify-content: space-between; align-items: flex-start; } .product-title { font-size: 22px; font-weight: 700; color: #1a1a1a; max-width: 70%; } .product-price { font-size: 22px; font-weight: 600; color: #0071e3; } .product-description { font-size: 14px; color: #747474; margin-top: 6px; line-height: 1.4; } .zoom-instructions { display: flex; align-items: center; margin-top: 15px; padding: 8px 15px; background-color: #f2f2f2; border-radius: 20px; font-size: 13px; color: #555; width: fit-content; transition: background-color 0.3s ease; } .zoom-icon { margin-right: 8px; font-size: 16px; } .thumbnail-row { display: flex; gap: 10px; margin-top: 15px; } .thumbnail { width: 60px; height: 60px; border-radius: 10px; border: 2px solid transparent; overflow: hidden; cursor: pointer; transition: all 0.2s ease; } .thumbnail:hover { transform: translateY(-3px); } .thumbnail.active { border-color: #0071e3; } .thumbnail img { width: 100%; height: 100%; object-fit: cover; } @media (max-width: 700px) { .container { width: 100%; height: 100%; border-radius: 0; } .product-title { font-size: 20px; } .thumbnail { width: 50px; height: 50px; } } @media (max-width: 500px) { .product-view { height: 70%; } .info-panel { height: 30%; } } /* Animation keyframes */ @keyframes pulse { 0% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); opacity: 0.7; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } </style> </head> <body> <div class="container"> <div class="product-view"> <div class="product-image-container"> <img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" class="product-image" alt="Premium Athletic Shoes"> <div class="zoom-mask"></div> <div class="zoom-highlight"></div> <div class="magnifier"></div> </div> </div> <div class="info-panel"> <div> <div class="product-details"> <h2 class="product-title">UltraBoost Precision Runner</h2> <div class="product-price">$189.99</div> </div> <p class="product-description">Engineered with responsive cushioning and a breathable mesh upper for maximum comfort during high-intensity training.</p> <div class="zoom-instructions"> <span class="zoom-icon">π</span> <span>Hover over the image to activate high-detail zoom</span> </div> </div> <div class="thumbnail-row"> <div class="thumbnail active"> <img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Red Shoes"> </div> <div class="thumbnail"> <img src="https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=725&q=80" alt="Black Shoes"> </div> <div class="thumbnail"> <img src="https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=687&q=80" alt="White Shoes"> </div> <div class="thumbnail"> <img src="https://images.unsplash.com/photo-1560769629-975ec94e6a86?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=764&q=80" alt="Blue Shoes"> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const productView = document.querySelector('.product-view'); const productImage = document.querySelector('.product-image'); const zoomMask = document.querySelector('.zoom-mask'); const zoomHighlight = document.querySelector('.zoom-highlight'); const magnifier = document.querySelector('.magnifier'); const zoomInstructions = document.querySelector('.zoom-instructions'); const thumbnails = document.querySelectorAll('.thumbnail'); let isZooming = false; let originalImageWidth = 0; let originalImageHeight = 0; // Load high-resolution image in the background const highResImage = new Image(); highResImage.src = productImage.src; highResImage.onload = function() { originalImageWidth = this.width; originalImageHeight = this.height; }; // Initialize zoom effect productView.addEventListener('mouseenter', () => { zoomMask.style.opacity = '1'; zoomHighlight.style.opacity = '1'; magnifier.style.transform = 'translate(-50%, -50%) scale(1)'; isZooming = true; zoomInstructions.style.backgroundColor = '#e8f4ff'; }); productView.addEventListener('mouseleave', () => { zoomMask.style.opacity = '0'; zoomHighlight.style.opacity = '0'; magnifier.style.transform = 'translate(-50%, -50%) scale(0)'; productImage.style.transform = 'scale(1)'; isZooming = false; zoomInstructions.style.backgroundColor = '#f2f2f2'; }); productView.addEventListener('mousemove', (e) => { if (!isZooming) return; const rect = productView.getBoundingClientRect(); // Calculate relative position within the container const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Normalize to percentage const xPercent = x / rect.width; const yPercent = y / rect.height; // Update zoom highlight position zoomHighlight.style.left = `${x}px`; zoomHighlight.style.top = `${y}px`; // Update magnifier position magnifier.style.left = `${x}px`; magnifier.style.top = `${y}px`; // Create a radial mask that darkens everything except around the cursor zoomMask.style.background = `radial-gradient( circle 100px at ${x}px ${y}px, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 70% )`; // Scale and position the image for zoom effect const scale = 1.5; const translateX = (0.5 - xPercent) * 40; const translateY = (0.5 - yPercent) * 40; productImage.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`; }); // Thumbnail functionality thumbnails.forEach(thumbnail => { thumbnail.addEventListener('click', function() { // Remove active class from all thumbnails thumbnails.forEach(t => t.classList.remove('active')); // Add active class to clicked thumbnail this.classList.add('active'); // Update main image with fade effect productImage.style.opacity = '0'; setTimeout(() => { productImage.src = this.querySelector('img').src; productImage.style.opacity = '1'; // Update high-res image highResImage.src = this.querySelector('img').src; }, 200); }); }); // Add subtle animation to zoom instructions zoomInstructions.style.animation = 'pulse 3s infinite'; }); </script> </body> </html>
<html> <head> <style> /* Reset and Base Styles */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background-color: #f5f7fa; color: #2a2f45; height: 100vh; width: 100%; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; } .container { width: 100%; max-width: 700px; height: 700px; position: relative; overflow: hidden; background-color: #fff; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05); display: flex; flex-direction: column; } /* Header Styles */ header { padding: 1.5rem; background: linear-gradient(135deg, #5468ff, #8093ff); color: white; text-align: center; border-radius: 12px 12px 0 0; } header h1 { font-size: 1.8rem; margin-bottom: 0.5rem; font-weight: 700; } header p { font-size: 1rem; opacity: 0.95; max-width: 500px; margin: 0 auto; } /* Main Content */ .tutorial-content { flex: 1; position: relative; overflow: hidden; display: flex; flex-direction: column; } .tutorial-screen { flex: 1; background-color: #f8fafc; padding: 1.5rem; position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; } /* Demo Interface Elements */ .demo-interface { width: 100%; max-width: 550px; background: white; border-radius: 8px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); padding: 1rem; display: flex; flex-direction: column; gap: 1rem; position: relative; z-index: 5; } .demo-header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 0.75rem; border-bottom: 1px solid #eaeef3; } .demo-logo { font-weight: 700; font-size: 1.2rem; color: #5468ff; display: flex; align-items: center; gap: 0.5rem; } .demo-logo svg { width: 24px; height: 24px; } .demo-nav { display: flex; gap: 1rem; } .demo-nav-item { font-size: 0.9rem; cursor: pointer; opacity: 0.7; transition: opacity 0.2s ease; } .demo-nav-item:hover { opacity: 1; } .demo-section { padding: 0.75rem; border-radius: 6px; background: #f9fafb; display: flex; gap: 0.75rem; align-items: flex-start; } .demo-icon { width: 36px; height: 36px; background-color: #e7ebff; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #5468ff; font-size: 1.2rem; } .demo-text { flex: 1; } .demo-text h3 { font-size: 1rem; margin-bottom: 0.3rem; } .demo-text p { font-size: 0.85rem; color: #64748b; line-height: 1.4; } .demo-button { margin-top: 0.3rem; display: inline-block; padding: 0.4rem 0.75rem; background-color: #5468ff; color: white; border-radius: 4px; font-size: 0.8rem; font-weight: 500; cursor: pointer; transition: background-color 0.2s ease; } .demo-button:hover { background-color: #4355e8; } /* Spotlight Mask */ .spotlight-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; pointer-events: none; } .spotlight-mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.75); z-index: 10; transition: background-color 0.5s ease; } .spotlight { position: absolute; border-radius: 50%; box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.75); background-color: transparent; transform: translate(-50%, -50%); transition: width 0.5s ease, height 0.5s ease, top 0.75s ease, left 0.75s ease; z-index: 15; } .tooltip { position: absolute; background-color: white; border-radius: 8px; padding: 0.75rem 1rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); font-size: 0.9rem; color: #333; max-width: 220px; z-index: 20; opacity: 0; transform: translateY(10px); transition: opacity 0.3s ease, transform 0.3s ease; } .tooltip.active { opacity: 1; transform: translateY(0); } .tooltip:before { content: ''; position: absolute; width: 12px; height: 12px; background-color: white; transform: rotate(45deg); z-index: -1; } .tooltip.top:before { bottom: -6px; left: 50%; transform: translateX(-50%) rotate(45deg); } .tooltip.bottom:before { top: -6px; left: 50%; transform: translateX(-50%) rotate(45deg); } .tooltip.left:before { top: 50%; right: -6px; transform: translateY(-50%) rotate(45deg); } .tooltip.right:before { top: 50%; left: -6px; transform: translateY(-50%) rotate(45deg); } /* Controls */ .tutorial-controls { display: flex; justify-content: space-between; padding: 1rem 1.5rem; background-color: #f5f7fa; border-top: 1px solid #e5eaef; } .control-btn { padding: 0.6rem 1.2rem; border-radius: 6px; font-weight: 500; font-size: 0.9rem; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 0.5rem; border: none; outline: none; } .btn-back { background-color: #e5eaef; color: #4a5568; } .btn-back:hover { background-color: #d9e1ea; } .btn-back:disabled { opacity: 0.5; cursor: not-allowed; } .btn-next { background-color: #5468ff; color: white; } .btn-next:hover { background-color: #4355e8; } .step-indicator { display: flex; align-items: center; gap: 0.5rem; } .step-dot { width: 8px; height: 8px; border-radius: 50%; background-color: #cbd5e0; transition: all 0.3s ease; } .step-dot.active { background-color: #5468ff; transform: scale(1.2); } /* Progress Bar */ .progress-container { width: 100%; height: 4px; background-color: #e5eaef; position: relative; } .progress-bar { height: 100%; background: linear-gradient(90deg, #5468ff, #8093ff); width: 0%; transition: width 0.3s ease; border-radius: 0 2px 2px 0; } /* Animations */ @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .spotlight-pulse { animation: pulse 2s infinite ease-in-out; } /* Responsive adjustments */ @media (max-width: 600px) { header h1 { font-size: 1.5rem; } header p { font-size: 0.9rem; } .demo-interface { max-width: 95%; } .tooltip { max-width: 180px; font-size: 0.8rem; } } </style> </head> <body> <div class="container"> <header> <h1>Interactive Tutorial Spotlight</h1> <p>Navigate through the tutorial to learn how this spotlight feature works to focus user attention</p> </header> <div class="progress-container"> <div class="progress-bar" id="progressBar"></div> </div> <div class="tutorial-content"> <div class="tutorial-screen"> <div class="demo-interface" id="demoInterface"> <div class="demo-header" id="demoHeader"> <div class="demo-logo"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10"></circle> <path d="M12 8v4l2 2"></path> </svg> SpotLearn </div> <div class="demo-nav"> <div class="demo-nav-item" id="demoNav1">Home</div> <div class="demo-nav-item" id="demoNav2">Features</div> <div class="demo-nav-item" id="demoNav3">About</div> </div> </div> <div class="demo-section" id="demoSection1"> <div class="demo-icon">1</div> <div class="demo-text"> <h3>Capture User Focus</h3> <p>Use the spotlight to direct attention exactly where it's needed during onboarding or tutorials.</p> <div class="demo-button" id="demoButton1">Learn more</div> </div> </div> <div class="demo-section" id="demoSection2"> <div class="demo-icon">2</div> <div class="demo-text"> <h3>Smooth Transitions</h3> <p>The spotlight moves gracefully between focal points with a gentle fade effect at the edges.</p> <div class="demo-button" id="demoButton2">See examples</div> </div> </div> <div class="demo-section" id="demoSection3"> <div class="demo-icon">3</div> <div class="demo-text"> <h3>Contextual Tooltips</h3> <p>Add informative tooltips that appear alongside the spotlight to provide additional guidance.</p> <div class="demo-button" id="demoButton3">Try it now</div> </div> </div> </div> </div> <div class="spotlight-container"> <div class="spotlight-mask"></div> <div class="spotlight"></div> <div class="tooltip" id="tooltip"></div> </div> </div> <div class="tutorial-controls"> <button class="control-btn btn-back" id="backBtn" disabled> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M19 12H5M12 19l-7-7 7-7"></path> </svg> Back </button> <div class="step-indicator" id="stepIndicator"> <!-- Steps will be added dynamically --> </div> <button class="control-btn btn-next" id="nextBtn"> Next <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M5 12h14M12 5l7 7-7 7"></path> </svg> </button> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Tutorial steps definition const tutorialSteps = [ { elementId: 'demoHeader', tooltipText: 'Start by highlighting the navigation header to orient users', tooltipPosition: 'bottom', spotlightWidth: 500, spotlightHeight: 60 }, { elementId: 'demoNav2', tooltipText: 'Narrow the focus to specific navigation elements', tooltipPosition: 'bottom', spotlightWidth: 80, spotlightHeight: 30 }, { elementId: 'demoSection1', tooltipText: 'The spotlight can expand to highlight entire content sections', tooltipPosition: 'right', spotlightWidth: 500, spotlightHeight: 100 }, { elementId: 'demoButton1', tooltipText: 'Or focus on interactive elements users need to click', tooltipPosition: 'bottom', spotlightWidth: 100, spotlightHeight: 30, addClass: 'spotlight-pulse' }, { elementId: 'demoSection2', tooltipText: 'Transitions between focal points are smooth and intuitive', tooltipPosition: 'left', spotlightWidth: 500, spotlightHeight: 100 }, { elementId: 'demoSection3', tooltipText: 'The mask is semi-transparent, so users maintain context while focusing', tooltipPosition: 'top', spotlightWidth: 500, spotlightHeight: 100 } ]; // DOM Elements const spotlight = document.querySelector('.spotlight'); const tooltip = document.getElementById('tooltip'); const backBtn = document.getElementById('backBtn'); const nextBtn = document.getElementById('nextBtn'); const stepIndicator = document.getElementById('stepIndicator'); const progressBar = document.getElementById('progressBar'); let currentStep = 0; // Initialize step indicators function initStepIndicators() { tutorialSteps.forEach((_, index) => { const dot = document.createElement('div'); dot.className = `step-dot ${index === 0 ? 'active' : ''}`; stepIndicator.appendChild(dot); }); } // Update the spotlight and tooltip position function updateSpotlight(step) { const element = document.getElementById(step.elementId); if (!element) return; const rect = element.getBoundingClientRect(); const containerRect = document.querySelector('.tutorial-screen').getBoundingClientRect(); // Clear any previous animation class spotlight.className = 'spotlight'; // Calculate position relative to container const top = rect.top - containerRect.top + rect.height / 2; const left = rect.left - containerRect.left + rect.width / 2; // Apply spotlight dimensions and position spotlight.style.width = `${step.spotlightWidth}px`; spotlight.style.height = `${step.spotlightHeight}px`; spotlight.style.top = `${top}px`; spotlight.style.left = `${left}px`; // Add animation class if specified if (step.addClass) { spotlight.classList.add(step.addClass); } // Update tooltip position and content tooltip.textContent = step.tooltipText; tooltip.className = `tooltip ${step.tooltipPosition}`; // Position tooltip based on its direction let tooltipTop, tooltipLeft; switch(step.tooltipPosition) { case 'top': tooltipTop = top - (step.spotlightHeight / 2) - tooltip.offsetHeight - 15; tooltipLeft = left - (tooltip.offsetWidth / 2); break; case 'bottom': tooltipTop = top + (step.spotlightHeight / 2) + 15; tooltipLeft = left - (tooltip.offsetWidth / 2); break; case 'left': tooltipTop = top - (tooltip.offsetHeight / 2); tooltipLeft = left - (step.spotlightWidth / 2) - tooltip.offsetWidth - 15; break; case 'right': tooltipTop = top - (tooltip.offsetHeight / 2); tooltipLeft = left + (step.spotlightWidth / 2) + 15; break; } tooltip.style.top = `${tooltipTop}px`; tooltip.style.left = `${tooltipLeft}px`; // Activate tooltip with slight delay for transition effect setTimeout(() => { tooltip.classList.add('active'); }, 300); } // Move to the specified step function goToStep(stepIndex) { if (stepIndex < 0 || stepIndex >= tutorialSteps.length) return; currentStep = stepIndex; // Update buttons state backBtn.disabled = currentStep === 0; nextBtn.textContent = currentStep === tutorialSteps.length - 1 ? 'Finish' : 'Next'; // Update step indicators document.querySelectorAll('.step-dot').forEach((dot, i) => { dot.classList.toggle('active', i === currentStep); }); // Update progress bar const progress = ((currentStep + 1) / tutorialSteps.length) * 100; progressBar.style.width = `${progress}%`; // Reset and update tooltip tooltip.classList.remove('active'); // Update spotlight after a short delay setTimeout(() => { updateSpotlight(tutorialSteps[currentStep]); }, 200); } // Event Listeners backBtn.addEventListener('click', () => { goToStep(currentStep - 1); }); nextBtn.addEventListener('click', () => { if (currentStep === tutorialSteps.length - 1) { // End of tutorial - reset to start for demo purposes goToStep(0); } else { goToStep(currentStep + 1); } }); // Make the demo buttons interactive in the demo interface document.querySelectorAll('.demo-button').forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); // Add ripple effect for buttons when clicked const ripple = document.createElement('span'); ripple.style.position = 'absolute'; ripple.style.borderRadius = '50%'; ripple.style.backgroundColor = 'rgba(255, 255, 255, 0.6)'; ripple.style.transform = 'scale(0)'; ripple.style.animation = 'ripple 0.6s linear'; ripple.style.pointerEvents = 'none'; const rect = this.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${e.clientX - rect.left - size/2}px`; ripple.style.top = `${e.clientY - rect.top - size/2}px`; this.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); }); }); // Initialize the tutorial initStepIndicators(); goToStep(0); // Add ripple animation const style = document.createElement('style'); style.textContent = ` @keyframes ripple { to { transform: scale(4); opacity: 0; } } `; document.head.appendChild(style); // Ensure the tutorial resets properly if it's being viewed multiple times window.addEventListener('resize', () => { if (currentStep < tutorialSteps.length) { updateSpotlight(tutorialSteps[currentStep]); } }); }); </script> </body> </html>
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>StreamVeil - Live Event Overlay</title> <style> :root { --gradient-primary: linear-gradient(135deg, rgba(89, 120, 255, 0.6), rgba(203, 75, 255, 0.4)); --gradient-secondary: linear-gradient(45deg, rgba(65, 223, 207, 0.4), rgba(100, 84, 255, 0.3)); --accent-color: #8a42ff; --text-color: #ffffff; --notification-color: rgba(255, 255, 255, 0.95); --shadow-color: rgba(0, 0, 0, 0.25); --reaction-bg: rgba(22, 24, 35, 0.5); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } body { width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #121212; overflow: hidden; } .container { width: 700px; height: 700px; position: relative; display: flex; justify-content: center; align-items: center; overflow: hidden; } .video-container { width: 100%; height: 100%; position: relative; border-radius: 12px; overflow: hidden; box-shadow: 0 8px 32px var(--shadow-color); } .video-placeholder { width: 100%; height: 100%; object-fit: cover; display: block; } .gradient-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: var(--gradient-primary); opacity: 0.4; transition: opacity 0.5s ease; pointer-events: none; } .secondary-overlay { position: absolute; bottom: 0; left: 0; width: 100%; height: 40%; background: var(--gradient-secondary); opacity: 0.3; transform: translateY(20%); transition: all 0.5s ease; pointer-events: none; } .video-container:hover .gradient-overlay { opacity: 0.2; } .video-container:hover .secondary-overlay { transform: translateY(10%); opacity: 0.2; } .control-bar { position: absolute; bottom: 0; left: 0; width: 100%; padding: 16px; display: flex; justify-content: space-between; align-items: center; background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent); z-index: 10; transform: translateY(100%); transition: transform 0.3s ease; } .video-container:hover .control-bar { transform: translateY(0); } .control-button { background: rgba(255, 255, 255, 0.15); border: none; color: var(--text-color); width: 40px; height: 40px; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; backdrop-filter: blur(8px); } .control-button:hover { background: rgba(255, 255, 255, 0.25); transform: scale(1.1); } .control-button i { font-size: 18px; } .control-left, .control-right { display: flex; gap: 12px; } .live-indicator { position: absolute; top: 16px; left: 16px; background-color: rgba(255, 0, 0, 0.8); color: white; padding: 6px 12px; border-radius: 4px; font-weight: 600; font-size: 14px; letter-spacing: 0.5px; display: flex; align-items: center; gap: 6px; z-index: 10; } .live-dot { width: 8px; height: 8px; background-color: white; border-radius: 50%; animation: pulse 1.5s infinite; } .viewer-count { position: absolute; top: 16px; right: 16px; background-color: rgba(0, 0, 0, 0.5); color: white; padding: 6px 12px; border-radius: 4px; font-size: 14px; display: flex; align-items: center; gap: 6px; z-index: 10; backdrop-filter: blur(4px); } .viewer-count i { font-size: 16px; } .notification { position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%) translateY(100px); background-color: var(--notification-color); color: #101010; padding: 12px 16px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); z-index: 100; display: flex; align-items: center; gap: 10px; max-width: 80%; backdrop-filter: blur(8px); opacity: 0; transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.5s ease; } .notification.show { transform: translateX(-50%) translateY(0); opacity: 1; } .notification-icon { width: 36px; height: 36px; border-radius: 50%; background-color: var(--accent-color); display: flex; justify-content: center; align-items: center; color: white; font-size: 16px; } .notification-content { flex: 1; } .notification-title { font-weight: 600; margin-bottom: 2px; } .notification-message { font-size: 14px; opacity: 0.8; } .reactions-container { position: absolute; bottom: 80px; right: 16px; display: flex; flex-direction: column; gap: 12px; z-index: 10; } .reaction-button { width: 44px; height: 44px; border-radius: 50%; background: var(--reaction-bg); border: 1px solid rgba(255, 255, 255, 0.2); color: white; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: all 0.2s ease; backdrop-filter: blur(8px); } .reaction-button:hover { transform: scale(1.1); background: rgba(255, 255, 255, 0.15); } .reaction-count { position: absolute; top: -6px; right: -6px; background-color: var(--accent-color); color: white; font-size: 12px; font-weight: 600; width: 20px; height: 20px; border-radius: 50%; display: flex; justify-content: center; align-items: center; } .emoji-animation { position: absolute; animation: floatUp 3s ease-out forwards; z-index: 15; pointer-events: none; font-size: 24px; } .progress-bar { position: absolute; bottom: 0; left: 0; height: 3px; background-color: var(--accent-color); width: 0%; transition: width 0.1s linear; z-index: 20; } .event-title { position: absolute; top: 60px; left: 16px; color: white; font-size: 20px; font-weight: 700; z-index: 10; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); max-width: 70%; } .event-subtitle { position: absolute; top: 86px; left: 16px; color: rgba(255, 255, 255, 0.8); font-size: 14px; z-index: 10; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); max-width: 70%; } .interaction-hint { position: absolute; bottom: 130px; left: 50%; transform: translateX(-50%); background-color: rgba(255, 255, 255, 0.15); color: white; padding: 8px 16px; border-radius: 20px; font-size: 14px; backdrop-filter: blur(4px); display: flex; align-items: center; gap: 8px; opacity: 0; transition: opacity 0.3s ease; z-index: 10; } .video-container:hover .interaction-hint { opacity: 1; } .poll-container { position: absolute; top: 50%; left: 16px; transform: translateY(-50%); background: rgba(22, 24, 35, 0.75); border-radius: 12px; padding: 16px; width: 280px; color: white; backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.1); z-index: 20; display: none; } .poll-container.show { display: block; animation: slideIn 0.5s ease forwards; } .poll-header { margin-bottom: 12px; } .poll-title { font-weight: 600; margin-bottom: 4px; } .poll-description { font-size: 14px; opacity: 0.8; } .poll-options { display: flex; flex-direction: column; gap: 8px; } .poll-option { background: rgba(255, 255, 255, 0.1); border-radius: 6px; padding: 10px 12px; position: relative; cursor: pointer; transition: all 0.2s ease; overflow: hidden; } .poll-option:hover { background: rgba(255, 255, 255, 0.2); } .poll-option-fill { position: absolute; top: 0; left: 0; height: 100%; background: var(--accent-color); opacity: 0.3; z-index: -1; width: 0%; transition: width 1s ease; } .poll-option-text { display: flex; justify-content: space-between; } .poll-percent { font-weight: 600; } .poll-timer { display: flex; justify-content: space-between; margin-top: 12px; font-size: 12px; align-items: center; } .poll-timer-bar { flex: 1; height: 4px; background: rgba(255, 255, 255, 0.2); border-radius: 2px; margin: 0 8px; overflow: hidden; } .poll-timer-fill { height: 100%; background: var(--accent-color); width: 100%; animation: timerCount 30s linear forwards; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } @keyframes floatUp { 0% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 1; } 100% { transform: translate(var(--tx), -180px) scale(1.5) rotate(var(--rot)); opacity: 0; } } @keyframes slideIn { from { transform: translateY(-50%) translateX(-20px); opacity: 0; } to { transform: translateY(-50%) translateX(0); opacity: 1; } } @keyframes timerCount { 0% { width: 100%; } 100% { width: 0%; } } /* Font awesome icons */ .fas { display: inline-block; width: 1em; height: 1em; background-size: contain; background-repeat: no-repeat; background-position: center; } .fa-eye::before { content: "ποΈ"; } .fa-heart::before { content: "β€οΈ"; } .fa-thumbs-up::before { content: "π"; } .fa-star::before { content: "β"; } .fa-comment::before { content: "π¬"; } .fa-gift::before { content: "π"; } .fa-play::before { content: "βΆοΈ"; } .fa-pause::before { content: "βΈοΈ"; } .fa-volume-up::before { content: "π"; } .fa-expand::before { content: "β€’"; } .fa-user-plus::before { content: "π₯"; } .fa-poll::before { content: "π"; } @media (max-width: 700px) { .poll-container { width: 240px; } .event-title { font-size: 18px; } .event-subtitle { font-size: 12px; } .control-button { width: 36px; height: 36px; } .reaction-button { width: 40px; height: 40px; } } </style> </head> <body> <div class="container"> <div class="video-container"> <img class="video-placeholder" src="https://source.unsplash.com/random/700x700/?conference" alt="Live stream"> <div class="gradient-overlay"></div> <div class="secondary-overlay"></div> <div class="live-indicator"> <div class="live-dot"></div> LIVE </div> <div class="viewer-count"> <i class="fas fa-eye"></i> <span id="viewer-count">1,247</span> </div> <h1 class="event-title">TechConnect 2023: Future of AR/VR</h1> <p class="event-subtitle">Panel Discussion with Industry Leaders</p> <div class="control-bar"> <div class="control-left"> <button class="control-button" id="play-button"> <i class="fas fa-pause"></i> </button> <button class="control-button"> <i class="fas fa-volume-up"></i> </button> </div> <div class="control-right"> <button class="control-button" id="poll-button"> <i class="fas fa-poll"></i> </button> <button class="control-button"> <i class="fas fa-expand"></i> </button> </div> </div> <div class="progress-bar"></div> <div class="reactions-container"> <button class="reaction-button" data-emoji="π"> <i class="fas fa-thumbs-up"></i> <span class="reaction-count">0</span> </button> <button class="reaction-button" data-emoji="β€οΈ"> <i class="fas fa-heart"></i> <span class="reaction-count">0</span> </button> <button class="reaction-button" data-emoji="β"> <i class="fas fa-star"></i> <span class="reaction-count">0</span> </button> </div> <div class="interaction-hint"> <i class="fas fa-comment"></i> Ask a question or react to the stream </div> <div class="notification" id="notification"> <div class="notification-icon"> <i class="fas fa-gift"></i> </div> <div class="notification-content"> <div class="notification-title">Sarah joined the stream</div> <div class="notification-message">Welcome to TechConnect 2023!</div> </div> </div> <div class="poll-container" id="poll-container"> <div class="poll-header"> <div class="poll-title">What aspect of AR/VR excites you most?</div> <div class="poll-description">Share your thoughts with the panel</div> </div> <div class="poll-options"> <div class="poll-option" data-option="1"> <div class="poll-option-fill" style="width: 65%"></div> <div class="poll-option-text"> <span>Gaming & Entertainment</span> <span class="poll-percent">65%</span> </div> </div> <div class="poll-option" data-option="2"> <div class="poll-option-fill" style="width: 15%"></div> <div class="poll-option-text"> <span>Remote Work Solutions</span> <span class="poll-percent">15%</span> </div> </div> <div class="poll-option" data-option="3"> <div class="poll-option-fill" style="width: 12%"></div> <div class="poll-option-text"> <span>Healthcare Applications</span> <span class="poll-percent">12%</span> </div> </div> <div class="poll-option" data-option="4"> <div class="poll-option-fill" style="width: 8%"></div> <div class="poll-option-text"> <span>Educational Tools</span> <span class="poll-percent">8%</span> </div> </div> </div> <div class="poll-timer"> <span>Poll closing in</span> <div class="poll-timer-bar"> <div class="poll-timer-fill"></div> </div> <span id="poll-time">30s</span> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { const videoContainer = document.querySelector('.video-container'); const progressBar = document.querySelector('.progress-bar'); const playButton = document.getElementById('play-button'); const notification = document.getElementById('notification'); const reactionButtons = document.querySelectorAll('.reaction-button'); const pollButton = document.getElementById('poll-button'); const pollContainer = document.getElementById('poll-container'); const pollOptions = document.querySelectorAll('.poll-option'); const viewerCountElement = document.getElementById('viewer-count'); const pollTimeElement = document.getElementById('poll-time'); let isPlaying = true; let progress = 0; let pollActive = false; let pollTimer = 30; // Simulate stream progress const progressInterval = setInterval(() => { if (isPlaying) { progress += 0.1; if (progress > 100) progress = 0; progressBar.style.width = `${progress}%`; } }, 100); // Toggle play/pause playButton.addEventListener('click', function() { isPlaying = !isPlaying; if (isPlaying) { playButton.innerHTML = '<i class="fas fa-pause"></i>'; } else { playButton.innerHTML = '<i class="fas fa-play"></i>'; } }); // Show random notifications function showNotification() { const notifications = [ { title: "David just joined", message: "Welcome to the live discussion!", icon: "fa-user-plus" }, { title: "Q&A Session in 5 minutes", message: "Prepare your questions for the panel", icon: "fa-comment" }, { title: "New poll available", message: "Share your opinion with the speakers", icon: "fa-poll" }, { title: "Sarah sent a super reaction", message: "\"This discussion is mind-blowing!\"", icon: "fa-gift" } ]; const randomNotification = notifications[Math.floor(Math.random() * notifications.length)]; notification.querySelector('.notification-title').textContent = randomNotification.title; notification.querySelector('.notification-message').textContent = randomNotification.message; notification.querySelector('.notification-icon i').className = `fas ${randomNotification.icon}`; notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); }, 5000); } // Show initial notification after 2 seconds setTimeout(showNotification, 2000); // Show random notifications periodically setInterval(() => { if (Math.random() > 0.5) { showNotification(); } }, 15000); // Handle reactions reactionButtons.forEach(button => { button.addEventListener('click', function() { const emoji = this.dataset.emoji; const countElement = this.querySelector('.reaction-count'); let count = parseInt(countElement.textContent); countElement.textContent = ++count; // Create floating emoji animation const emojiElement = document.createElement('div'); emojiElement.className = 'emoji-animation'; emojiElement.textContent = emoji; // Random starting position near the button const rect = this.getBoundingClientRect(); const containerRect = videoContainer.getBoundingClientRect(); const startX = rect.left - containerRect.left + rect.width / 2; const startY = rect.top - containerRect.top; emojiElement.style.left = `${startX}px`; emojiElement.style.top = `${startY}px`; // Random trajectory emojiElement.style.setProperty('--tx', `${(Math.random() * 100 - 50)}px`); emojiElement.style.setProperty('--rot', `${(Math.random() * 40 - 20)}deg`); videoContainer.appendChild(emojiElement); // Remove after animation completes setTimeout(() => { emojiElement.remove(); }, 3000); }); }); // Toggle poll visibility pollButton.addEventListener('click', function() { pollActive = !pollActive; if (pollActive) { pollContainer.classList.add('show'); startPollTimer(); } else { pollContainer.classList.remove('show'); } }); // Handle poll options pollOptions.forEach(option => { option.addEventListener('click', function() { // Get current percentage const percentElement = this.querySelector('.poll-percent'); const fillElement = this.querySelector('.poll-option-fill'); let percent = parseInt(percentElement.textContent); // Increment by 1-3% (simulating a vote) const increment = Math.floor(Math.random() * 3) + 1; percent += increment; // Update display percentElement.textContent = `${percent}%`; fillElement.style.width = `${percent}%`; // Disable further clicks pollOptions.forEach(opt => { opt.style.pointerEvents = 'none'; opt.style.opacity = '0.7'; }); this.style.opacity = '1'; showNotification(); }); }); // Poll timer function startPollTimer() { pollTimer = 30; pollTimeElement.textContent = `${pollTimer}s`; const timerInterval = setInterval(() => { pollTimer--; pollTimeElement.textContent = `${pollTimer}s`; if (pollTimer <= 0) { clearInterval(timerInterval); pollContainer.classList.remove('show'); pollActive = false; } }, 1000); } // Simulate fluctuating viewer count setInterval(() => { const currentCount = parseInt(viewerCountElement.textContent.replace(',', '')); const change = Math.floor(Math.random() * 21) - 10; // -10 to +10 const newCount = Math.max(1000, currentCount + change); viewerCountElement.textContent = newCount.toLocaleString(); }, 5000); }); </script> </body> </html>