Fix : 360 모바일 개선

This commit is contained in:
qsxft258@gmail.com 2025-09-17 22:45:06 +09:00
parent 95eaf73e30
commit ca88e829ad
2 changed files with 98 additions and 47 deletions

View File

@ -440,7 +440,6 @@
height: 100vh; height: 100vh;
background: rgba(0, 0, 0, 0.95); background: rgba(0, 0, 0, 0.95);
z-index: 2000; z-index: 2000;
backdrop-filter: blur(5px);
} }
.panorama-modal.active { .panorama-modal.active {
@ -515,7 +514,6 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
backdrop-filter: blur(10px);
z-index: 10; z-index: 10;
} }
@ -549,7 +547,6 @@
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 500; font-weight: 500;
transition: all 0.3s ease; transition: all 0.3s ease;
backdrop-filter: blur(10px);
min-width: 80px; min-width: 80px;
font-family: inherit; font-family: inherit;
} }
@ -594,7 +591,6 @@
justify-content: center; justify-content: center;
font-size: 20px; font-size: 20px;
transition: all 0.3s ease; transition: all 0.3s ease;
backdrop-filter: blur(10px);
z-index: 100; z-index: 100;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
@ -651,7 +647,6 @@
max-width: 500px; max-width: 500px;
width: 90%; width: 90%;
z-index: 200; z-index: 200;
backdrop-filter: blur(20px);
border: 2px solid rgba(255, 255, 255, 0.2); border: 2px solid rgba(255, 255, 255, 0.2);
display: none; display: none;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8);
@ -696,10 +691,8 @@
/* 서버 호환성을 위한 추가 스타일 */ /* 서버 호환성을 위한 추가 스타일 */
.panorama-modal { .panorama-modal {
/* 서버에서 배경 블러 지원 안될 때 대비 */ /* 깔끔한 배경 - 블러 효과 제거 */
background: rgba(0, 0, 0, 0.95) !important; background: rgba(0, 0, 0, 0.95) !important;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
} }
.panorama-viewer-container { .panorama-viewer-container {
@ -743,6 +736,30 @@
height: 250px; height: 250px;
} }
/* 모바일 360도 뷰어 컨테이너 최적화 - 경계 문제 해결 */
.panorama-viewer-container {
/* GPU 가속 활성화로 부드러운 렌더링 */
transform: translateZ(0);
-webkit-transform: translateZ(0);
will-change: transform;
/* 터치 시 확대/축소 제어 */
touch-action: pan-x pinch-zoom;
-webkit-overflow-scrolling: touch;
/* 경계 부분 매끄럽게 처리 */
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
/* 모바일 줌 시 이미지 품질 개선 */
.panorama-viewer-container img {
/* 확대 시에도 선명한 이미지 유지 */
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
/* 경계 부분 부드럽게 처리 */
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.panorama-modal-close { .panorama-modal-close {
width: 44px; width: 44px;
height: 44px; height: 44px;

View File

@ -375,22 +375,29 @@ class Easy360Viewer {
} }
setupImageSize() { setupImageSize() {
// 이미지 자연 어숙비를 유지하면서 컨테이너 높이에 맞춤 // 컨테이너 크기 가져오기
const containerHeight = this.container.clientHeight; const containerHeight = this.container.clientHeight;
const containerWidth = this.container.clientWidth; const containerWidth = this.container.clientWidth;
const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight; const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight;
// 360도 이미지는 매우 가로가 기므로 컨테이너보다 훨씬 커야 함 // 모바일에서 줌 시 경계 문제 해결을 위한 더 정확한 크기 계산
let imageWidth = containerHeight * imageAspectRatio; let imageHeight = containerHeight * this.zoom;
let imageWidth = imageHeight * imageAspectRatio;
// 컨테이너보다 작으면 컨테이너 너비에 맞춤 // 360도 이미지는 매우 가로가 길므로 최소 크기 보장
if (imageWidth < containerWidth * 2) { const minWidth = Math.max(containerWidth * 3, imageWidth);
imageWidth = containerWidth * 3; // 360도를 위해 충분히 큼 imageWidth = minWidth;
imageHeight = imageWidth / imageAspectRatio;
// 줌 레벨에 따른 크기 조정 (모바일 확대 시 경계 문제 해결)
if (this.zoom > 1) {
imageHeight = Math.max(containerHeight, imageHeight);
imageWidth = imageHeight * imageAspectRatio;
} }
// 이미지 크기 설정 - 브라우저 호환성 개선 // 이미지 크기 설정 - 픽셀 완벽 정렬로 경계 문제 방지
this.image.style.width = Math.round(imageWidth) + 'px'; this.image.style.width = Math.round(imageWidth) + 'px';
this.image.style.height = Math.round(containerHeight) + 'px'; this.image.style.height = Math.round(imageHeight) + 'px';
this.imageWrapper.style.width = Math.round(imageWidth) + 'px'; this.imageWrapper.style.width = Math.round(imageWidth) + 'px';
this.imageWrapper.style.height = Math.round(containerHeight) + 'px'; this.imageWrapper.style.height = Math.round(containerHeight) + 'px';
@ -557,8 +564,12 @@ class Easy360Viewer {
// 좌우 이동 (드래그 방향과 반대) // 좌우 이동 (드래그 방향과 반대)
this.currentX -= deltaX; this.currentX -= deltaX;
// 속도 계산 (관성용) // 속도 계산 (관성용) - 더 부드러운 관성 적용
this.velocity = -deltaX * 0.1; this.velocity = -deltaX * 0.15;
// 극한 속도 제한 (튀는 현상 방지)
const maxVelocity = this.singleImageWidth * 0.05;
this.velocity = Math.max(-maxVelocity, Math.min(maxVelocity, this.velocity));
this.updateTransform(); this.updateTransform();
this.lastX = currentX; this.lastX = currentX;
@ -583,11 +594,20 @@ class Easy360Viewer {
handleResize() { handleResize() {
if (this.image && this.image.complete) { if (this.image && this.image.complete) {
// 리사이즈 시 비율 유지 // 리사이즈 시 비율 유지하되, 튀는 현상 방지
const oldRatio = this.currentX / this.singleImageWidth; const oldRatio = (this.currentX - this.singleImageWidth) / this.singleImageWidth;
const oldImageWidth = this.singleImageWidth;
this.setupImageSize(); this.setupImageSize();
this.createDuplicateImages(); // 이미지 다시 생성 this.createDuplicateImages(); // 이미지 다시 생성
this.currentX = this.singleImageWidth * oldRatio; // 비율 유지
// 정규화된 위치로 복원 (중앙 이미지 기준)
this.currentX = this.singleImageWidth + (this.singleImageWidth * oldRatio);
// 경계값 확인 및 보정
if (this.currentX < 0) this.currentX += this.singleImageWidth;
if (this.currentX > this.singleImageWidth * 2) this.currentX -= this.singleImageWidth;
this.updateTransform(); this.updateTransform();
} }
} }
@ -595,16 +615,30 @@ class Easy360Viewer {
updateTransform() { updateTransform() {
if (!this.imageWrapper || !this.singleImageWidth) return; if (!this.imageWrapper || !this.singleImageWidth) return;
// 무한 스크롤 처리 - 더 부드럽게 // 무한 스크롤 처리 - 튀는 버그 방지를 위한 부드러운 전환
if (this.currentX <= 0) { const threshold = this.singleImageWidth * 0.1; // 10% 여유 공간
this.currentX = this.singleImageWidth;
} else if (this.currentX >= this.singleImageWidth * 2) { // 왼쪽 경계 처리 - 부드러운 전환
this.currentX = this.singleImageWidth; if (this.currentX < -threshold) {
this.currentX = this.singleImageWidth + (this.currentX + threshold);
}
// 오른쪽 경계 처리 - 부드러운 전환
else if (this.currentX > this.singleImageWidth * 2 + threshold) {
this.currentX = this.singleImageWidth + (this.currentX - this.singleImageWidth * 2 - threshold);
} }
// 부드러운 변환을 위해 소수점 제거 // 정확한 무한 루프 경계 처리 (튀는 현상 방지)
const normalizedX = ((this.currentX % this.singleImageWidth) + this.singleImageWidth) % this.singleImageWidth;
const actualX = normalizedX + this.singleImageWidth;
// 현재 위치와 목표 위치의 차이가 큰 경우만 보정 (부드러운 회전 유지)
if (Math.abs(this.currentX - actualX) > this.singleImageWidth * 0.5) {
this.currentX = actualX;
}
// 고정밀 변환 계산 (소수점 2자리까지 유지하여 부드러움 보장)
const translateX = Math.round(-this.currentX * 100) / 100; const translateX = Math.round(-this.currentX * 100) / 100;
const scale = Math.round(this.zoom * 100) / 100; const scale = Math.round(this.zoom * 1000) / 1000;
const transform = `translateX(${translateX}px) scale(${scale})`; const transform = `translateX(${translateX}px) scale(${scale})`;
this.imageWrapper.style.transform = transform; this.imageWrapper.style.transform = transform;