[260204] 역할별 스크립트 분리, 다크모드 추가

This commit is contained in:
2026-02-04 14:56:59 +09:00
parent d4ccc616a7
commit b29cd5e7d9
12 changed files with 1165 additions and 1056 deletions

116
scripts/carousel.js Normal file
View File

@@ -0,0 +1,116 @@
/** 모달 내 이미지 캐러셀 (드래그·스와이프·인덱스 동기화) */
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',
});
}