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