여행 기록 프론트 오류 수정
This commit is contained in:
@@ -303,9 +303,13 @@
|
||||
|
||||
.travel-modal__frame {
|
||||
width: 100%;
|
||||
height: 68vh;
|
||||
max-height: 68vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
background: rgba(8, 10, 16, 0.3);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.travel-modal__image {
|
||||
@@ -313,7 +317,14 @@
|
||||
max-height: 68vh;
|
||||
object-fit: contain;
|
||||
border-radius: 14px;
|
||||
animation: travel-slide 260ms ease;
|
||||
}
|
||||
|
||||
.travel-modal__image.is-next {
|
||||
animation: travel-slide-next 280ms ease;
|
||||
}
|
||||
|
||||
.travel-modal__image.is-prev {
|
||||
animation: travel-slide-prev 280ms ease;
|
||||
}
|
||||
|
||||
.travel-modal__strip {
|
||||
@@ -387,14 +398,28 @@
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
@keyframes travel-slide {
|
||||
.travel-modal__arrow:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.4;
|
||||
transform: none;
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
@keyframes travel-slide-next {
|
||||
from {
|
||||
opacity: 0.6;
|
||||
transform: translateY(8px) scale(0.98);
|
||||
transform: translateX(20px) scale(0.98);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes travel-slide-prev {
|
||||
from {
|
||||
transform: translateX(-20px) scale(0.98);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,6 +434,7 @@
|
||||
}
|
||||
|
||||
.travel-modal__frame {
|
||||
height: 56vh;
|
||||
max-height: 56vh;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,9 @@ const Travel = () => {
|
||||
const [regionsGeojson, setRegionsGeojson] = useState(null);
|
||||
const [selectedPhotoIndex, setSelectedPhotoIndex] = useState(null);
|
||||
const [modalOffset, setModalOffset] = useState(24);
|
||||
const [touchStartX, setTouchStartX] = useState(null);
|
||||
const touchStartXRef = useRef(null);
|
||||
const [slideDirection, setSlideDirection] = useState('next');
|
||||
const [slideToken, setSlideToken] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [hasNext, setHasNext] = useState(true);
|
||||
const cacheRef = useRef(new Map());
|
||||
@@ -366,6 +368,28 @@ const Travel = () => {
|
||||
}
|
||||
}, [hasNext, loading, loadingMore, page, photoSummary, selectedRegion]);
|
||||
|
||||
const bumpSlide = (direction) => {
|
||||
setSlideDirection(direction);
|
||||
setSlideToken((prev) => prev + 1);
|
||||
};
|
||||
|
||||
const goPrev = useCallback(() => {
|
||||
if (selectedPhotoIndex === null || selectedPhotoIndex <= 0) return;
|
||||
bumpSlide('prev');
|
||||
setSelectedPhotoIndex(selectedPhotoIndex - 1);
|
||||
}, [selectedPhotoIndex]);
|
||||
|
||||
const goNext = useCallback(() => {
|
||||
if (
|
||||
selectedPhotoIndex === null ||
|
||||
selectedPhotoIndex >= photos.length - 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
bumpSlide('next');
|
||||
setSelectedPhotoIndex(selectedPhotoIndex + 1);
|
||||
}, [photos.length, selectedPhotoIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPhotoIndex === null) return undefined;
|
||||
|
||||
@@ -375,45 +399,34 @@ const Travel = () => {
|
||||
return;
|
||||
}
|
||||
if (event.key === 'ArrowLeft') {
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null
|
||||
? prev
|
||||
: (prev - 1 + photos.length) % photos.length
|
||||
);
|
||||
goPrev();
|
||||
}
|
||||
if (event.key === 'ArrowRight') {
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null ? prev : (prev + 1) % photos.length
|
||||
);
|
||||
goNext();
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchStart = (event) => {
|
||||
if (selectedPhotoIndex === null) return;
|
||||
const touch = event.touches[0];
|
||||
setTouchStartX(touch.clientX);
|
||||
touchStartXRef.current = touch.clientX;
|
||||
};
|
||||
|
||||
const handleTouchEnd = (event) => {
|
||||
if (selectedPhotoIndex === null || touchStartX === null) return;
|
||||
if (selectedPhotoIndex === null || touchStartXRef.current === null)
|
||||
return;
|
||||
const touch = event.changedTouches[0];
|
||||
const deltaX = touch.clientX - touchStartX;
|
||||
const deltaX = touch.clientX - touchStartXRef.current;
|
||||
if (Math.abs(deltaX) > 50) { // 스와이프 거리 임계값
|
||||
if (deltaX > 0) {
|
||||
// 왼쪽으로 스와이프: 이전 사진
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null
|
||||
? prev
|
||||
: (prev - 1 + photos.length) % photos.length
|
||||
);
|
||||
goPrev();
|
||||
} else {
|
||||
// 오른쪽으로 스와이프: 다음 사진
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null ? prev : (prev + 1) % photos.length
|
||||
);
|
||||
goNext();
|
||||
}
|
||||
}
|
||||
setTouchStartX(null);
|
||||
touchStartXRef.current = null;
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
@@ -424,7 +437,7 @@ const Travel = () => {
|
||||
window.removeEventListener('touchstart', handleTouchStart);
|
||||
window.removeEventListener('touchend', handleTouchEnd);
|
||||
};
|
||||
}, [photos.length, selectedPhotoIndex]);
|
||||
}, [goNext, goPrev, selectedPhotoIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPhotoIndex === null) return undefined;
|
||||
@@ -441,6 +454,11 @@ const Travel = () => {
|
||||
: getStripRange(photos.length, selectedPhotoIndex);
|
||||
|
||||
const handleSelectPhoto = (index, event) => {
|
||||
if (selectedPhotoIndex === null) {
|
||||
bumpSlide('next');
|
||||
} else if (index !== selectedPhotoIndex) {
|
||||
bumpSlide(index > selectedPhotoIndex ? 'next' : 'prev');
|
||||
}
|
||||
if (event) {
|
||||
const pointY =
|
||||
typeof event.clientY === 'number'
|
||||
@@ -571,26 +589,20 @@ const Travel = () => {
|
||||
<button
|
||||
type="button"
|
||||
className="travel-modal__arrow is-prev"
|
||||
onClick={() =>
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null
|
||||
? prev
|
||||
: (prev - 1 + photos.length) %
|
||||
photos.length
|
||||
)
|
||||
}
|
||||
onClick={goPrev}
|
||||
disabled={selectedPhotoIndex === 0}
|
||||
aria-label="Previous"
|
||||
>
|
||||
{'<'}
|
||||
</button>
|
||||
<div className="travel-modal__frame">
|
||||
<img
|
||||
key={
|
||||
photos[selectedPhotoIndex]?.original ||
|
||||
photos[selectedPhotoIndex]?.src ||
|
||||
selectedPhotoIndex
|
||||
}
|
||||
className="travel-modal__image"
|
||||
key={`${selectedPhotoIndex}-${slideToken}`}
|
||||
className={`travel-modal__image ${
|
||||
slideDirection === 'prev'
|
||||
? 'is-prev'
|
||||
: 'is-next'
|
||||
}`}
|
||||
src={
|
||||
photos[selectedPhotoIndex]?.original ||
|
||||
photos[selectedPhotoIndex]?.src
|
||||
@@ -609,12 +621,9 @@ const Travel = () => {
|
||||
<button
|
||||
type="button"
|
||||
className="travel-modal__arrow is-next"
|
||||
onClick={() =>
|
||||
setSelectedPhotoIndex((prev) =>
|
||||
prev === null
|
||||
? prev
|
||||
: (prev + 1) % photos.length
|
||||
)
|
||||
onClick={goNext}
|
||||
disabled={
|
||||
selectedPhotoIndex === photos.length - 1
|
||||
}
|
||||
aria-label="Next"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user