- 카드 전환 방식 변경

- 필터 조건 선택 가능하게 변경 등
This commit is contained in:
2026-02-12 01:35:44 +09:00
parent d9dea9581c
commit a7817d2113
17 changed files with 440 additions and 225 deletions

View File

@@ -14,6 +14,8 @@ window.closeModal = closeModal;
window.changePage = changePage;
window.scrollToImage = scrollToImage;
let fadeTimers = {};
// 뷰 전환 이벤트
document.getElementById('view-grid').onclick = () => {
state.viewMode = 'grid';
@@ -259,56 +261,161 @@ window.exportToExcel = () => {
let currentHoverIndex = -1;
window.handleThumbnailHover = (event, productId) => {
window.handleThumbnailHover = (e, productId) => {
if (window.isDragging) return;
const product = productsData.find((p) => p.id === productId);
if (!product || product.images.length <= 1) return;
if (!product || !product.images || product.images.length <= 1) return;
const container = event.currentTarget;
const thumbImg = document.getElementById(`thumb-${productId}`);
const indicator = document.getElementById(`indicator-${productId}`);
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left;
let index = Math.floor((x / rect.width) * product.images.length);
index = Math.max(0, Math.min(product.images.length - 1, index));
const rect = container.getBoundingClientRect();
const x = event.clientX - rect.left;
const index = Math.min(Math.floor((x / rect.width) * product.images.length), product.images.length - 1);
// [핵심] 인덱스가 실제로 바뀔 때만 딱 한 번 실행
if (currentHoverIndex !== index) {
currentHoverIndex = index;
const targetImgUrl = product.images[index];
requestAnimationFrame(() => {
// 이미 프리로드된 상태라면 즉시 교체됨
thumbImg.style.backgroundImage = `url("${targetImgUrl}")`;
if (indicator) {
Array.from(indicator.children).forEach((dot, i) => {
if (i === index) {
dot.style.backgroundColor = 'white';
dot.style.transform = 'scale(1.2)';
dot.style.opacity = '1';
} else {
dot.style.backgroundColor = 'rgba(255, 255, 255, 0.4)';
dot.style.transform = 'scale(1)';
dot.style.opacity = '0.7';
}
});
}
});
updateThumbnailWithFade(productId, product.images[index], index);
}
};
// 페이드 업데이트 함수
function updateThumbnailWithFade(productId, newImageUrl, index) {
const mainThumb = document.getElementById(`thumb-${productId}`);
const fadeThumb = document.getElementById(`thumb-fade-${productId}`);
const indicator = document.getElementById(`indicator-${productId}`);
if (!mainThumb || !fadeThumb) return;
// 기존에 해당 카드에서 돌아가던 타이머가 있다면 즉시 제거
if (fadeTimers[productId]) {
clearTimeout(fadeTimers[productId]);
}
// 페이드 레이어 세팅
fadeThumb.style.transition = 'opacity 0.3s ease-in-out';
fadeThumb.style.backgroundImage = `url("${newImageUrl}")`;
fadeThumb.style.opacity = '1';
// 타이머 시작
fadeTimers[productId] = setTimeout(() => {
mainThumb.style.backgroundImage = `url("${newImageUrl}")`;
fadeThumb.style.opacity = '0';
delete fadeTimers[productId]; // 작업 완료 후 타이머 삭제
}, 300);
if (indicator) updateIndicatorUI(indicator, index);
}
window.handleThumbnailLeave = (productId) => {
currentHoverIndex = -1; // 인덱스 초기화
resetThumbnail(productId);
};
function resetThumbnail(productId) {
// 1. 진행 중인 모든 페이드 타이머 즉시 파괴
if (fadeTimers[productId]) {
clearTimeout(fadeTimers[productId]);
delete fadeTimers[productId];
}
const product = productsData.find((p) => p.id === productId);
if (!product) return;
const thumbImg = document.getElementById(`thumb-${productId}`);
const mainThumb = document.getElementById(`thumb-${productId}`);
const fadeThumb = document.getElementById(`thumb-fade-${productId}`);
const indicator = document.getElementById(`indicator-${productId}`);
thumbImg.style.backgroundImage = `url("${product.images[0]}")`;
if (indicator) {
Array.from(indicator.children).forEach((dot, i) => {
dot.style.backgroundColor = i === 0 ? 'white' : 'rgba(255,255,255,0.3)';
});
if (mainThumb && fadeThumb) {
const firstImg = `url("${product.images[0]}")`;
// 2. 페이드 레이어를 즉시 숨김 (transition 방해 금지)
fadeThumb.style.transition = 'none';
fadeThumb.style.opacity = '0';
// 3. 두 레이어 모두 첫 번째 이미지로 강제 일치
mainThumb.style.backgroundImage = firstImg;
fadeThumb.style.backgroundImage = firstImg;
// 4. 다음 호버를 위해 트랜지션 복구
setTimeout(() => {
fadeThumb.style.transition = 'opacity 0.3s ease-in-out';
}, 50);
}
if (indicator) updateIndicatorUI(indicator, 0);
}
// 중복 코드를 방지하기 위한 UI 업데이트 헬퍼
function updateIndicatorUI(indicator, activeIndex) {
Array.from(indicator.children).forEach((dot, i) => {
const isActive = i === activeIndex;
dot.style.backgroundColor = isActive ? 'white' : 'rgba(255, 255, 255, 0.4)';
dot.style.transform = isActive ? 'scale(1.2)' : 'scale(1)';
dot.style.opacity = isActive ? '1' : '0.7';
});
}
// 터치 상태 관리를 위한 변수
let touchStartX = 0;
let isDragging = false;
window.handleTouchStart = (e) => {
touchStartX = e.touches[0].clientX;
isDragging = false;
};
window.handleTouchMove = (e, productId) => {
const product = productsData.find((p) => p.id === productId);
if (!product || !product.images || product.images.length <= 1) return;
const cardElement = e.currentTarget;
const cardWidth = cardElement.offsetWidth;
const touchCurrentX = e.touches[0].clientX;
const diffX = touchStartX - touchCurrentX;
if (Math.abs(diffX) > 10) {
isDragging = true;
if (e.cancelable) e.preventDefault();
}
if (isDragging) {
const step = cardWidth / product.images.length;
let index = Math.floor(Math.abs(diffX) / step);
index = Math.max(0, Math.min(product.images.length - 1, index));
const mainThumb = document.getElementById(`thumb-${productId}`);
const fadeThumb = document.getElementById(`thumb-fade-${productId}`);
if (mainThumb && fadeThumb) {
// 드래그 중에는 페이드 없이 즉시 교체 (반응성 우선)
mainThumb.style.backgroundImage = `url("${product.images[index]}")`;
fadeThumb.style.opacity = '0'; // 페이드 레이어 숨김
updateIndicator(productId, index);
}
}
};
window.handleTouchEnd = (e, productId) => {
if (e.cancelable) e.preventDefault();
if (!isDragging) {
window.openModal(productId);
} else {
resetThumbnail(productId); // 드래그 종료 시 확실한 리셋
}
isDragging = false;
};
// 인디케이터 업데이트 헬퍼 함수
function updateIndicator(productId, index) {
const indicator = document.getElementById(`indicator-${productId}`);
if (indicator) {
Array.from(indicator.children).forEach((dot, i) => {
dot.style.backgroundColor = i === index ? 'white' : 'rgba(255, 255, 255, 0.4)';
dot.style.opacity = i === index ? '1' : '0.7';
});
}
}