1/** 2 * Rotating Glow Border Effect 3 * Creates smooth rotating glow borders similar to modern web designs 4 */ 5 6(function() { 7 'use strict'; 8 9 let animationId = null; 10 const glowElements = []; 11 12 /** 13 * Initialize rotating glow elements 14 */ 15 function initRotatingGlow() { 16 // Find all elements that need JS-controlled rotating glow 17 const elements = document.querySelectorAll('.js-rotating-glow'); 18 19 elements.forEach(element => { 20 // Create glow structure if it doesn't exist 21 let glowWrapper = element.querySelector('.glow-wrapper'); 22 if (!glowWrapper) { 23 glowWrapper = document.createElement('div'); 24 glowWrapper.className = 'glow-wrapper pointer-event-off'; 25 26 const glowItem = document.createElement('div'); 27 glowItem.className = 'glow-item-2'; 28 29 glowWrapper.appendChild(glowItem); 30 element.appendChild(glowWrapper); 31 32 // Wrap existing content 33 const content = Array.from(element.childNodes).filter(node => 34 node !== glowWrapper && node.nodeType === Node.ELEMENT_NODE 35 ); 36 37 if (content.length > 0 && !element.querySelector('.js-rotating-glow-content')) { 38 const contentWrapper = document.createElement('div'); 39 contentWrapper.className = 'js-rotating-glow-content'; 40 41 content.forEach(node => { 42 contentWrapper.appendChild(node); 43 }); 44 45 element.appendChild(contentWrapper); 46 } 47 } 48 49 // Store element data for animation 50 const glowItem = glowWrapper.querySelector('.glow-item-2'); 51 if (glowItem) { 52 glowElements.push({ 53 element: glowItem, 54 rotation: Math.random() * 360, // Start at random rotation 55 speed: parseFloat(element.dataset.glowSpeed || '1'), // Rotation speed multiplier 56 wrapper: glowWrapper 57 }); 58 } 59 }); 60 61 // Start animation if we have elements 62 if (glowElements.length > 0) { 63 startAnimation(); 64 } 65 } 66 67 /** 68 * Start the animation loop 69 */ 70 function startAnimation() { 71 if (animationId) return; // Already running 72 73 function animate() { 74 glowElements.forEach(item => { 75 // Increment rotation 76 item.rotation += item.speed * 0.8; // Adjust speed here 77 78 // Keep rotation within 0-360 79 if (item.rotation >= 360) { 80 item.rotation -= 360; 81 } 82 83 // Apply transform 84 item.element.style.transform = `translate3d(0px, 0px, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(${item.rotation}deg) skew(0deg, 0deg)`; 85 item.element.style.transformStyle = 'preserve-3d'; 86 item.element.style.willChange = 'transform'; 87 }); 88 89 animationId = requestAnimationFrame(animate); 90 } 91 92 animate(); 93 } 94 95 /** 96 * Stop the animation 97 */ 98 function stopAnimation() { 99 if (animationId) { 100 cancelAnimationFrame(animationId); 101 animationId = null; 102 } 103 } 104 105 /** 106 * Handle visibility change to pause/resume animation 107 */ 108 function handleVisibilityChange() { 109 if (document.hidden) { 110 stopAnimation(); 111 } else if (glowElements.length > 0) { 112 startAnimation(); 113 } 114 } 115 116 /** 117 * Add rotating glow to an element programmatically 118 */ 119 function addRotatingGlow(element, options = {}) { 120 if (!element) return; 121 122 element.classList.add('js-rotating-glow'); 123 124 if (options.speed) { 125 element.dataset.glowSpeed = options.speed; 126 } 127 128 // Re-initialize to pick up new element 129 initRotatingGlow(); 130 } 131 132 /** 133 * Remove rotating glow from an element 134 */ 135 function removeRotatingGlow(element) { 136 if (!element) return; 137 138 element.classList.remove('js-rotating-glow'); 139 const glowWrapper = element.querySelector('.glow-wrapper'); 140 if (glowWrapper) { 141 glowWrapper.remove(); 142 } 143 144 // Remove from animation array 145 for (let i = glowElements.length - 1; i >= 0; i--) { 146 if (glowElements[i].wrapper.parentElement === element) { 147 glowElements.splice(i, 1); 148 } 149 } 150 151 // Stop animation if no elements left 152 if (glowElements.length === 0) { 153 stopAnimation(); 154 } 155 } 156 157 /** 158 * Initialize when DOM is ready 159 */
160 function init() { 161 if (document.readyState === 'loading') { 162 document.addEventListener('DOMContentLoaded', init); 163 return; 164 } 165 166 // Initialize all rotating glow elements 167 initRotatingGlow(); 168 169 // Handle visibility changes for performance 170 document.addEventListener('visibilitychange', handleVisibilityChange); 171 172 // Handle reduced motion preference 173 if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 174 stopAnimation(); 175 } 176 177 // Debug info 178 if (window.location.hostname === 'localhost' || window.location.hostname.includes('.local')) { 179 console.log('Rotating Glow initialized:', glowElements.length, 'elements'); 180 } 181 } 182 183 // Expose public API 184 window.RotatingGlow = { 185 init: initRotatingGlow, 186 start: startAnimation, 187 stop: stopAnimation, 188 add: addRotatingGlow, 189 remove: removeRotatingGlow 190 }; 191 192 // Auto-initialize 193 init(); 194 195})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.