- 카드 전환 방식 변경
- 필터 조건 선택 가능하게 변경 등
This commit is contained in:
179
scripts/main.js
179
scripts/main.js
@@ -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';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user