document.addEventListener('DOMContentLoaded', () => { const buttons = document.querySelectorAll('.toggle-btn'); const wrapper = document.querySelector('.table-wrapper'); const columns = document.querySelectorAll('.column'); let isClickScrolling = false; let isInitialLoad = true; setTimeout(() => { isInitialLoad = false; }, 100); buttons.forEach((btn) => { btn.addEventListener('click', (e) => { e.preventDefault(); const currentTarget = btn.dataset.target; const targetId = currentTarget === 'challenges' ? 'solutions' : 'challenges'; const targetColumn = document.getElementById(targetId); if (targetColumn) { isClickScrolling = true; targetColumn.scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest', }); buttons.forEach((b) => { const active = b.dataset.target === targetId; b.classList.toggle('is-active', active); b.setAttribute('aria-selected', active); }); setTimeout(() => { isClickScrolling = false; }, 800); } }); }); const observer = new IntersectionObserver( (entries) => { if (isClickScrolling || isInitialLoad) return; entries.forEach((entry) => { if (entry.isIntersecting) { const id = entry.target.id; buttons.forEach((btn) => { const isActive = btn.dataset.target === id; btn.classList.toggle('is-active', isActive); btn.setAttribute('aria-selected', isActive); }); } }); }, { root: wrapper, threshold: 0.6, } ); columns.forEach((col) => observer.observe(col)); }); function equalHeights() { const leftItems = document.querySelectorAll('.itemc'); const rightItems = document.querySelectorAll('.itemr'); leftItems.forEach((leftItem, index) => { const rightItem = rightItems[index]; if (!rightItem) return; leftItem.style.height = 'auto'; rightItem.style.height = 'auto'; const maxHeight = Math.max(leftItem.offsetHeight, rightItem.offsetHeight); leftItem.style.height = `${maxHeight}px`; rightItem.style.height = `${maxHeight}px`; }); } window.addEventListener('load', equalHeights); window.addEventListener('resize', equalHeights); const stepsData = [ { number: '01', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '02', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '03', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '04', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '05', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '06', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '07', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '08', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, { number: '09', title: 'ایدهیابی و پیدا کردن مشکلات صفحه', intro: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', content: [ 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ', ], }, ]; const container = document.getElementById('stepsProcess'); const isMobile = window.innerWidth <= 768; const visibleSteps = isMobile ? stepsData.filter((step) => Number(step.number) <= 7) : stepsData; visibleSteps.forEach((step, i) => { const intro = step.intro || ''; const items = step.content || []; container.insertAdjacentHTML( 'beforeend', `
${intro}
` : ''}${slide.description}
`; textTrack.appendChild(item); }); } renderMedia(); renderTexts(); const mediaItems = document.querySelectorAll('.media-item'); const textItems = document.querySelectorAll('.text-item'); const totalSlides = slides.length; const showcaseScrollContainer = showcase.querySelector('.container-showcase'); document.documentElement.style.setProperty('--slides-count', slides.length); function setActiveSlide(current) { mediaItems.forEach((item, index) => { item.classList.toggle('active', index === current); }); textItems.forEach((item, index) => { item.classList.toggle('active', index === current); }); } function resetTextItemInlineStyles() { textItems.forEach((item) => { item.style.opacity = ''; item.style.filter = ''; }); } function updateScrollScene() { const isMobile = window.innerWidth <= 768; const rect = showcase.getBoundingClientRect(); const sectionTop = window.scrollY + rect.top; const scrollInside = window.scrollY - sectionTop; const scrollHeight = showcaseScrollContainer?.offsetHeight ?? showcase.offsetHeight; const maxScroll = Math.max(scrollHeight - window.innerHeight, 1); const progress = Math.min(Math.max(scrollInside / maxScroll, 0), 1); const exactIndex = progress * (totalSlides - 1); let current; if (isMobile) { current = Math.floor(exactIndex + 0.15); current = Math.min(Math.max(current, 0), totalSlides - 1); textTrack.style.transform = 'translateY(0)'; resetTextItemInlineStyles(); setActiveSlide(current); return; } current = Math.round(exactIndex); textTrack.style.transform = `translateY(-${exactIndex * 60}vh)`; textItems.forEach((item) => { item.classList.remove('active'); }); textItems.forEach((item, index) => { const offset = index - exactIndex; const absOffset = Math.abs(offset); let blur = 0; let opacity = 1; if (offset > 0) { blur = absOffset > 0.1 ? Math.min((absOffset - 0.1) * 25, 20) : 0; opacity = Math.max(1 - absOffset * 2.5, 0); } item.style.filter = `blur(${blur}px)`; item.style.opacity = opacity; }); setActiveSlide(current); } let currentSlideIndex = 0; let isAnimating = false; let lastScrollTime = 0; const scrollCooldown = 800; let touchStartY = 0; let touchStartX = 0; function goToSlide(index) { if (index < 0 || index >= totalSlides) return; currentSlideIndex = index; isAnimating = true; const progress = index / (totalSlides - 1); const exactIndex = index; const isMobile = window.innerWidth <= 768; if (isMobile) { textTrack.style.transform = 'translateY(0)'; resetTextItemInlineStyles(); setActiveSlide(currentSlideIndex); setTimeout(() => { isAnimating = false; }, 500); return; } textTrack.style.transform = `translateY(-${exactIndex * 60}vh)`; textItems.forEach((item) => { item.classList.remove('active'); }); textItems.forEach((item, idx) => { const offset = idx - exactIndex; const absOffset = Math.abs(offset); let blur = 0; let opacity = 1; if (offset > 0) { blur = absOffset > 0.1 ? Math.min((absOffset - 0.1) * 25, 20) : 0; opacity = Math.max(1 - absOffset * 2.5, 0); } item.style.filter = `blur(${blur}px)`; item.style.opacity = opacity; }); setActiveSlide(currentSlideIndex); setTimeout(() => { isAnimating = false; }, 600); } let isSectionPinned = false; // استفاده از Intersection Observer برای فهمیدن اینکه چه زمانی سکشن وارد دید کامل میشود const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { // زمانی که سکشن کاملاً یا بخش زیادی از آن در صفحه است isSectionPinned = entry.isIntersecting; }); }, { threshold: 0.1, // حساسیت ورود سکشن به صفحه } ); observer.observe(showcase); // showcase.addEventListener('wheel', function(e) { // const currentTime = new Date().getTime(); // const isAtEnd = currentSlideIndex === totalSlides - 1; // const isAtStart = currentSlideIndex === 0; // const rect = showcase.getBoundingClientRect(); // if (rect.top > 5 && e.deltaY > 0) { // return; // خارج شدن از تابع و اجازه به اسکرول عادی مرورگر برای بالا آوردن عکس // } // if (isAtStart && e.deltaY < 0 && rect.top >= 0) { // return; // } // if ((e.deltaY > 0 && !isAtEnd) || (e.deltaY < 0 && !isAtStart)) { // e.preventDefault(); // } // if (isAnimating || (currentTime - lastScrollTime < scrollCooldown)) { // return; // } // if (Math.abs(e.deltaY) < 10) return; // if (e.deltaY > 0) { // if (!isAtEnd) { // lastScrollTime = currentTime; // goToSlide(currentSlideIndex + 1); // } // } else { // if (!isAtStart) { // lastScrollTime = currentTime; // goToSlide(currentSlideIndex - 1); // } // } // }, { passive: false }); showcase.addEventListener( 'wheel', function (e) { const currentTime = new Date().getTime(); const isAtEnd = currentSlideIndex === totalSlides - 1; const isAtStart = currentSlideIndex === 0; // گرفتن موقعیت لحظهای سکشن اصلی const rect = showcase.getBoundingClientRect(); // ۱. بررسی حرکت از بالا به پایین: // اگر کاربر رو به پایین اسکرول میکند ولی سرِ سکشن هنوز به بالای صفحه (سقف) نرسیده است if (e.deltaY > 0 && rect.top > 5) { return; // اجازه بده صفحه اسکرول طبیعی شود تا سکشن به سقف بچسبد } // ۲. بررسی حرکت از پایین به بالا: // اگر کاربر رو به بالا اسکرول میکند ولی انتهای سکشن هنوز کاملاً بالا نیامده تا مماس شود // فرمول: rect.bottom باید با ارتفاع پنجره (window.innerHeight) یا نقطه پایانی مماس باشد if (e.deltaY < 0 && rect.bottom < window.innerHeight - 5) { return; // اجازه بده صفحه طبیعی بالا برود تا سکشن در موقعیت درست قرار بگیرد } // ۳. مدیریت خروج از حالت اسلایدر وقتی به ابتدا یا انتها میرسیم: // اگر در اسلاید اول هستیم و رو به بالا اسکرول میکنیم، بگذار صفحه بالا برود if (isAtStart && e.deltaY < 0 && rect.top >= -5) { return; } // اگر در اسلاید آخر هستیم و رو به پایین اسکرول میکنیم، بگذار صفحه پایین برود if (isAtEnd && e.deltaY > 0 && rect.bottom <= window.innerHeight + 5) { return; } // جلوگیری از اسکرول پیشفرض بدنه صفحه در طول انیمیشن اسلایدر if ((e.deltaY > 0 && !isAtEnd) || (e.deltaY < 0 && !isAtStart)) { e.preventDefault(); } if (isAnimating || currentTime - lastScrollTime < scrollCooldown) { return; } if (Math.abs(e.deltaY) < 10) return; if (e.deltaY > 0) { if (!isAtEnd) { lastScrollTime = currentTime; goToSlide(currentSlideIndex + 1); } } else { if (!isAtStart) { lastScrollTime = currentTime; goToSlide(currentSlideIndex - 1); } } }, { passive: false } ); showcase.addEventListener('touchstart', function(e) { touchStartY = e.touches[0].clientY; touchStartX = e.touches[0].clientX; }, { passive: true }); // showcase.addEventListener('touchmove', function(e) { // if (!touchStartY) return; // const currentTouchY = e.touches[0].clientY; // const currentTouchX = e.touches[0].clientX; // const diffY = touchStartY - currentTouchY; // const diffX = touchStartX - currentTouchX; // if (Math.abs(diffX) > Math.abs(diffY)) { // return; // } // const rect = showcase.getBoundingClientRect(); // if (rect.top > 5 && diffY > 0) { // return; // } // const isAtEnd = currentSlideIndex === totalSlides - 1; // const isAtStart = currentSlideIndex === 0; // if (isAtStart && diffY < 0 && rect.top >= 0) { // return; // } // if ((diffY > 0 && !isAtEnd) || (diffY < 0 && !isAtStart)) { // if (e.cancelable) e.preventDefault(); // } else { // return; // } // const currentTime = new Date().getTime(); // if (isAnimating || (currentTime - lastScrollTime < scrollCooldown)) { // return; // } // if (Math.abs(diffY) > 50) { // if (diffY > 0) { // if (!isAtEnd) { // lastScrollTime = currentTime; // goToSlide(currentSlideIndex + 1); // } // } else { // if (!isAtStart) { // lastScrollTime = currentTime; // goToSlide(currentSlideIndex - 1); // } // } // touchStartY = currentTouchY; // } // }, { passive: false }); showcase.addEventListener( 'touchmove', function (e) { if (!touchStartY) return; const currentTouchY = e.touches[0].clientY; const currentTouchX = e.touches[0].clientX; const diffY = touchStartY - currentTouchY; const diffX = touchStartX - currentTouchX; if (Math.abs(diffX) > Math.abs(diffY)) { return; } const rect = showcase.getBoundingClientRect(); // حرکت رو به پایین (انگشت به سمت بالا) و سکشن هنوز به سقف نرسیده if (diffY > 0 && rect.top > 5) { return; } // حرکت رو به بالا (انگشت به سمت پایین) و تهِ سکشن هنوز وارد کادر نشده if (diffY < 0 && rect.bottom < window.innerHeight - 5) { return; } const isAtEnd = currentSlideIndex === totalSlides - 1; const isAtStart = currentSlideIndex === 0; if (isAtStart && diffY < 0 && rect.top >= -5) { return; } if (isAtEnd && diffY > 0 && rect.bottom <= window.innerHeight + 5) { return; } if ((diffY > 0 && !isAtEnd) || (diffY < 0 && !isAtStart)) { if (e.cancelable) e.preventDefault(); } else { return; } const currentTime = new Date().getTime(); if (isAnimating || currentTime - lastScrollTime < scrollCooldown) { return; } if (Math.abs(diffY) > 50) { if (diffY > 0) { if (!isAtEnd) { lastScrollTime = currentTime; goToSlide(currentSlideIndex + 1); } } else { if (!isAtStart) { lastScrollTime = currentTime; goToSlide(currentSlideIndex - 1); } } touchStartY = currentTouchY; } }, { passive: false } ); showcase.addEventListener('touchend', function() { touchStartY = 0; }, { passive: true }); goToSlide(0); const portfolioNavConfig = { actionType: 'project', projectPreviewImage: './src/assets/images/gallery-thumbnail.webp', websiteUrl: 'https://example.com', }; const overlay = document.getElementById('portfolioOverlay'); const imagePopup = document.getElementById('portfolioOverlayimg'); const navLinksContainer = document.getElementById('portfolioNavLinks'); const navAction = document.getElementById('portfolioNavAction'); const navSections = [...document.querySelectorAll('section[data-nav-title]')].filter( (section) => section.dataset.nav !== 'false' && section.offsetParent !== null ); navSections.forEach((section) => { const id = section.id || section.className.split(' ')[0]; section.id = id; const btn = document.createElement('button'); btn.className = 'portfolio-nav-btn'; btn.textContent = section.dataset.navTitle; btn.dataset.target = id; btn.addEventListener('click', () => { document.getElementById(id).scrollIntoView({ behavior: 'smooth', block: 'start', }); }); navLinksContainer.appendChild(btn); }); const navButtons = document.querySelectorAll('.portfolio-nav-btn'); const sectionObserver = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const activeId = entry.target.id; navButtons.forEach((btn) => { btn.classList.remove('active'); if (btn.dataset.target === activeId) { btn.classList.add('active'); btn.scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest', }); } }); } }); }, { threshold: 0.45 } ); navSections.forEach((section) => sectionObserver.observe(section)); const portfolioNav = document.getElementById('portfolioNav'); document.querySelector('.portfolio-nav-meeting').addEventListener('click', function () { const target = document.querySelector(this.dataset.scrollTarget); if (target) { target.scrollIntoView({ behavior: 'smooth', }); } }); const aboutSection = document.querySelector('.about-section'); const showcaseSection = document.querySelector('.showcase-section'); const mediaSection = document.querySelector('.media-section'); function togglePortfolioNav() { const aboutTop = aboutSection.getBoundingClientRect().top; const showcaseRect = showcaseSection.getBoundingClientRect(); const mediaTop = mediaSection.getBoundingClientRect().top; const afterAbout = aboutTop <= window.innerHeight; const showcaseVisible = showcaseRect.top <= window.innerHeight && showcaseRect.bottom >= 0; const mediaVisible = mediaTop <= window.innerHeight; if (afterAbout) { portfolioNav.classList.add('show'); } else { portfolioNav.classList.remove('show'); } if (showcaseVisible && !mediaVisible) { portfolioNav.classList.remove('show'); } } window.addEventListener('scroll', togglePortfolioNav); window.addEventListener('load', togglePortfolioNav); function openPortfolioPopup() { overlay.classList.add('active'); document.body.style.overflow = 'hidden'; const sideWrapper = document.querySelector('.side-buttons-wrapper'); const portfolioWrapper = document.querySelector('.portfolio-logo-wrapper'); const desktopBtn = document.querySelector('.desktop-btn'); const phoneBtn = document.querySelector('.phone-btn'); const desktopUse = document.getElementById('desktopIcon'); const phoneUse = document.getElementById('phoneIcon'); sideWrapper?.classList.remove('rotated-desktop'); if (window.innerWidth <= 768) { handleMobilePopupView(); phoneBtn?.classList.add('active'); desktopBtn?.classList.remove('active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon-active'); } else { rotateOverlay?.classList.remove('show'); portfolioWrapper?.classList.remove('mobile-view'); sideWrapper?.classList.remove('rotated-desktop'); desktopBtn?.classList.add('active'); phoneBtn?.classList.remove('active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon-active'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon'); } } if (portfolioNavConfig.actionType === 'project') { navAction.innerHTML = ` `; document .querySelector('.portfolio-nav-project-btn') ?.addEventListener('click', openPortfolioPopup); } if (portfolioNavConfig.actionType === 'website') { navAction.innerHTML = ` بازدید از سایت `; } document.querySelectorAll('.close-popup').forEach((btn) => { btn.addEventListener('click', () => { const video = imagePopup.querySelector('.video-popup-player'); if (video) { video.pause(); video.removeAttribute('src'); video.load(); // video.src = ''; } overlay.classList.remove('active'); imagePopup.classList.remove('active'); document.body.style.overflow = ''; }); }); function openImagePopup(src) { // const img = imagePopup?.querySelector('img'); const img = imagePopup.querySelector('.img-popup-img'); const video = imagePopup.querySelector('.video-popup-player'); video.pause(); // video.src = ''; video.style.display = 'none'; img.style.display = 'block'; img.src = src; imagePopup?.classList.add('active'); document.body.style.overflow = 'hidden'; } imagePopup?.addEventListener('click', (e) => { const video = imagePopup.querySelector('.video-popup-player'); if (video) { video.pause(); video.src = ''; } }); function openVideoPopup(src, poster = '') { const img = imagePopup.querySelector('.img-popup-img'); const video = imagePopup.querySelector('.video-popup-player'); img.style.display = 'none'; video.style.display = 'block'; video.pause(); video.removeAttribute('poster'); if (poster) { video.poster = poster; } video.src = src; video.load(); imagePopup.classList.add('active'); document.body.style.overflow = 'hidden'; } document.addEventListener('DOMContentLoaded', () => { const mediaContainer = document.getElementById('media-section-container'); const section = document.querySelector('.media-section'); const mediaData = { type: 'image', src: './public/images/media.jpg', hideOnMobile: false, }; function handleMobileVisibility() { if (window.innerWidth <= 768 && mediaData.hideOnMobile) { section.style.display = 'none'; } else { section.style.display = 'block'; } } handleMobileVisibility(); window.addEventListener('resize', handleMobileVisibility); function renderMedia(data) { let element; if (data.type === 'image') { element = document.createElement('img'); element.src = data.src; element.alt = ''; } else if (data.type === 'gif') { element = document.createElement('img'); element.src = data.src; element.alt = ''; } else if (data.type === 'video') { element = document.createElement('video'); element.src = data.src; element.autoplay = true; element.muted = true; element.loop = true; element.playsInline = true; } element.classList.add('media'); mediaContainer.appendChild(element); } renderMedia(mediaData); }); document.querySelector('.gallery-thumbnail .wrapper')?.addEventListener('click', (e) => { e.preventDefault(); openPopupByIndex(0); }); const navScroll = document.getElementById('portfolioNavScroll'); const scrollThumb = document.querySelector('.portfolio-scroll-thumb'); const scrollTrack = document.querySelector('.portfolio-scroll-track'); function updateFakeScrollbar() { if (!navScroll || !scrollThumb || !scrollTrack) return; const maxScroll = navScroll.scrollWidth - navScroll.clientWidth; const currentScroll = Math.abs(navScroll.scrollLeft); const trackWidth = scrollTrack.clientWidth; const thumbWidth = 28; const maxThumbMove = trackWidth - thumbWidth; const scrollPercent = currentScroll / maxScroll; scrollThumb.style.right = `${scrollPercent * maxThumbMove}px`; } navScroll?.addEventListener('scroll', updateFakeScrollbar); window.addEventListener('load', updateFakeScrollbar); window.addEventListener('resize', updateFakeScrollbar); document.addEventListener('DOMContentLoaded', () => { const phoneBtn = document.querySelector('.phone-btn'); const desktopBtn = document.querySelector('.desktop-btn'); const portfolioWrapper = document.querySelector('.portfolio-logo-wrapper'); const desktopUse = document.getElementById('desktopIcon'); const phoneUse = document.getElementById('phoneIcon'); const sideWrapper = document.querySelector('.side-buttons-wrapper'); let isMobileView = window.innerWidth <= 768; function updateUI() { sideWrapper?.classList.remove('rotated-desktop'); if (isMobileView) { portfolioWrapper?.classList.add('mobile-view'); phoneBtn?.classList.add('active'); desktopBtn?.classList.remove('active'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon-active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon'); } else { portfolioWrapper?.classList.remove('mobile-view'); desktopBtn?.classList.add('active'); phoneBtn?.classList.remove('active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon-active'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon'); } } phoneBtn?.addEventListener('click', () => { isMobileView = true; updateUI(); }); desktopBtn?.addEventListener('click', () => { isMobileView = false; updateUI(); }); updateUI(); }); const relatedCards = [ ...document.querySelectorAll('[data-related-section="gallery"] .related-card'), ]; const hasGalleryThumbnail = document.querySelector('.gallery-thumbnail') !== null; let currentPopupIndex = 0; const galleryItems = []; if (hasGalleryThumbnail) { galleryItems.push({ type: 'project', }); } relatedCards.forEach((card) => { galleryItems.push({ type: 'card', element: card, }); }); function openPopupByIndex(index) { const item = galleryItems[index]; if (!item) return; currentPopupIndex = index; overlay.classList.remove('active'); imagePopup.classList.remove('active'); if (item.type === 'project') { openPortfolioPopup(); } else { const media = item.element.querySelector('.media-container'); const mediaType = media.dataset.type; const src = media.dataset.src; const poster = media.dataset.poster; if (mediaType === 'video') { openVideoPopup(src, poster); } else { openImagePopup(src); } } updatePopupButtons(); } function updatePopupButtons() { document.querySelectorAll('.next-popup').forEach((btn) => { btn.disabled = currentPopupIndex === galleryItems.length - 1; btn.classList.toggle('disabled', currentPopupIndex === galleryItems.length - 1); }); document.querySelectorAll('.prev-popup').forEach((btn) => { btn.disabled = currentPopupIndex === 0; btn.classList.toggle('disabled', currentPopupIndex === 0); }); } document.querySelector('.gallery-thumbnail .wrapper')?.addEventListener('click', (e) => { e.preventDefault(); openPopupByIndex(0); }); relatedCards.forEach((card) => { card.addEventListener('click', (e) => { if (e.target.closest('.play-btn')) return; const targetIndex = galleryItems.findIndex(item => item.element === card); if (targetIndex !== -1) { openPopupByIndex(targetIndex); } }); }); document.querySelectorAll('.next-popup').forEach((btn) => { btn.addEventListener('click', () => { if (currentPopupIndex < galleryItems.length - 1) { openPopupByIndex(currentPopupIndex + 1); } }); }); document.querySelectorAll('.prev-popup').forEach((btn) => { btn.addEventListener('click', () => { if (currentPopupIndex > 0) { openPopupByIndex(currentPopupIndex - 1); } }); }); window.addEventListener('keydown', (e) => { if (!imagePopup.classList.contains('active')) return; if (e.key === 'ArrowRight') { if (currentPopupIndex < galleryItems.length - 1) { openPopupByIndex(currentPopupIndex + 1); } } if (e.key === 'ArrowLeft') { if (currentPopupIndex > 0) { openPopupByIndex(currentPopupIndex - 1); } } }); let rotationTimer = null; function handleMobilePopupView() { const rotateOverlay = document.getElementById('rotateOverlay'); const portfolioWrapper = document.querySelector('.portfolio-logo-wrapper'); const sideWrapper = document.querySelector('.side-buttons-wrapper'); if (!rotateOverlay || !portfolioWrapper || !sideWrapper) return; rotateOverlay.classList.add('show'); clearTimeout(rotationTimer); rotationTimer = setTimeout(() => { rotateOverlay.classList.remove('show'); updateOrientationState(); }, 2000); window.removeEventListener('resize', updateOrientationState); window.removeEventListener('orientationchange', updateOrientationState); window.addEventListener('resize', updateOrientationState); window.addEventListener('orientationchange', updateOrientationState); function updateOrientationState() { const isLandscape = window.innerWidth > window.innerHeight; const desktopBtn = document.querySelector('.desktop-btn'); const phoneBtn = document.querySelector('.phone-btn'); const desktopUse = document.getElementById('desktopIcon'); const phoneUse = document.getElementById('phoneIcon'); if (isLandscape) { portfolioWrapper.classList.remove('mobile-view'); sideWrapper.classList.add('rotated-desktop'); sideWrapper.classList.add('is-rotated'); overlay.classList.add('is-rotated2'); desktopBtn?.classList.add('active'); phoneBtn?.classList.remove('active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon-active'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon'); } else { portfolioWrapper.classList.add('mobile-view'); sideWrapper.classList.remove('rotated-desktop'); sideWrapper.classList.remove('is-rotated'); overlay.classList.remove('is-rotated2'); phoneBtn?.classList.add('active'); desktopBtn?.classList.remove('active'); phoneUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#phone-icon-active'); desktopUse?.setAttribute('href', './src/assets/icons/portfolioP.svg#descktop-icon'); } } updateOrientationState(); } window.addEventListener('load', () => { const portfolioImageScroll = document.getElementById('portfolioScroll'); const portfolioCustomThumb = document.getElementById('scrollThumb'); const portfolioCustomTrack = document.querySelector('.scroll-track'); if (!portfolioImageScroll || !portfolioCustomThumb || !portfolioCustomTrack) { console.log('Portfolio scroll elements not found'); return; } let isDraggingPortfolioThumb = false; function updatePortfolioThumb() { if (isDraggingPortfolioThumb) return; const maxScroll = portfolioImageScroll.scrollHeight - portfolioImageScroll.clientHeight; const maxThumbMove = portfolioCustomTrack.clientHeight - portfolioCustomThumb.clientHeight; if (maxScroll <= 0) return; const thumbPosition = (portfolioImageScroll.scrollTop / maxScroll) * maxThumbMove; portfolioCustomThumb.style.top = `${thumbPosition}px`; } portfolioImageScroll.addEventListener('scroll', updatePortfolioThumb); updatePortfolioThumb(); let portfolioDragOffsetY = 0; portfolioCustomThumb.addEventListener('mousedown', (event) => { isDraggingPortfolioThumb = true; document.body.style.userSelect = 'none'; portfolioDragOffsetY = event.clientY - portfolioCustomThumb.getBoundingClientRect().top; }); document.addEventListener('mouseup', () => { isDraggingPortfolioThumb = false; document.body.style.userSelect = ''; }); document.addEventListener('mousemove', (event) => { if (!isDraggingPortfolioThumb) return; const trackRect = portfolioCustomTrack.getBoundingClientRect(); let newThumbTop = event.clientY - trackRect.top - portfolioDragOffsetY; const maxThumbTop = portfolioCustomTrack.clientHeight - portfolioCustomThumb.clientHeight; newThumbTop = Math.max(0, Math.min(newThumbTop, maxThumbTop)); portfolioCustomThumb.style.top = `${newThumbTop}px`; const scrollPercent = newThumbTop / maxThumbTop; const maxImageScroll = portfolioImageScroll.scrollHeight - portfolioImageScroll.clientHeight; portfolioImageScroll.scrollTop = scrollPercent * maxImageScroll; }); }); const nameInput = document.getElementById('name'); const nameError = document.getElementById('nameError'); nameInput.addEventListener('input', function () { const hasNumber = /\d/.test(this.value); if (hasNumber) { this.classList.add('input-error'); nameError.textContent = '* فقط حروف فارسی و انگلیسی مجاز است'; } else { this.classList.remove('input-error'); nameError.textContent = ''; } }); const phoneInput = document.getElementById('phone'); const phoneError = document.getElementById('phoneError'); phoneInput.addEventListener('input', function () { const hasLetter = /[a-zA-Zآ-ی]/.test(this.value); if (hasLetter) { this.classList.add('input-error'); phoneError.textContent = 'فقط عدد مجاز است'; } else { this.classList.remove('input-error'); phoneError.textContent = ''; } this.value = this.value.replace(/\D/g, ''); }); const emailInput = document.getElementById('email'); const emailError = document.getElementById('emailError'); emailInput.addEventListener('input', function () { if (this.value !== '' && !this.value.includes('@')) { this.classList.add('input-error'); emailError.textContent = 'ایمیل باید شامل @ باشد'; } else { this.classList.remove('input-error'); emailError.textContent = ''; } }); const slider = document.getElementById('portfolioNavScroll'); let isDown = false; let startX = 0; let scrollLeft = 0; slider.addEventListener('mousedown', (e) => { isDown = true; slider.style.cursor = 'grabbing'; startX = e.pageX; scrollLeft = slider.scrollLeft; }); window.addEventListener('mouseup', () => { isDown = false; slider.style.cursor = 'grab'; }); slider.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const walk = e.pageX - startX; slider.scrollLeft = scrollLeft - walk; }); slider.addEventListener( 'wheel', (e) => { e.preventDefault(); slider.scrollLeft += e.deltaY; }, { passive: false } );