PageSourceSearch

https://eepymep.netlify.app/js/scroll-effects.js

js eepymep.netlify.app collected 2026-10-03 10:41:06 UTC 3,859 bytes, 103 lines download raw bytes

1/*
2 * scroll-effects.js — Parallax & Scroll Animation Controller
3 * ───────────────────────────────────────────────────────────
4 * Handles:
5 *   1. Parallax background (moves slower as you scroll)
6 *   2. Fade-in animations when sections enter viewport
7 *   3. Smooth momentum scrolling feel
8 */
9
10(function () {
11
12  // Check if user prefers reduced motion
13  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
14  if (prefersReducedMotion) return; // Exit if user prefers no animations
15
16  // ── 1. PARALLAX BACKGROUND ─────────────────────────────────
17  const background = document.querySelector('body::before') || document.body;
18  let ticking = false;
19
20  function updateParallax() {
21    const scrolled = window.pageYOffset;
22    const parallaxSpeed = 0.5; // Background moves at 50% speed
23    
24    // Apply parallax to the body's ::before pseudo-element via CSS variable
25    document.body.style.setProperty('--parallax-offset', scrolled * parallaxSpeed + 'px');
26    
27    ticking = false;
28  }
29
30  window.addEventListener('scroll', () => {
31    if (!ticking) {
32      window.requestAnimationFrame(updateParallax);
33      ticking = true;
34    }
35  });
36
37  // Update parallax position via transform on body
38  function applyParallax() {
39    const scrolled = window.pageYOffset;
40    const parallaxSpeed = 0.3; // Slower = more dramatic
41    document.body.style.backgroundPositionY = -(scrolled * parallaxSpeed) + 'px';
42  }
43
44  window.addEventListener('scroll', () => {
45    if (!ticking) {
46      window.requestAnimationFrame(applyParallax);
47      ticking = true;
48    }
49  });
50
51  // ── 2. SCROLL FADE-IN ANIMATIONS ───────────────────────────
52  // Use Intersection Observer to detect when elements enter viewport
53  const observerOptions = {
54    root: null,              // viewport
55    threshold: 0.1,          // trigger when 10% of element is visible
56    rootMargin: '0px 0px -100px 0px'  // trigger slightly before element enters
57  };
58
59  const observer = new IntersectionObserver((entries) => {
60    entries.forEach(entry => {
61      if (entry.isIntersecting) {
62        entry.target.classList.add('in-view');
63        // Optionally unobserve after animation to save resources
64        // observer.unobserve(entry.target);
65      }
66    });
67  }, observerOptions);
68
69  // Observe all sections and header
70  const elementsToAnimate = document.querySelectorAll('section, header');
71  elementsToAnimate.forEach(el => observer.observe(el));
72
73  // ── 3. SMOOTH MOMENTUM SCROLLING ───────────────────────────
74  // Add CSS property for smooth scrolling
75  // (Already handled in CSS via scroll-behavior: smooth)
76  
77  // Optional: Add custom easing for anchor links
78  document.querySelectorAll('a[href^="#"]').forEach(anchor => {
79    anchor.addEventListener('click', function (e) {
80      const targetId = this.getAttribute('href');
81      if (targetId === '#') return;
82      
83      const targetElement = document.querySelector(targetId);
84      if (targetElement) {
85        e.preventDefault();
86        targetElement.scrollIntoView({
87          behavior: 'smooth',
88          block: 'start'
89        });
90      }
91    });
92  });
93
94  // ── Initial Load: Show hero immediately ────────────────────
95  // Header should appear immediately without waiting for scroll
96  const header = document.querySelector('header');
97  if (header) {
98    setTimeout(() => {
99      header.classList.add('in-view');
100    }, 100);
101  }
102
103})();

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.