Created
June 18, 2025 15:47
-
-
Save nextab/ea6bba07125522092b900f2f27b5fe3c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
// Variables to track scroll state and rotation | |
let lastScrollTop = 0; | |
let scrollTimeout; | |
let rotationAngles = {}; // Store rotation angles for each SVG | |
const ROTATION_SPEED = 0.5; // Degrees per scroll event | |
let animatedSections = new Map(); // Track sections being animated | |
// Function to get or initialize rotation angle for an element | |
function getRotationAngle(element) { | |
const id = element.dataset.rotationId || | |
(element.dataset.rotationId = Math.random().toString(36).substring(2, 11)); | |
if (rotationAngles[id] === undefined) { | |
rotationAngles[id] = 0; | |
} | |
return rotationAngles[id]; | |
} | |
// Function to handle mobile navigation clicks | |
function handleMobileNavClick(element) { | |
// Since the "opened" class is added after the click, | |
// we need to wait for the next tick to check the class | |
setTimeout(() => { | |
// Find the parent mobile_nav element which gets the 'opened' class | |
const mobileNav = element.closest('.mobile_nav'); | |
if (mobileNav && mobileNav.classList.contains('opened')) { | |
document.body.classList.add('mobile_nav_opened'); | |
} else { | |
document.body.classList.remove('mobile_nav_opened'); | |
} | |
}, 100); | |
} | |
// Function to check mobile nav state | |
function checkMobileNavState() { | |
const mobileNav = document.querySelector('header.et-l .mobile_nav'); | |
if (mobileNav && mobileNav.classList.contains('opened')) { | |
document.body.classList.add('mobile_nav_opened'); | |
} else { | |
document.body.classList.remove('mobile_nav_opened'); | |
} | |
} | |
// Function to set rotation angle for an element | |
function setRotationAngle(element, angle) { | |
const id = element.dataset.rotationId; | |
if (id) { | |
rotationAngles[id] = angle; | |
} | |
} | |
// Function to apply rotation to an element | |
function applyRotation(element, angle) { | |
element.style.transform = `translatex(-50%) translatey(-50%) rotate(${angle}deg)`; | |
} | |
// Function to calculate scroll progress for an element | |
function calculateScrollProgress(element) { | |
const rect = element.getBoundingClientRect(); | |
const windowHeight = window.innerHeight || document.documentElement.clientHeight; | |
// Calculate when the bottom of the section is 20% from the bottom of the viewport | |
const startPoint = windowHeight * 0.8; | |
// Calculate when the bottom of the section is 20% from the top of the viewport | |
const endPoint = windowHeight * 0.2; | |
// If the section's bottom is above the start point, progress is 0 | |
if (rect.bottom > startPoint) { | |
return 0; | |
} | |
// If the section's bottom is above the end point, progress is 1 | |
if (rect.bottom < endPoint) { | |
return 1; | |
} | |
// Calculate progress between 0 and 1 | |
return 1 - ((rect.bottom - endPoint) / (startPoint - endPoint)); | |
} | |
// Function to update lawnmower and grass positions based on scroll progress | |
function updateLawnmowerPosition(section, progress) { | |
// Calculate positions based on progress (0 to 1) | |
// For lawnmower: right position from -60px to (100% + 60px) | |
const totalWidth = section.offsetWidth + 120; // section width + 60px on each side | |
const lawnmowerPosition = -60 + (totalWidth * progress); | |
// For grass: mask position from 0 to -500px | |
const grassMaskPosition = -500 * progress; | |
// Apply styles directly using CSS custom properties | |
section.style.setProperty('--lawnmower-position', `${lawnmowerPosition}px`); | |
section.style.setProperty('--grass-mask-position', `${grassMaskPosition}px`); | |
} | |
// Function to handle scroll events | |
function handleScroll() { | |
const scrollPosition = window.scrollY || document.documentElement.scrollTop; | |
// Add scrolling class to body | |
if (scrollPosition > 0) { | |
document.body.classList.add('scrolling'); | |
} else { | |
document.body.classList.remove('scrolling'); | |
} | |
// Determine scroll direction | |
const scrollingDown = scrollPosition > lastScrollTop; | |
lastScrollTop = scrollPosition; | |
// Update SVG rotation based on scroll direction | |
document.querySelectorAll('header.et-l .logo-container svg').forEach(element => { | |
// Get current rotation angle | |
let angle = getRotationAngle(element); | |
// Update angle based on scroll direction | |
if (scrollingDown) { | |
angle -= ROTATION_SPEED; // Counter-clockwise | |
} else { | |
angle += ROTATION_SPEED; // Clockwise | |
} | |
// Normalize angle to keep it between 0 and 360 | |
angle = angle % 360; | |
// Store updated angle | |
setRotationAngle(element, angle); | |
// Apply rotation | |
applyRotation(element, angle); | |
}); | |
// Check for grass sections in view and update their animation | |
updateGrassSectionsAnimation(); | |
// Clear the timeout if it's set | |
if (scrollTimeout) { | |
clearTimeout(scrollTimeout); | |
} | |
// Set a timeout to mark the end of scrolling | |
scrollTimeout = setTimeout(function() { | |
// When scrolling stops, we don't need to do anything special | |
// as the positions are already applied directly to the elements | |
}, 300); | |
} | |
// Function to check if an element is in the viewport | |
function isInViewport(element) { | |
const rect = element.getBoundingClientRect(); | |
const windowHeight = window.innerHeight || document.documentElement.clientHeight; | |
// Element is in view if its bottom is visible and not too close to the top | |
return ( | |
rect.bottom > 0 && | |
rect.bottom <= windowHeight | |
); | |
} | |
// Function to update grass sections animation based on scroll position | |
function updateGrassSectionsAnimation() { | |
// Get all sections with grass_bottom class and the last section | |
const grassSections = document.querySelectorAll('#main-content .et_pb_section.grass_bottom'); | |
// const lastSection = document.querySelector('#main-content .et_builder_inner_content > .et_pb_section:last-child'); | |
// Process grass_bottom sections | |
grassSections.forEach(section => { | |
if (isInViewport(section) && !section.classList.contains('mowed')) { | |
// Add to animated sections if not already there | |
if (!animatedSections.has(section)) { | |
section.classList.add('animating'); | |
animatedSections.set(section, true); | |
} | |
// Calculate and apply progress | |
const progress = calculateScrollProgress(section); | |
updateLawnmowerPosition(section, progress); | |
// Mark as mowed when animation completes | |
if (progress >= 1 && !section.classList.contains('mowed')) { | |
section.classList.remove('animating'); | |
section.classList.add('mowed'); | |
} | |
} | |
}); | |
} | |
// Initialize | |
document.addEventListener('DOMContentLoaded', function() { | |
// Initialize last scroll position | |
lastScrollTop = window.scrollY || document.documentElement.scrollTop; | |
// Add scroll event listener | |
window.addEventListener('scroll', handleScroll, { passive: true }); | |
// Initialize SVG elements | |
document.querySelectorAll('.logo-container svg').forEach(element => { | |
// Remove any existing transform to start fresh | |
element.style.transform = 'translatex(-50%) translatey(-50%) rotate(0deg)'; | |
// Initialize rotation tracking | |
getRotationAngle(element); | |
}); | |
// Initial check for grass sections animation | |
updateGrassSectionsAnimation(); | |
// Approach 1: Use event capturing phase (runs before bubbling phase) | |
document.addEventListener('click', function(event) { | |
const target = event.target; | |
if (target.classList && target.classList.contains('mobile_menu_bar')) { | |
console.log('Capture phase detected mobile_menu_bar click'); | |
handleMobileNavClick(target); | |
} | |
}, true); // true enables capture phase | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment