From 44cb0c302ba7530e9b01db38908eeea3f66dcd9d Mon Sep 17 00:00:00 2001 From: "qsxft258@gmail.com" Date: Wed, 17 Sep 2025 22:21:10 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=EA=B0=A4=EB=9F=AC=EB=A6=AC=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/gallery.css | 104 +++++++++++++++++++++++++++++++++++++++++------- js/gallery.js | 61 +++++++++++++++++----------- 2 files changed, 127 insertions(+), 38 deletions(-) diff --git a/css/gallery.css b/css/gallery.css index efb69c9..e589f8e 100644 --- a/css/gallery.css +++ b/css/gallery.css @@ -552,37 +552,65 @@ text-align: center; } -/* 새로운 닫기 버튼 */ +/* 예쁜 X 버튼 */ .panorama-modal-close { position: absolute; top: var(--spacing-xl); right: var(--spacing-xl); background: rgba(0, 0, 0, 0.8); color: var(--text-white); - width: 60px; - height: 60px; + width: 50px; + height: 50px; border-radius: 50%; - border: 3px solid rgba(255, 255, 255, 0.3); + border: 2px solid rgba(255, 255, 255, 0.3); cursor: pointer; display: flex; align-items: center; justify-content: center; - font-size: var(--font-2xl); + font-size: 20px; transition: all 0.3s ease; backdrop-filter: blur(10px); z-index: 100; + font-family: Arial, sans-serif; } .panorama-modal-close:hover { background: var(--primary-color); border-color: var(--primary-color); - transform: scale(1.1) rotate(90deg); + transform: scale(1.1) rotate(180deg); box-shadow: 0 8px 20px rgba(255, 136, 0, 0.4); } -.panorama-modal-close::before { - content: '✕'; - font-weight: bold; +.panorama-modal-close:active { + transform: scale(0.95); +} + +/* X 모양 스타일링 */ +.panorama-modal-close .close-icon { + position: relative; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; +} + +.panorama-modal-close .close-icon::before, +.panorama-modal-close .close-icon::after { + content: ''; + position: absolute; + width: 18px; + height: 2px; + background: currentColor; + border-radius: 1px; +} + +.panorama-modal-close .close-icon::before { + transform: rotate(45deg); +} + +.panorama-modal-close .close-icon::after { + transform: rotate(-45deg); } /* 새로운 도움말 패널 */ @@ -640,7 +668,48 @@ line-height: 1.5; } -/* 기존 스타일 제거됨 - 새로운 도움말 패널로 대체 */ +/* 서버 호환성을 위한 추가 스타일 */ +.panorama-modal { + /* 서버에서 배경 블러 지원 안될 때 대비 */ + background: rgba(0, 0, 0, 0.95) !important; + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); +} + +.panorama-viewer-container { + /* GPU 가속 및 성능 최적화 */ + transform: translateZ(0); + -webkit-transform: translateZ(0); + will-change: transform; +} + +/* 로딩 스피너 개선 */ +.panorama-loader .spinner { + border: 3px solid rgba(255, 255, 255, 0.3); + border-top: 3px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* 폰트 대비책 */ +.panorama-control-buttons { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +/* 모바일 최적화 */ +@media (max-width: 768px) { + .panorama-modal-close { + width: 44px; + height: 44px; + top: 20px; + right: 20px; + } + + .panorama-modal-close .close-icon::before, + .panorama-modal-close .close-icon::after { + width: 16px; + } +} /* 반응형 디자인 */ @media (max-width: 768px) { @@ -789,11 +858,16 @@ } .panorama-modal-close { - width: 50px; - height: 50px; - font-size: var(--font-lg); - top: var(--spacing-md); - right: var(--spacing-md); + width: 40px; + height: 40px; + top: 15px; + right: 15px; + } + + .panorama-modal-close .close-icon::before, + .panorama-modal-close .close-icon::after { + width: 14px; + height: 2px; } .panorama-help-panel { diff --git a/js/gallery.js b/js/gallery.js index b15ecfe..b5b41ab 100644 --- a/js/gallery.js +++ b/js/gallery.js @@ -317,7 +317,7 @@ class Easy360Viewer { user-select: none; `; - // 360도 이미지 래퍼 + // 360도 이미지 래퍼 - 성능 최적화 this.imageWrapper = document.createElement('div'); this.imageWrapper.style.cssText = ` position: absolute; @@ -327,12 +327,14 @@ class Easy360Viewer { will-change: transform; display: flex; align-items: center; + transform-origin: left center; + backface-visibility: hidden; `; // 로딩 표시 this.showLoading(); - // 메인 360도 이미지 + // 메인 360도 이미지 - 고해상도 설정 this.image = document.createElement('img'); this.image.src = this.imageSrc; this.image.style.cssText = ` @@ -342,6 +344,9 @@ class Easy360Viewer { user-select: none; pointer-events: none; display: block; + image-rendering: -webkit-optimize-contrast; + image-rendering: crisp-edges; + filter: contrast(1.05) saturate(1.1); `; this.image.draggable = false; @@ -365,14 +370,25 @@ class Easy360Viewer { setupImageSize() { // 이미지 자연 어숙비를 유지하면서 컨테이너 높이에 맞춤 const containerHeight = this.container.clientHeight; + const containerWidth = this.container.clientWidth; const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight; - const imageWidth = containerHeight * imageAspectRatio; - // 360도 이미지는 보통 매우 가로가 김 + // 360도 이미지는 매우 가로가 기므로 컨테이너보다 훨씬 커야 함 + let imageWidth = containerHeight * imageAspectRatio; + + // 컨테이너보다 작으면 컨테이너 너비에 맞춤 + if (imageWidth < containerWidth * 2) { + imageWidth = containerWidth * 3; // 360도를 위해 충분히 큼 + } + + // 이미지 크기 설정 + this.image.style.width = imageWidth + 'px'; + this.image.style.height = containerHeight + 'px'; this.imageWrapper.style.width = imageWidth + 'px'; + this.imageWrapper.style.height = containerHeight + 'px'; - // 스크롤 가능 범위 계산 - this.maxX = Math.max(0, imageWidth - this.container.clientWidth); + // 단일 이미지 너비 저장 + this.singleImageWidth = imageWidth; } createDuplicateImages() { @@ -380,21 +396,18 @@ class Easy360Viewer { const imageClone1 = this.image.cloneNode(); const imageClone2 = this.image.cloneNode(); + // 동일한 스타일 적용 imageClone1.style.cssText = this.image.style.cssText; imageClone2.style.cssText = this.image.style.cssText; this.imageWrapper.appendChild(imageClone1); this.imageWrapper.appendChild(imageClone2); - // 래퍼 전체 너비를 3배로 - const currentWidth = parseInt(this.imageWrapper.style.width); - this.imageWrapper.style.width = (currentWidth * 3) + 'px'; + // 래퍼 전체 너비를 3배로 설정 + this.imageWrapper.style.width = (this.singleImageWidth * 3) + 'px'; - // 스크롤 범위 업데이트 - this.maxX = currentWidth * 2; - - // 시작 위치를 중앙 이미지로 - this.currentX = currentWidth; + // 시작 위치를 중앙 이미지로 (두 번째 이미지) + this.currentX = this.singleImageWidth; this.updateTransform(); } @@ -559,21 +572,23 @@ class Easy360Viewer { handleResize() { if (this.image && this.image.complete) { + // 리사이즈 시 비율 유지 + const oldRatio = this.currentX / this.singleImageWidth; this.setupImageSize(); + this.createDuplicateImages(); // 이미지 다시 생성 + this.currentX = this.singleImageWidth * oldRatio; // 비율 유지 this.updateTransform(); } } updateTransform() { - if (!this.imageWrapper) return; + if (!this.imageWrapper || !this.singleImageWidth) return; - // 무한 스크롤 처리 - const singleImageWidth = parseInt(this.imageWrapper.style.width) / 3; - - if (this.currentX < 0) { - this.currentX += singleImageWidth; - } else if (this.currentX > singleImageWidth * 2) { - this.currentX -= singleImageWidth; + // 무한 스크롤 처리 - 더 부드럽게 + if (this.currentX <= 0) { + this.currentX = this.singleImageWidth; + } else if (this.currentX >= this.singleImageWidth * 2) { + this.currentX = this.singleImageWidth; } const transform = `translateX(${-this.currentX}px) scale(${this.zoom})`; @@ -602,7 +617,7 @@ class Easy360Viewer { } reset() { - this.currentX = parseInt(this.imageWrapper.style.width) / 3; // 중앙 이미지로 + this.currentX = this.singleImageWidth; // 중앙 이미지로 this.zoom = 1; this.velocity = 0; this.stopAutoRotate();