Untitled
unknown
javascript
2 months ago
5.3 kB
6
Indexable
/**
* Portfolio Parallax & Navigation Controller
* Handles: 7-item swapping, progress tracking, and side-nav visibility.
*/
//Enable Horizontal Parallax Scrolling
const parallaxSection = document.getElementById('parallax-section');
const projectItems = document.querySelectorAll('.project-item');
const sideNav = document.querySelector('.side-nav');
const dots = document.querySelectorAll('.nav-dot');
const totalItems = projectItems.length;
const handleParallaxScroll = () => {
const sectionRect = parallaxSection.getBoundingClientRect();
const viewportHeight = window.innerHeight;
// 1. Toggle Side-Nav Visibility
// Shows the dots only when the portfolio section is active in the viewport
if (sectionRect.top <= 0 && sectionRect.bottom >= viewportHeight / 2) {
sideNav.classList.add('show');
} else {
sideNav.classList.remove('show');
}
// 2. Calculate Overall Progress (0 to 1)
const totalScrollDist = sectionRect.height - viewportHeight;
const currentScrollPos = -sectionRect.top;
let overallProgress = currentScrollPos / totalScrollDist;
overallProgress = Math.max(0, Math.min(overallProgress, 1));
// 3. Update Each Project Item
const activeIndex = Math.round(overallProgress * (totalItems - 1));
projectItems.forEach((item, i) => {
// itemActiveProgress: 0 is perfectly centered
const itemActiveProgress = (overallProgress * (totalItems - 1)) - i;
// Determine visibility:
// We use isActive for the hard toggle.
// The "at the end" logic ensures the last item stays visible if we scroll past the section.
const isActive = (i === activeIndex);
// Parallax Transforms (Always active for smooth movement)
const xOffset = itemActiveProgress * -160;
const scale = 1 - (Math.abs(itemActiveProgress) * 0.05);
item.style.transform = `translateX(${xOffset}px) scale(${scale})`;
// Hard Visibility Toggle (No Fading)
if (isActive) {
item.style.opacity = 1;
item.style.pointerEvents = 'auto';
item.style.zIndex = 2;
item.style.visibility = 'visible';
} else {
item.style.opacity = 0;
item.style.pointerEvents = 'none';
item.style.zIndex = 1;
item.style.visibility = 'hidden'; // Completely hide non-active items
}
// Dot Navigation Update
if (dots[i]) {
dots[i].classList.toggle('active', isActive);
}
// Internal Image Parallax
const img = item.querySelector('.parallax-img');
if (img) {
// Keeps the image moving slightly even when the item is stationary
img.style.transform = `translateY(${itemActiveProgress * 40}px)`;
}
});
};
// --- Event Listeners ---
window.addEventListener('scroll', handleParallaxScroll);
dots.forEach((dot, index) => {
dot.addEventListener('click', () => {
const sectionTop = parallaxSection.offsetTop;
const totalHeight = parallaxSection.offsetHeight - window.innerHeight;
const targetScroll = sectionTop + (totalHeight * (index / (totalItems - 1)));
window.scrollTo({
top: targetScroll,
behavior: 'smooth'
});
});
});
handleParallaxScroll();
// SKILLS & EXPERTISE (Isometric Layers)
document.addEventListener('DOMContentLoaded', () => {
const layers = document.querySelectorAll('.iso-layer');
layers.forEach(layer => {
const img = layer.querySelector('img');
layer.addEventListener('mouseenter', () => {
if (!layer.classList.contains('is-active')) {
img.src = img.getAttribute('data-active');
}
});
layer.addEventListener('mouseleave', () => {
if (!layer.classList.contains('is-active')) {
img.src = img.getAttribute('data-default');
}
});
});
});
function handleLayerClick(event, clickedElement) {
event.stopPropagation();
const layers = document.querySelectorAll('.iso-layer');
layers.forEach(layer => {
const img = layer.querySelector('img');
if (layer === clickedElement) {
layer.classList.add('is-active');
img.src = img.getAttribute('data-active');
} else {
layer.classList.remove('is-active');
img.src = img.getAttribute('data-default');
}
});
}
window.addEventListener('click', () => {
document.querySelectorAll('.iso-layer').forEach(layer => {
const img = layer.querySelector('img');
layer.classList.remove('is-active');
img.src = img.getAttribute('data-default');
});
});
// TEXT PATH ANIMATION
const globalPath = document.getElementById('globalTextPath');
let globalOffset = 0;
function animateGlobalScroll() {
globalOffset -= 0.02;
if (globalOffset < -50) {
globalOffset = 0;
}
if(globalPath) globalPath.setAttribute('startOffset', globalOffset + '%');
requestAnimationFrame(animateGlobalScroll);
}
animateGlobalScroll();
Editor is loading...
Leave a Comment