/** 모달 내 이미지 캐러셀 (드래그·스와이프·인덱스 동기화) */ export function getRealIndex(container, originalLength) { let rawIndex = Math.round(container.scrollLeft / container.clientWidth); let index = rawIndex - 1; if (index < 0) index = originalLength - 1; if (index >= originalLength) index = 0; return index; } export function ensureThumbnailVisible(index) { const container = document.getElementById('modal-thumbnails'); if (!container) return; const thumbs = container.querySelectorAll('.modal-thumb-item'); const active = thumbs[index]; if (!active) return; const cRect = container.getBoundingClientRect(); const tRect = active.getBoundingClientRect(); const isVisible = tRect.top >= cRect.top && tRect.bottom <= cRect.bottom; if (!isVisible) { active.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } export function syncModalUI(originalLength) { const container = document.getElementById('modal-main-carousel-container'); if (!container) return; const index = getRealIndex(container, originalLength); document.querySelectorAll('.modal-thumb-item').forEach((t, i) => { t.classList.toggle('border-primary', i === index); t.classList.toggle('opacity-100', i === index); t.classList.toggle('opacity-70', i !== index); }); document.querySelectorAll('.modal-dot-item').forEach((d, i) => { d.classList.toggle('bg-primary', i === index); d.classList.toggle('w-4', i === index); d.classList.toggle('bg-gray-300', i !== index); d.classList.toggle('w-2', i !== index); }); ensureThumbnailVisible(index); } export function initBetterCarousel(container, originalLength) { let isDragging = false; let startX = 0; let startScroll = 0; let startTime = 0; const width = () => container.clientWidth; container.addEventListener('mousedown', start); container.addEventListener('touchstart', start, { passive: true }); function start(e) { isDragging = true; startX = e.touches ? e.touches[0].pageX : e.pageX; startScroll = container.scrollLeft; startTime = Date.now(); } container.addEventListener('mousemove', move); container.addEventListener('touchmove', move, { passive: false }); function move(e) { if (!isDragging) return; const x = e.touches ? e.touches[0].pageX : e.pageX; container.scrollLeft = startScroll - (x - startX); } container.addEventListener('mouseup', end); container.addEventListener('mouseleave', end); container.addEventListener('touchend', end); function end(e) { if (!isDragging) return; isDragging = false; const w = width(); if (w <= 0) return; const endScroll = container.scrollLeft; const delta = endScroll - startScroll; const elapsed = Date.now() - startTime; // 클릭만 했을 때(이동 거리 거의 없음)는 슬라이드 이동 안 함. 일정 이상 드래그했을 때만 방향 적용 const minMove = Math.min(w * 0.05, 20); const hasMoved = Math.abs(delta) >= minMove; const direction = hasMoved && (Math.abs(delta) > w * 0.1 || elapsed < 200) ? (delta > 0 ? 1 : -1) : 0; let index = Math.round(endScroll / w) + direction; index = Math.max(0, Math.min(originalLength + 1, index)); const targetLeft = index * w; // 손 뗀 직후 같은 프레임에서 스크롤 충돌 방지 + 미세 이동은 즉시 적용해서 튐 방지 requestAnimationFrame(() => { const snapThreshold = w * 0.03; const useSmooth = Math.abs(container.scrollLeft - targetLeft) > snapThreshold; container.style.scrollBehavior = useSmooth ? 'smooth' : 'auto'; container.scrollTo({ left: targetLeft }); const loopDelay = useSmooth ? 320 : 0; setTimeout(() => { container.style.scrollBehavior = 'auto'; if (index === 0) container.scrollLeft = w * originalLength; if (index === originalLength + 1) container.scrollLeft = w; syncModalUI(originalLength); }, loopDelay); }); } } export function scrollToImage(index) { const container = document.getElementById('modal-main-carousel-container'); if (!container) return; container.scrollTo({ left: container.clientWidth * (index + 1), behavior: 'smooth', }); }