1/** 2 * IronWiFi Scroll Reveal â lightweight IntersectionObserver animations 3 * ~3KB, no dependencies, progressive enhancement 4 * Usage: add data-reveal="up|fade|stagger|steps" to elements 5 * add data-counter to number elements for count-up animation 6 */ 7(function() { 8 'use strict'; 9 10 // Respect reduced motion preference 11 if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; 12 13 // Add base hidden styles via JS (progressive enhancement: no JS = everything visible) 14 var style = document.createElement('style'); 15 style.textContent = [ 16 '[data-reveal]{opacity:0;transition:opacity 0.7s ease-out,transform 0.7s ease-out}', 17 '[data-reveal="up"]{transform:translateY(30px)}', 18 '[data-reveal="fade"]{transform:none}', 19 '[data-reveal].revealed{opacity:1!important;transform:translateY(0)!important}', 20 '[data-reveal="stagger"]>*{opacity:0;transform:translateY(20px);transition:opacity 0.5s ease-out,transform 0.5s ease-out}', 21 '[data-reveal="stagger"].revealed>*{opacity:1;transform:translateY(0)}', 22 '[data-reveal="stagger"].revealed>*:nth-child(1){transition-delay:0ms}', 23 '[data-reveal="stagger"].revealed>*:nth-child(2){transition-delay:100ms}', 24 '[data-reveal="stagger"].revealed>*:nth-child(3){transition-delay:200ms}', 25 '[data-reveal="stagger"].revealed>*:nth-child(4){transition-delay:300ms}', 26 '[data-reveal="stagger"].revealed>*:nth-child(5){transition-delay:400ms}', 27 '[data-reveal="stagger"].revealed>*:nth-child(6){transition-delay:500ms}', 28 '[data-reveal="stagger"].revealed>*:nth-child(7){transition-delay:600ms}', 29 '[data-reveal="stagger"].revealed>*:nth-child(8){transition-delay:700ms}', 30 '[data-reveal="steps"] .pp-step-number,[data-reveal="steps"] .pp-journey-icon{opacity:0;transform:scale(0.5);transition:opacity 0.4s ease-out,transform 0.4s ease-out}', 31 '[data-reveal="steps"].revealed .pp-step-number,[data-reveal="steps"].revealed .pp-journey-icon{opacity:1;transform:scale(1)}', 32 '[data-reveal="steps"].revealed *:nth-child(1) .pp-step-number,[data-reveal="steps"].revealed *:nth-child(1) .pp-journey-icon{transition-delay:0s}', 33 '[data-reveal="steps"].revealed *:nth-child(2) .pp-step-number,[data-reveal="steps"].revealed *:nth-child(2) .pp-journey-icon{transition-delay:0.3s}', 34 '[data-reveal="steps"].revealed *:nth-child(3) .pp-step-number,[data-reveal="steps"].revealed *:nth-child(3) .pp-journey-icon{transition-delay:0.6s}', 35 '[data-reveal="steps"].revealed *:nth-child(4) .pp-step-number,[data-reveal="steps"].revealed *:nth-child(4) .pp-journey-icon{transition-delay:0.9s}' 36 ].join('\n'); 37 document.head.appendChild(style); 38 39 // Scroll reveal observer 40 var revealObserver = new IntersectionObserver(function(entries) { 41 entries.forEach(function(entry) { 42 if (entry.isIntersecting) { 43 entry.target.classList.add('revealed'); 44 revealObserver.unobserve(entry.target); 45 } 46 }); 47 }, { threshold: 0.15, rootMargin: '0px 0px -40px 0px' }); 48 49 document.querySelectorAll('[data-reveal]').forEach(function(el) { 50 // Immediately reveal elements already in viewport (no animation for above-fold) 51 var rect = el.getBoundingClientRect(); 52 if (rect.top < window.innerHeight && rect.bottom > 0) { 53 el.classList.add('revealed'); 54 } else { 55 revealObserver.observe(el); 56 } 57 }); 58 59 // Counter animation 60 function animateCounter(el) { 61 var text = el.textContent.trim(); 62 var match = text.match(/^([<>]?)(\d+(?:\.\d+)?)(.*)/); 63 if (!match) return; 64 65 var prefix = match[1]; 66 var target = parseFloat(match[2]); 67 var suffix = match[3]; 68 var isFloat = match[2].indexOf('.') !== -1; 69 var decimals = isFloat ? match[2].split('.')[1].length : 0; 70 var duration = 1500; 71 var start = performance.now(); 72 73 function easeOutExpo(t) { 74 return t === 1 ? 1 : 1 - Math.pow(2, -10 * t); 75 } 76 77 function tick(now) { 78 var elapsed = now - start; 79 var progress = Math.min(elapsed / duration, 1); 80 var eased = easeOutExpo(progress); 81 var current = eased * target; 82 83 el.textContent = prefix + (isFloat ? current.toFixed(decimals) : Math.round(current)) + suffix; 84 85 if (progress < 1) { 86 requestAnimationFrame(tick); 87 } 88 } 89 90 requestAnimationFrame(tick); 91 } 92 93 var counterObserver = new IntersectionObserver(function(entries) {
94 entries.forEach(function(entry) { 95 if (entry.isIntersecting) { 96 animateCounter(entry.target); 97 counterObserver.unobserve(entry.target); 98 } 99 }); 100 }, { threshold: 0.5 }); 101 102 document.querySelectorAll('[data-counter]').forEach(function(el) { 103 counterObserver.observe(el); 104 }); 105})();
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.