Files
inova-aboutus/src/assets/js/aboutus.js
2026-07-05 12:52:17 +03:30

806 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ========================= هیرو سکشن==============
const thumbnail = document.getElementById('videoThumbnail');
const video = document.getElementById('aboutVideo');
thumbnail.addEventListener('click', () => {
thumbnail.style.display = 'none';
video.style.display = 'block';
video.play();
});
// ========================= اسلایدر سکشن دوم==============
const slides = [
{
type: 'image',
src: 'src/assets/images/pic2.webp',
number: 'شروع داستان',
title: 'اکثر کسب‌وکارها با یک ایده جذاب شروع می‌شوند ولی اینوا با یک شکست آغاز کرد!',
description:
'چند سال پیش، یک جوان بوشهری با این هدف وارد دنیای کسب‌وکار شد که فقط به فکر خلق درآمد نباشد. این جوان می‌خواست مسئله‌ای را حل کند، ارزشی ایجاد کند و محصول را به دست کسانی برساند که به آن نیاز دارند. اولین قدمش شد راه‌اندازی یک فروشگاه آنلاین خرما؛ محصولی اصیل، باکیفیت و ریشه‌دار.',
gradientClass: 'gradient-one',
mobilePositionClass: 'mobile-pos-one',
},
{
type: 'image',
src: 'src/assets/images/pic4.webp',
number: 'درس اول',
title: 'موفقیت فقط به کیفیت محصول وابسته نیست.',
description:
'اما واقعیت بازار با چیزی که تصور می‌کرد متفاوت بود. محصول خوب بود، اما نمی‌فروخت. نه چون کسی خرما نمی‌خواست بلکه کسی خرمای این فروشگاه آنلاین را نمی‌دید. همان‌جا بود که جوان، درس اول را یاد گرفت: موفقیت فقط به کیفیت محصول وابسته نیست. اگر کسی از وجودت خبر نداشته باشد، بهترین محصول دنیا هم روی قفسه‌های دیجیتال خاک می‌خورد.',
gradientClass: 'gradient-two',
mobilePositionClass: 'mobile-pos-two',
},
{
type: 'image',
src: 'src/assets/images/pic5.webp',
number: 'زمینه شکل گیری ',
title: 'پیدا کردن مشکل و راه‌حل آن',
description:
'نه جا زد، نه ناامید شد؛ شروع کرد دنبال دلیل گشتن و آموختن بیشتر و بیشتر: از طراحی سایت و سئو گرفته تا تولید محتوا، تبلیغات دیجیتال، برندینگ و تجربه کاربری. هر پروژه، هر تجربه و هر ساعتِ یادگیری یک واقعیت را برایش روشن‌تر می‌کرد: دیده نشدن فقط مشکل او نبود. هزاران کسب‌وکار، استارتاپ و برند کوچک وجود داشتند که محصولات و خدمات ارزشمندی ارائه می‌کردند، اما نمی‌توانستند به مخاطبان درست دسترسی پیدا کنند.',
gradientClass: 'gradient-three',
mobilePositionClass: 'mobile-pos-three',
},
{
type: 'image',
src: 'src/assets/images/pic6.webp',
number: ' تولد اینوا',
title: 'اینوا از دل همین نیاز متولد شد!',
description:
'اینوا باور دارد که رشد کسب‌وکارها نباید فقط در اختیار برندهای بزرگ با بودجه‌های سنگین باشد. هر ایده ارزشمند، هر محصول خوب و هر کسب‌وکاری که برای مشتریانش ارزش خلق می‌کند، شایسته دیده شدن است.',
gradientClass: 'gradient-three',
mobilePositionClass: 'mobile-pos-three',
},
{
type: 'image',
src: 'src/assets/images/pic3.webp',
number: ' هدف اینوا ',
title: 'اینوا در خدمت کسب و کارها',
description:
'اینوا اینجاست تا به کسب‌وکارها کمک کند که هویت برند قدرتمندتری بسازند، مخاطبان خود را پیدا کرده و مسیر رشدشان را سریع‌تر طی کنند. چون ما از نزدیک تجربه کرده‌ایم که گاهی فاصله بین یک ایده عالی و یک کسب‌وکار موفق، فقط دیده شدن است. و این دقیقاً همان فاصله‌ای است که اینوا به‌درستی آن را پرخواهد کرد ',
gradientClass: 'gradient-three',
mobilePositionClass: 'mobile-pos-three',
},
];
const showcase = document.getElementById('showcase');
const mediaWrapper = document.getElementById('showcasemediaWrapper');
const textTrack = document.getElementById('textTrack');
function renderMedia() {
slides.forEach((slide, index) => {
const item = document.createElement('div');
item.className = `
media-item
${slide.gradientClass || ''}
${slide.mobilePositionClass || ''}
`;
if (index === 0) {
item.classList.add('active');
}
if (slide.type === 'image' || slide.type === 'gif') {
const img = document.createElement('img');
img.src = slide.src;
item.appendChild(img);
}
if (slide.type === 'video') {
const video = document.createElement('video');
video.src = slide.src;
video.autoplay = true;
video.loop = true;
video.muted = true;
video.playsInline = true;
item.appendChild(video);
}
mediaWrapper.appendChild(item);
});
}
function renderTexts() {
slides.forEach((slide) => {
const item = document.createElement('div');
item.className = 'text-item';
item.innerHTML = `
<span>${slide.number}</span>
<h2>
${slide.title}
</h2>
<p>
${slide.description}
</p>
`;
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);
const current = Math.round(exactIndex);
if (isMobile) {
textTrack.style.transform = 'translateY(0)';
resetTextItemInlineStyles();
setActiveSlide(current);
return;
}
textTrack.style.transform = `translateY(-${exactIndex * 60}vh)`;
textItems.forEach((item) => {
item.classList.remove('active');
});
textItems.forEach((item, index) => {
const offset = index - exactIndex;
let blur = 0;
let opacity = 1;
if (offset > 0) {
const enterProgress = Math.min(offset, 1);
blur = Math.max((enterProgress - 0.35) * 14, 0);
opacity = Math.min(1.2 - enterProgress, 1);
}
item.style.filter = `blur(${blur}px)`;
item.style.opacity = opacity;
});
setActiveSlide(current);
}
window.addEventListener('scroll', updateScrollScene);
window.addEventListener('resize', updateScrollScene);
updateScrollScene();
// =========================================== 300پروژه ================================================
document.addEventListener('DOMContentLoaded', () => {
const stats = [
{ number: '1.5X', text: 'افزایش رشد/فروش' },
{ number: '45+', text: 'تعداد مشتریان' },
{ number: '91%', text: 'رضایت کارفرمایان' },
{ number: '300+', text: 'پروژه موفق' },
];
const numberEl = document.getElementById('mobileNumber');
const textEl = document.getElementById('mobileText');
const content = document.querySelector('.mobile-content');
let index = 0;
setInterval(() => {
content.classList.add('fade-out');
setTimeout(() => {
index = (index + 1) % stats.length;
numberEl.textContent = stats[index].number;
textEl.textContent = stats[index].text;
content.classList.remove('fade-out');
}, 500);
}, 3000);
});
//op-box
function initOpSlider() {
const slider = document.querySelector('.op-box');
const pagination = document.querySelector('.op-pagination');
if (!slider || !pagination) {
return;
}
let track = slider.querySelector('.op-track');
if (!track) {
track = document.createElement('div');
track.className = 'op-track';
while (slider.firstChild) {
track.appendChild(slider.firstChild);
}
slider.appendChild(track);
}
const cards = track.querySelectorAll('.op-card');
if (!cards.length) {
return;
}
let cardWidth = 0;
let cardsPerPage = 1;
let totalPages = 1;
function getCardsPerPage() {
const width = slider.clientWidth;
if (width <= 768) {
return 1;
}
if (width <= 1099) {
return 2;
}
return 3;
}
function getMaxScroll() {
return Math.max(0, slider.scrollWidth - slider.clientWidth);
}
function getScrollOffset() {
const max = getMaxScroll();
return Math.max(0, max - slider.scrollLeft);
}
function getCurrentPage() {
if (!cardWidth) {
return 0;
}
const max = getMaxScroll();
if (max <= 0) {
return 0;
}
const offset = getScrollOffset();
if (offset <= 8) {
return 0;
}
if (offset >= max - 8) {
return totalPages - 1;
}
return Math.min(Math.round(offset / (cardWidth * cardsPerPage)), totalPages - 1);
}
function scrollToPage(page) {
const max = getMaxScroll();
const target = max - Math.min(page * cardsPerPage * cardWidth, max);
slider.scrollTo({
left: target,
behavior: 'smooth',
});
}
function updateDots() {
const page = getCurrentPage();
pagination.querySelectorAll('.op-dot').forEach((dot, dotIndex) => {
dot.classList.toggle('active', dotIndex === page);
});
}
function setupSlider() {
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const previousPage = cardWidth ? getCurrentPage() : 0;
cardsPerPage = getCardsPerPage();
const cardBasis =
cardsPerPage === 1
? Math.min(358, slider.clientWidth - 48)
: (slider.clientWidth - gap * (cardsPerPage - 1)) / cardsPerPage;
cards.forEach((card) => {
card.style.flex = `0 0 ${cardBasis}px`;
card.style.width = `${cardBasis}px`;
card.style.maxWidth = `${cardBasis}px`;
});
cardWidth = cardBasis + gap;
totalPages = Math.ceil(cards.length / cardsPerPage);
pagination.innerHTML = '';
for (let i = 0; i < totalPages; i++) {
const dot = document.createElement('button');
dot.type = 'button';
dot.classList.add('op-dot');
dot.setAttribute('aria-label', `اسلاید ${i + 1}`);
dot.addEventListener('click', () => {
scrollToPage(i);
});
pagination.appendChild(dot);
}
const max = getMaxScroll();
slider.scrollLeft = max - Math.min(previousPage * cardsPerPage * cardWidth, max);
updateDots();
}
setupSlider();
window.addEventListener('resize', setupSlider);
slider.addEventListener('scroll', () => requestAnimationFrame(updateDots), { passive: true });
const DRAG_THRESHOLD = 40;
let isDown = false;
let startX = 0;
let startScroll = 0;
let startPage = 0;
slider.addEventListener('pointerdown', (e) => {
if ('mouse' === e.pointerType && 0 !== e.button) {
return;
}
isDown = true;
startX = e.clientX;
startScroll = slider.scrollLeft;
startPage = getCurrentPage();
slider.setPointerCapture(e.pointerId);
slider.style.scrollBehavior = 'auto';
slider.style.cursor = 'grabbing';
});
slider.addEventListener('pointermove', (e) => {
if (!isDown) {
return;
}
e.preventDefault();
slider.scrollLeft = startScroll - (e.clientX - startX);
});
function endDrag(e) {
if (!isDown) {
return;
}
isDown = false;
slider.style.scrollBehavior = '';
slider.style.cursor = 'grab';
const delta = e.clientX - startX;
let targetPage = startPage;
if (delta > DRAG_THRESHOLD) {
targetPage = startPage + 1;
} else if (delta < -DRAG_THRESHOLD) {
targetPage = startPage - 1;
}
scrollToPage(Math.max(0, Math.min(totalPages - 1, targetPage)));
}
slider.addEventListener('pointerup', endDrag);
slider.addEventListener('pointercancel', endDrag);
slider.style.cursor = 'grab';
}
document.addEventListener('DOMContentLoaded', initOpSlider);
// ================scroll logos========================
document.querySelectorAll('.slider-track').forEach((track) => {
const items = Array.from(track.children);
items.forEach((item) => track.appendChild(item.cloneNode(true)));
});
// =========================================== تایم لاین================================================
(function () {
const track = document.getElementById('timelineTrack');
const viewport = document.getElementById('timelineViewport');
const btnPrev = document.getElementById('timelinePrev');
const btnNext = document.getElementById('timelineNext');
let currentIndex = 0; // how many cards we've "shifted" from start
const MOBILE_BP = 768;
/* ── helpers ── */
function isMobile() {
return window.innerWidth <= MOBILE_BP;
}
function getCardWidth() {
const firstCard = track.querySelector('.timeline-card');
if (!firstCard) return 0;
const style = getComputedStyle(firstCard);
const gap = parseFloat(getComputedStyle(track).gap) || 24;
return firstCard.offsetWidth + gap;
}
function totalCards() {
return track.querySelectorAll('.timeline-card').length;
}
function visibleCount() {
if (window.innerWidth <= 768) return 1;
if (window.innerWidth <= 1024) return 2;
return 3;
}
function maxIndex() {
return Math.max(0, totalCards() - visibleCount());
}
function applyTransform() {
const shift = currentIndex * getCardWidth();
track.style.transform = `translateX(${shift}px)`;
}
function updateButtons() {
btnPrev.disabled = currentIndex <= 0;
btnNext.disabled = currentIndex >= maxIndex();
}
function goTo(index) {
currentIndex = Math.max(0, Math.min(index, maxIndex()));
applyTransform();
updateButtons();
}
/* ── nav click ── */
btnPrev.addEventListener('click', () => goTo(currentIndex - 1));
btnNext.addEventListener('click', () => goTo(currentIndex + 1));
/* ── touch / swipe ── */
let touchStartX = 0;
let touchStartY = 0;
let isDragging = false;
track.addEventListener(
'touchstart',
(e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
isDragging = false;
},
{ passive: true }
);
track.addEventListener(
'touchmove',
(e) => {
const dx = e.touches[0].clientX - touchStartX;
const dy = e.touches[0].clientY - touchStartY;
if (!isDragging && Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 8) {
isDragging = true;
}
if (isDragging) e.preventDefault();
},
{ passive: false }
);
track.addEventListener('touchend', (e) => {
if (!isDragging) return;
const dx = e.changedTouches[0].clientX - touchStartX;
const threshold = 50;
if (dx > threshold) goTo(currentIndex - 1);
if (dx < -threshold) goTo(currentIndex + 1);
isDragging = false;
});
/* ── resize ── */
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
currentIndex = Math.min(currentIndex, maxIndex());
applyTransform();
updateButtons();
initToggleVisibility();
}, 120);
});
/* ── show/hide "بیشتر" only when text actually overflows 6 lines ── */
function initToggleVisibility() {
track.querySelectorAll('.timeline-card__body').forEach((body) => {
const index = body.id.replace('body-', '');
const toggle = document.getElementById(`toggle-${index}`);
if (!toggle) return;
// Temporarily remove clamp to measure full natural height
body.style.webkitLineClamp = 'unset';
body.style.display = 'block';
const fullHeight = body.scrollHeight;
// Re-apply clamp and measure clamped height
body.style.webkitLineClamp = '';
body.style.display = '';
const clampedHeight = body.clientHeight;
// If already expanded, keep button visible regardless
if (body.classList.contains('is-expanded')) {
toggle.classList.add('is-visible');
return;
}
// Show button only if content is taller than what's visible
if (fullHeight > clampedHeight + 2) {
toggle.classList.add('is-visible');
} else {
toggle.classList.remove('is-visible');
}
});
}
/* ── init ── */
updateButtons();
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(initToggleVisibility);
} else {
setTimeout(initToggleVisibility, 200);
}
/* ── expose toggleCard globally ── */
window.toggleCard = function (index) {
const body = document.getElementById(`body-${index}`);
const toggle = document.getElementById(`toggle-${index}`);
const icon = toggle.querySelector('.timeline-card__toggle-icon');
const label = toggle.querySelector('.timeline-card__toggle-label');
const expanded = body.classList.toggle('is-expanded');
icon.textContent = expanded ? '' : '+';
label.textContent = expanded ? 'کمتر' : 'بیشتر';
toggle.setAttribute('aria-expanded', expanded);
};
})();
// ================ pic team========================
const picteamSlides = [
{
src: './public/images/pic2.png',
caption: 'خانواده خلاق اینوا',
},
{
src: './public/images/pic1.webp',
caption: ' جلسات گروهی برای خلق ایده های ناب ',
},
{
src: './public/images/pic3.webp',
caption: ' جادوی سینما به سبک اینوا ',
},
];
const total = picteamSlides.length;
let current = 0;
let captionVisible = false;
/* ─── BUILD DESKTOP ─── */
const track = document.getElementById('picteamTrack');
picteamSlides.forEach((s, i) => {
const slide = document.createElement('div');
slide.className = 'picteam-slide';
slide.dataset.index = i;
const img = document.createElement('img');
img.src = s.src;
img.alt = s.caption;
img.loading = 'lazy';
const line = document.createElement('div');
line.className = 'picteam-accent-line';
const caption = document.createElement('div');
caption.className = 'picteam-caption';
caption.textContent = s.caption;
slide.appendChild(img);
slide.appendChild(line);
slide.appendChild(caption);
track.appendChild(slide);
slide.addEventListener('mouseenter', () => {
if (slide.classList.contains('active') && !captionVisible) {
captionVisible = true;
const cap = slide.querySelector('.picteam-caption');
setTimeout(() => cap.classList.add('visible'), 80);
}
});
});
function mod(n, m) {
return ((n % m) + m) % m;
}
function updateDesktop(animate) {
const slides = track.querySelectorAll('.picteam-slide');
const prevIdx = mod(current - 1, total);
const nextIdx = mod(current + 1, total);
slides.forEach((slide, i) => {
const caption = slide.querySelector('.picteam-caption');
slide.className = 'picteam-slide';
caption.className = 'picteam-caption';
if (i === current) {
slide.classList.add('active');
if (captionVisible) {
// small delay so transition is visible
setTimeout(() => caption.classList.add('visible'), 150);
}
} else if (i === prevIdx) {
slide.classList.add('prev');
} else if (i === nextIdx) {
slide.classList.add('next');
} else {
slide.classList.add('hidden');
}
});
}
document.getElementById('picteamPrev').addEventListener('click', () => {
current = mod(current - 1, total);
captionVisible = true;
updateDesktop(true);
updateMobile();
});
document.getElementById('picteamNext').addEventListener('click', () => {
current = mod(current + 1, total);
captionVisible = true;
updateDesktop(true);
updateMobile();
});
/* ─── BUILD MOBILE ─── */
const mobileTrack = document.getElementById('picteamMobileTrack');
const dotsContainer = document.getElementById('picteamDots');
picteamSlides.forEach((s, i) => {
// slide
const slide = document.createElement('div');
slide.className = 'picteam-mobile-slide';
const img = document.createElement('img');
img.src = s.src;
img.alt = s.caption;
img.loading = 'lazy';
const line = document.createElement('div');
line.className = 'picteam-accent-line';
const caption = document.createElement('div');
caption.className = 'picteam-mobile-caption';
caption.textContent = s.caption;
slide.appendChild(img);
slide.appendChild(line);
slide.appendChild(caption);
mobileTrack.appendChild(slide);
// dot
const dot = document.createElement('button');
dot.className = 'picteam-dot' + (i === 0 ? ' active' : '');
dot.setAttribute('aria-label', `اسلاید ${i + 1}`);
dot.addEventListener('click', () => {
current = i;
updateMobile();
updateDesktop(true);
});
dotsContainer.appendChild(dot);
});
function updateMobile() {
mobileTrack.style.transform = `translateX(${current * 100}%)`;
// dots
const dots = dotsContainer.querySelectorAll('.picteam-dot');
dots.forEach((d, i) => d.classList.toggle('active', i === current));
// captions
const mobileCaptions = mobileTrack.querySelectorAll('.picteam-mobile-caption');
mobileCaptions.forEach((c, i) => {
c.classList.remove('visible');
if (i === current) {
setTimeout(() => {
c.classList.add('visible');
}, 150);
}
});
}
/* RTL swipe: drag right = prev, drag left = next */
let touchStartX = 0;
let touchEndX = 0;
const mobileWrapper = document.getElementById('picteamMobileWrapper');
mobileWrapper.addEventListener(
'touchstart',
(e) => {
touchStartX = e.changedTouches[0].clientX;
},
{ passive: true }
);
mobileWrapper.addEventListener(
'touchend',
(e) => {
touchEndX = e.changedTouches[0].clientX;
const diff = touchStartX - touchEndX;
if (Math.abs(diff) > 40) {
if (diff > 0) {
current = mod(current - 1, total);
} else {
current = mod(current + 1, total);
}
updateMobile();
updateDesktop(true);
}
},
{ passive: true }
);
/* ─── INTERSECTION OBSERVER (scroll reveal captions) ─── */
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !captionVisible) {
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.4 }
);
observer.observe(document.getElementById('picteam'));
/* ─── INIT ─── */
updateDesktop(false);
updateMobile();