[260222]
필터 로직 개선 최종 스크립트 코드 정리
This commit is contained in:
@@ -1,111 +1,51 @@
|
||||
/** 모달 내 이미지 캐러셀 (드래그·스와이프·인덱스 동기화) */
|
||||
/**
|
||||
* carousel.js
|
||||
* 모달 이미지 캐러셀: 무한 루프 슬라이드, 드래그/스와이프 및 UI 동기화
|
||||
*/
|
||||
|
||||
// ==========================================================================
|
||||
// 1. 유틸리티 및 계산 함수 (Internal Utils)
|
||||
// ==========================================================================
|
||||
|
||||
/** 현재 스크롤 위치를 바탕으로 실제 이미지 인덱스(0 ~ length-1)를 반환 */
|
||||
export function getRealIndex(container, originalLength) {
|
||||
let rawIndex = Math.round(container.scrollLeft / container.clientWidth);
|
||||
const 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' });
|
||||
}
|
||||
/** 드래그 종료 시 관성 및 이동 거리를 계산하여 다음 인덱스 결정 */
|
||||
function calculateTargetIndex(delta, elapsed, currentScroll, width, originalLength) {
|
||||
const minMove = Math.min(width * 0.05, 20);
|
||||
const hasMoved = Math.abs(delta) >= minMove;
|
||||
|
||||
// 빠른 스와이프(200ms 미만) 또는 일정 거리 이상 드래그 시 방향 확정
|
||||
const direction = hasMoved && (Math.abs(delta) > width * 0.1 || elapsed < 200) ? (delta > 0 ? 1 : -1) : 0;
|
||||
|
||||
let index = Math.round(currentScroll / width) + direction;
|
||||
return Math.max(0, Math.min(originalLength + 1, index));
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// 2. UI 동기화 및 스크롤 (UI & Scrolling)
|
||||
// ==========================================================================
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// 썸네일 & 도트 업데이트 로직 (생략 - 기존과 동일)
|
||||
updateThumbnailUI(index);
|
||||
updateDotUI(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;
|
||||
@@ -114,3 +54,78 @@ export function scrollToImage(index) {
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// 3. 캐러셀 엔진 (Core Engine)
|
||||
// ==========================================================================
|
||||
|
||||
export function initBetterCarousel(container, originalLength) {
|
||||
let state = {
|
||||
isDragging: false,
|
||||
startX: 0,
|
||||
startScroll: 0,
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
const getX = (e) => (e.touches ? e.touches[0].pageX : e.pageX);
|
||||
|
||||
// 핸들러 분리: 시작
|
||||
const handleStart = (e) => {
|
||||
state.isDragging = true;
|
||||
state.startX = getX(e);
|
||||
state.startScroll = container.scrollLeft;
|
||||
state.startTime = Date.now();
|
||||
};
|
||||
|
||||
// 핸들러 분리: 이동
|
||||
const handleMove = (e) => {
|
||||
if (!state.isDragging) return;
|
||||
container.scrollLeft = state.startScroll - (getX(e) - state.startX);
|
||||
};
|
||||
|
||||
// 핸들러 분리: 종료
|
||||
const handleEnd = () => {
|
||||
if (!state.isDragging) return;
|
||||
state.isDragging = false;
|
||||
|
||||
const w = container.clientWidth;
|
||||
if (w <= 0) return;
|
||||
|
||||
const delta = container.scrollLeft - state.startScroll;
|
||||
const elapsed = Date.now() - state.startTime;
|
||||
|
||||
const index = calculateTargetIndex(delta, elapsed, container.scrollLeft, w, originalLength);
|
||||
finalizeScroll(container, index, w, originalLength);
|
||||
};
|
||||
|
||||
/** 드래그 종료 후 스냅 애니메이션 및 루프 처리 */
|
||||
function finalizeScroll(container, index, width, originalLength) {
|
||||
requestAnimationFrame(() => {
|
||||
const targetLeft = index * width;
|
||||
const useSmooth = Math.abs(container.scrollLeft - targetLeft) > width * 0.03;
|
||||
|
||||
container.style.scrollBehavior = useSmooth ? 'smooth' : 'auto';
|
||||
container.scrollTo({ left: targetLeft });
|
||||
|
||||
// 루프 워프 처리
|
||||
setTimeout(
|
||||
() => {
|
||||
container.style.scrollBehavior = 'auto';
|
||||
if (index === 0) container.scrollLeft = width * originalLength;
|
||||
if (index === originalLength + 1) container.scrollLeft = width;
|
||||
syncModalUI(originalLength);
|
||||
},
|
||||
useSmooth ? 320 : 0,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 리스너 등록
|
||||
container.addEventListener('mousedown', handleStart);
|
||||
container.addEventListener('touchstart', handleStart, { passive: true });
|
||||
container.addEventListener('mousemove', handleMove);
|
||||
container.addEventListener('touchmove', handleMove, { passive: false });
|
||||
container.addEventListener('mouseup', handleEnd);
|
||||
container.addEventListener('mouseleave', handleEnd);
|
||||
container.addEventListener('touchend', handleEnd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user