From 243e3b3bc080efc05cfebd9cbe7fc095a61a91bc Mon Sep 17 00:00:00 2001 From: "qsxft258@gmail.com" Date: Wed, 17 Sep 2025 22:18:02 +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 --- .claude/settings.local.json | 4 +- css/gallery.css | 285 +++++++++------ gallery.html | 2 - js/gallery.js | 709 ++++++++++++++++++++++++++---------- 4 files changed, 705 insertions(+), 295 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 485bc5b..3d73aad 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -7,7 +7,9 @@ "Bash(sed:*)", "Bash(sort:*)", "WebFetch(domain:studio.v-llage.com)", - "Bash(awk:*)" + "Bash(awk:*)", + "Bash(python:*)", + "Bash(start http://localhost:8000/gallery.html)" ], "deny": [], "ask": [] diff --git a/css/gallery.css b/css/gallery.css index bd187b3..efb69c9 100644 --- a/css/gallery.css +++ b/css/gallery.css @@ -412,7 +412,7 @@ 20%, 80% { opacity: 1; } } -/* 개선된 360도 이미지 전체화면 모달 */ +/* 새로운 360도 파노라마 모달 */ .panorama-modal { display: none; position: fixed; @@ -422,7 +422,7 @@ height: 100vh; background: rgba(0, 0, 0, 0.95); z-index: 2000; - cursor: grab; + backdrop-filter: blur(5px); } .panorama-modal.active { @@ -431,111 +431,153 @@ justify-content: center; } -.panorama-modal:active { - cursor: grabbing; -} - .panorama-modal-content { position: relative; - width: 90vw; - height: 70vh; - max-width: 1200px; + width: 95vw; + height: 90vh; + max-width: 1400px; border-radius: var(--border-radius); overflow: hidden; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.7); + background: #000; } -.panorama-modal-viewer { +/* 새로운 360도 뷰어 컨테이너 */ +.panorama-viewer-container { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: var(--border-radius); background: #000; + cursor: grab; } -.panorama-modal-image { +.panorama-viewer-container:active { + cursor: grabbing; +} + +/* 360도 뷰어 로딩 스피너 */ +.panorama-loader { position: absolute; - top: 0; - left: 0; - height: 100%; - object-fit: cover; - transition: transform 0.1s ease-out; - will-change: transform; - user-select: none; - pointer-events: none; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + color: var(--text-white); + z-index: 10; } +.panorama-loader .spinner { + width: 50px; + height: 50px; + border: 3px solid rgba(255, 255, 255, 0.3); + border-top: 3px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 15px; +} + +.panorama-loader p { + font-size: var(--font-base); + opacity: 0.8; + margin: 0; +} + +/* 새로운 모달 컨트롤 */ .panorama-modal-controls { position: absolute; bottom: 0; left: 0; right: 0; background: linear-gradient(transparent, rgba(0, 0, 0, 0.9)); - padding: var(--spacing-xl); + padding: var(--spacing-xl) var(--spacing-2xl); display: flex; justify-content: space-between; align-items: center; + backdrop-filter: blur(10px); } .panorama-modal-title { color: var(--text-white); - font-weight: 600; - font-size: var(--font-xl); + font-weight: 700; + font-size: var(--font-2xl); + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } -.panorama-modal-buttons { +/* 새로운 컨트롤 버튼 스타일 */ +.panorama-control-buttons { display: flex; - gap: var(--spacing-md); + gap: var(--spacing-lg); + flex-wrap: wrap; } -.panorama-modal-btn { - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.3); +.panorama-btn { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--spacing-xs); + background: rgba(255, 255, 255, 0.15); + border: 2px solid rgba(255, 255, 255, 0.3); color: var(--text-white); - padding: var(--spacing-sm) var(--spacing-lg); + padding: var(--spacing-md) var(--spacing-lg); border-radius: var(--border-radius); cursor: pointer; - font-size: var(--font-base); - transition: var(--transition); - backdrop-filter: blur(10px); + font-size: var(--font-sm); font-weight: 500; + transition: all 0.3s ease; + backdrop-filter: blur(10px); + min-width: 80px; } -.panorama-modal-btn:hover { +.panorama-btn:hover { background: var(--primary-color); border-color: var(--primary-color); - transform: translateY(-2px); + transform: translateY(-3px) scale(1.05); + box-shadow: 0 8px 20px rgba(255, 136, 0, 0.4); } -.panorama-modal-btn.active { +.panorama-btn.active { background: var(--primary-color); border-color: var(--primary-color); + box-shadow: 0 4px 15px rgba(255, 136, 0, 0.3); } +.panorama-btn i { + font-size: var(--font-lg); +} + +.panorama-btn span { + font-size: var(--font-xs); + text-align: center; +} + +/* 새로운 닫기 버튼 */ .panorama-modal-close { position: absolute; - top: var(--spacing-lg); - right: var(--spacing-lg); - background: rgba(0, 0, 0, 0.7); + top: var(--spacing-xl); + right: var(--spacing-xl); + background: rgba(0, 0, 0, 0.8); color: var(--text-white); - width: 50px; - height: 50px; + width: 60px; + height: 60px; border-radius: 50%; - border: 2px solid rgba(255, 255, 255, 0.3); + border: 3px solid rgba(255, 255, 255, 0.3); cursor: pointer; display: flex; align-items: center; justify-content: center; - font-size: var(--font-xl); - transition: var(--transition); + font-size: var(--font-2xl); + transition: all 0.3s ease; backdrop-filter: blur(10px); + z-index: 100; } .panorama-modal-close:hover { background: var(--primary-color); border-color: var(--primary-color); - transform: scale(1.1); + transform: scale(1.1) rotate(90deg); + box-shadow: 0 8px 20px rgba(255, 136, 0, 0.4); } .panorama-modal-close::before { @@ -543,60 +585,63 @@ font-weight: bold; } -.panorama-modal-help-text { +/* 새로운 도움말 패널 */ +.panorama-help-panel { position: absolute; - top: var(--spacing-xl); - right: var(--spacing-xl); - background: rgba(0, 0, 0, 0.9); - color: var(--text-white); - padding: var(--spacing-lg); - border-radius: var(--border-radius); - font-size: var(--font-sm); - max-width: 250px; - z-index: 10; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - display: none; -} - -.panorama-modal-help-text p { - margin: 0 0 var(--spacing-xs) 0; -} - -.panorama-modal-help-text strong { - color: var(--primary-color); - font-size: var(--font-base); -} - -.panorama-modal-help { - position: absolute; - top: var(--spacing-lg); - left: var(--spacing-lg); - background: rgba(255, 136, 0, 0.9); - color: var(--text-white); - padding: var(--spacing-sm) var(--spacing-lg); - border-radius: var(--border-radius); - font-size: var(--font-base); - font-weight: 500; - animation: fadeInOut 4s ease-in-out; - backdrop-filter: blur(10px); -} - -.panorama-modal-indicator { - position: absolute; - top: var(--spacing-lg); + top: 50%; left: 50%; - transform: translateX(-50%); - background: rgba(0, 0, 0, 0.7); + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.95); color: var(--text-white); - padding: var(--spacing-sm) var(--spacing-lg); - border-radius: var(--border-radius-full); - font-size: var(--font-base); - font-weight: 600; - border: 2px solid rgba(255, 255, 255, 0.3); - backdrop-filter: blur(10px); + padding: var(--spacing-2xl); + border-radius: var(--border-radius-lg); + max-width: 500px; + width: 90%; + z-index: 200; + backdrop-filter: blur(20px); + border: 2px solid rgba(255, 255, 255, 0.2); + display: none; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8); } +.panorama-help-panel h3 { + color: var(--primary-color); + font-size: var(--font-xl); + margin: 0 0 var(--spacing-lg) 0; + text-align: center; + font-weight: 700; +} + +.help-content { + display: flex; + flex-direction: column; + gap: var(--spacing-md); +} + +.help-item { + display: flex; + align-items: center; + gap: var(--spacing-md); + padding: var(--spacing-sm); + background: rgba(255, 255, 255, 0.05); + border-radius: var(--border-radius); + border-left: 3px solid var(--primary-color); +} + +.help-item i { + color: var(--primary-color); + font-size: var(--font-lg); + width: 24px; + text-align: center; +} + +.help-item span { + font-size: var(--font-base); + line-height: 1.5; +} + +/* 기존 스타일 제거됨 - 새로운 도움말 패널로 대체 */ + /* 반응형 디자인 */ @media (max-width: 768px) { .page-header h1 { @@ -709,36 +754,64 @@ } .panorama-modal-content { - width: 95vw; - height: 60vh; + width: 98vw; + height: 85vh; } .panorama-modal-controls { padding: var(--spacing-lg); + flex-direction: column; + gap: var(--spacing-md); + align-items: center; } .panorama-modal-title { + font-size: var(--font-lg); + text-align: center; + } + + .panorama-control-buttons { + gap: var(--spacing-sm); + justify-content: center; + } + + .panorama-btn { + min-width: 70px; + padding: var(--spacing-sm) var(--spacing-md); + } + + .panorama-btn i { font-size: var(--font-base); } - .panorama-modal-btn { - font-size: var(--font-sm); - padding: var(--spacing-xs) var(--spacing-md); + .panorama-btn span { + font-size: 10px; } .panorama-modal-close { - width: 40px; - height: 40px; - font-size: var(--font-base); + width: 50px; + height: 50px; + font-size: var(--font-lg); + top: var(--spacing-md); + right: var(--spacing-md); } - .panorama-modal-help { - font-size: var(--font-sm); - padding: var(--spacing-xs) var(--spacing-md); + .panorama-help-panel { + width: 95%; + padding: var(--spacing-lg); + max-width: none; } - .panorama-modal-indicator { + .panorama-help-panel h3 { + font-size: var(--font-lg); + } + + .help-item { + gap: var(--spacing-sm); + padding: var(--spacing-xs); + } + + .help-item span { font-size: var(--font-sm); - padding: var(--spacing-xs) var(--spacing-md); } } \ No newline at end of file diff --git a/gallery.html b/gallery.html index b87d8a4..6fa507d 100644 --- a/gallery.html +++ b/gallery.html @@ -40,8 +40,6 @@ - - diff --git a/js/gallery.js b/js/gallery.js index 3a1b63e..b15ecfe 100644 --- a/js/gallery.js +++ b/js/gallery.js @@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', function() { initGallery(); initLightbox(); initGalleryAnimations(); - initAFrame360Viewers(); + initCustom360Viewers(); }); // 갤러리 초기화 @@ -267,268 +267,605 @@ function handleSwipe() { } // ======================================== -// A-Frame 기반 360도 VR 뷰어 - 안정적이고 강력한 VR 체험 +// 간단한 360도 파노라마 뷰어 - 좌우 스크롤 방식 // ======================================== -let currentAFrameScene = null; -let autoRotationInterval = null; -let isAutoRotating = false; - -function initAFrame360Viewers() { - // 모달 생성 - createAFrameModal(); +class Easy360Viewer { + constructor(container, imageSrc, title) { + this.container = container; + this.imageSrc = imageSrc; + this.title = title; + + // 뷰어 상태 + this.currentX = 0; // 현재 X 위치 + this.maxX = 0; // 최대 스크롤 가능 X + this.zoom = 1; + + // 마우스/터치 상태 + this.isDragging = false; + this.startX = 0; + this.lastX = 0; + this.velocity = 0; + + // 자동 회전 + this.isAutoRotating = false; + this.autoRotateSpeed = 1; + this.animationId = null; + + // 터치 줌 + this.touchCount = 0; + this.lastPinchDistance = 0; + + this.init(); + } - // 미리보기 설정 - initAFramePreviews(); + init() { + this.createViewer(); + this.bindEvents(); + this.startAnimation(); + } + + createViewer() { + // 컨테이너 스타일 + this.container.style.cssText = ` + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + background: #000; + cursor: grab; + user-select: none; + `; + + // 360도 이미지 래퍼 + this.imageWrapper = document.createElement('div'); + this.imageWrapper.style.cssText = ` + position: absolute; + top: 0; + left: 0; + height: 100%; + will-change: transform; + display: flex; + align-items: center; + `; + + // 로딩 표시 + this.showLoading(); + + // 메인 360도 이미지 + this.image = document.createElement('img'); + this.image.src = this.imageSrc; + this.image.style.cssText = ` + height: 100%; + width: auto; + object-fit: cover; + user-select: none; + pointer-events: none; + display: block; + `; + this.image.draggable = false; + + // 이미지 로드 완료 후 + this.image.onload = () => { + this.hideLoading(); + this.setupImageSize(); + this.createDuplicateImages(); + this.updateTransform(); + }; + + this.image.onerror = () => { + this.hideLoading(); + this.showError(); + }; + + this.imageWrapper.appendChild(this.image); + this.container.appendChild(this.imageWrapper); + } + + setupImageSize() { + // 이미지 자연 어숙비를 유지하면서 컨테이너 높이에 맞춤 + const containerHeight = this.container.clientHeight; + const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight; + const imageWidth = containerHeight * imageAspectRatio; + + // 360도 이미지는 보통 매우 가로가 김 + this.imageWrapper.style.width = imageWidth + 'px'; + + // 스크롤 가능 범위 계산 + this.maxX = Math.max(0, imageWidth - this.container.clientWidth); + } + + createDuplicateImages() { + // 무한 스크롤을 위해 이미지 복사본 생성 + 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'; + + // 스크롤 범위 업데이트 + this.maxX = currentWidth * 2; + + // 시작 위치를 중앙 이미지로 + this.currentX = currentWidth; + this.updateTransform(); + } + + showLoading() { + const loader = document.createElement('div'); + loader.className = 'panorama-loader'; + loader.innerHTML = ` +
+

360° 이미지 로딩중...

+ `; + loader.style.cssText = ` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + color: #fff; + z-index: 10; + pointer-events: none; + `; + + this.container.appendChild(loader); + this.loadingElement = loader; + } + + hideLoading() { + if (this.loadingElement) { + this.loadingElement.remove(); + this.loadingElement = null; + } + } + + showError() { + const errorElement = document.createElement('div'); + errorElement.innerHTML = ` +
+
😞
+
이미지를 불러올 수 없습니다
+
이미지 파일을 확인해주세요
+
+ `; + errorElement.style.cssText = ` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10; + `; + + this.container.appendChild(errorElement); + } + + bindEvents() { + // 마우스 이벤트 + this.container.addEventListener('mousedown', this.handleStart.bind(this)); + this.container.addEventListener('mousemove', this.handleMove.bind(this)); + this.container.addEventListener('mouseup', this.handleEnd.bind(this)); + this.container.addEventListener('mouseleave', this.handleEnd.bind(this)); + + // 터치 이벤트 + this.container.addEventListener('touchstart', this.handleStart.bind(this), { passive: false }); + this.container.addEventListener('touchmove', this.handleMove.bind(this), { passive: false }); + this.container.addEventListener('touchend', this.handleEnd.bind(this)); + + // 휠 이벤트 (좌우 스크롤) + this.container.addEventListener('wheel', this.handleWheel.bind(this), { passive: false }); + + // 컨텍스트 메뉴 방지 + this.container.addEventListener('contextmenu', e => e.preventDefault()); + + // 리사이즈 + window.addEventListener('resize', this.handleResize.bind(this)); + } + + handleStart(e) { + e.preventDefault(); + this.isDragging = true; + this.velocity = 0; + this.container.style.cursor = 'grabbing'; + this.stopAutoRotate(); + + if (e.touches) { + this.touchCount = e.touches.length; + + if (this.touchCount === 1) { + this.startX = e.touches[0].clientX; + } else if (this.touchCount === 2) { + // 핀치 줌 초기화 + const dx = e.touches[0].clientX - e.touches[1].clientX; + const dy = e.touches[0].clientY - e.touches[1].clientY; + this.lastPinchDistance = Math.sqrt(dx * dx + dy * dy); + return; // 핀치일 때는 드래그 비활성화 + } + } else { + this.startX = e.clientX; + } + + this.lastX = this.startX; + } + + handleMove(e) { + if (!this.isDragging) return; + e.preventDefault(); + + let currentX; + + if (e.touches) { + if (this.touchCount === 1 && e.touches.length === 1) { + currentX = e.touches[0].clientX; + this.updatePosition(currentX); + + } else if (this.touchCount === 2 && e.touches.length === 2) { + // 핀치 줌 처리 + const dx = e.touches[0].clientX - e.touches[1].clientX; + const dy = e.touches[0].clientY - e.touches[1].clientY; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (this.lastPinchDistance > 0) { + const scale = distance / this.lastPinchDistance; + this.zoom = Math.max(0.8, Math.min(2.5, this.zoom * scale)); + this.updateTransform(); + } + + this.lastPinchDistance = distance; + return; + } + } else { + currentX = e.clientX; + this.updatePosition(currentX); + } + } + + updatePosition(currentX) { + const deltaX = currentX - this.lastX; + + // 좌우 이동 (드래그 방향과 반대) + this.currentX -= deltaX; + + // 속도 계산 (관성용) + this.velocity = -deltaX * 0.1; + + this.updateTransform(); + this.lastX = currentX; + } + + handleEnd() { + this.isDragging = false; + this.touchCount = 0; + this.lastPinchDistance = 0; + this.container.style.cursor = 'grab'; + } + + handleWheel(e) { + e.preventDefault(); + + // 휠로 좌우 스크롤 + const scrollSpeed = 50; + this.currentX += e.deltaY > 0 ? scrollSpeed : -scrollSpeed; + + this.updateTransform(); + } + + handleResize() { + if (this.image && this.image.complete) { + this.setupImageSize(); + this.updateTransform(); + } + } + + updateTransform() { + if (!this.imageWrapper) 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; + } + + const transform = `translateX(${-this.currentX}px) scale(${this.zoom})`; + this.imageWrapper.style.transform = transform; + } + + startAnimation() { + const animate = () => { + // 관성 적용 + if (!this.isDragging && Math.abs(this.velocity) > 0.5) { + this.currentX += this.velocity; + this.velocity *= 0.95; // 마찰력 + this.updateTransform(); + } + + // 자동 회전 + if (this.isAutoRotating && !this.isDragging) { + this.currentX += this.autoRotateSpeed; + this.updateTransform(); + } + + this.animationId = requestAnimationFrame(animate); + }; + + animate(); + } + + reset() { + this.currentX = parseInt(this.imageWrapper.style.width) / 3; // 중앙 이미지로 + this.zoom = 1; + this.velocity = 0; + this.stopAutoRotate(); + this.updateTransform(); + } + + startAutoRotate() { + this.isAutoRotating = true; + } + + stopAutoRotate() { + this.isAutoRotating = false; + } + + zoomIn() { + this.zoom = Math.min(2.5, this.zoom * 1.2); + this.updateTransform(); + } + + zoomOut() { + this.zoom = Math.max(0.8, this.zoom * 0.8); + this.updateTransform(); + } + + destroy() { + this.stopAutoRotate(); + + if (this.animationId) { + cancelAnimationFrame(this.animationId); + } + + window.removeEventListener('resize', this.handleResize.bind(this)); + + if (this.container) { + this.container.innerHTML = ''; + } + } } -function initAFramePreviews() { - const containers = document.querySelectorAll('.panorama-clickable'); +let current360Viewer = null; + +function initCustom360Viewers() { + createPanoramaModal(); + initPanoramaPreviews(); +} + +function initPanoramaPreviews() { + const clickableElements = document.querySelectorAll('.panorama-clickable'); - containers.forEach(container => { - const imageSrc = container.dataset.image; - const title = container.dataset.title; + clickableElements.forEach(element => { + const imageSrc = element.dataset.image; + const title = element.dataset.title; - // 클릭 이벤트 - container.addEventListener('click', () => { - openAFrameModal(imageSrc, title); + element.addEventListener('click', () => { + openPanoramaModal(imageSrc, title); }); - // 도움말 자동 숨김 + // 호버 효과 + element.addEventListener('mouseenter', () => { + element.style.transform = 'scale(1.02)'; + }); + + element.addEventListener('mouseleave', () => { + element.style.transform = 'scale(1)'; + }); + + // 도움말 자동 사라짐 setTimeout(() => { - const help = container.querySelector('.panorama-help'); - if (help) { - help.style.opacity = '0'; - setTimeout(() => help.remove(), 1000); + const helpText = element.querySelector('.panorama-help'); + if (helpText) { + helpText.style.opacity = '0'; + setTimeout(() => helpText.remove(), 500); } }, 4000); }); } -function createAFrameModal() { +function createPanoramaModal() { const modalHTML = ` -
+
-
- +
+
+
-
-
- - - - +
- -
-

360° VR 조작법

-

🖱️ 드래그: 전방향 회전

-

📱 터치: 터치 후 드래그

-

🖱️ 휠: 줌 인/아웃

-

📱 핀치: 줌 인/아웃

-

⌨️ WASD: 이동

-

📱 VR모드: VR 헤드셋 지원

-

⌨️ ESC: 닫기

+ +
+

360° 조작 가이드

+
+
+ + 마우스 드래그로 화면을 회전시킬 수 있습니다 +
+
+ + 터치 스크린에서는 손가락으로 드래그하세요 +
+
+ + 마우스 휠이나 핀치로 확대/축소할 수 있습니다 +
+
+ + 자동회전 버튼으로 자동으로 둘러볼 수 있습니다 +
+
+ + ESC 키를 눌러 닫을 수 있습니다 +
+
`; document.body.insertAdjacentHTML('beforeend', modalHTML); - setupAFrameModalListeners(); + setupPanoramaModalEvents(); } -function setupAFrameModalListeners() { - const modal = document.getElementById('aframe-modal'); - const closeBtn = document.getElementById('aframe-modal-close'); - const resetBtn = document.getElementById('aframe-reset-btn'); - const autoBtn = document.getElementById('aframe-auto-btn'); - const vrBtn = document.getElementById('aframe-vr-btn'); - const helpBtn = document.getElementById('aframe-help-btn'); - const helpText = document.getElementById('aframe-help-text'); +function setupPanoramaModalEvents() { + const modal = document.getElementById('panorama-modal'); + const closeBtn = document.getElementById('panorama-close-btn'); + const resetBtn = document.getElementById('panorama-reset-btn'); + const autoBtn = document.getElementById('panorama-auto-btn'); + const zoomInBtn = document.getElementById('panorama-zoom-in-btn'); + const zoomOutBtn = document.getElementById('panorama-zoom-out-btn'); + const helpBtn = document.getElementById('panorama-help-btn'); + const helpPanel = document.getElementById('panorama-help-panel'); - let helpVisible = false; + let isAutoRotating = false; + let isHelpVisible = false; // 모달 닫기 - closeBtn.addEventListener('click', closeAFrameModal); + closeBtn.addEventListener('click', closePanoramaModal); modal.addEventListener('click', (e) => { - if (e.target === modal) closeAFrameModal(); + if (e.target === modal) closePanoramaModal(); }); - // ESC 키 + // ESC 키로 닫기 document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal.classList.contains('active')) { - closeAFrameModal(); + closePanoramaModal(); } }); - // 리셋 버튼 + // 초기화 버튼 resetBtn.addEventListener('click', () => { - if (currentAFrameScene) { - const camera = currentAFrameScene.querySelector('[camera]'); - if (camera) { - camera.setAttribute('rotation', '0 0 0'); - camera.setAttribute('position', '0 0 0'); - } + if (current360Viewer) { + current360Viewer.reset(); } }); - // 자동 회전 버튼 + // 자동회전 버튼 autoBtn.addEventListener('click', () => { - if (isAutoRotating) { - stopAutoRotation(); - autoBtn.innerHTML = ' 자동회전'; - autoBtn.classList.remove('active'); - } else { - startAutoRotation(); - autoBtn.innerHTML = ' 정지'; - autoBtn.classList.add('active'); + if (current360Viewer) { + if (isAutoRotating) { + current360Viewer.stopAutoRotate(); + autoBtn.innerHTML = '자동회전'; + autoBtn.classList.remove('active'); + isAutoRotating = false; + } else { + current360Viewer.startAutoRotate(); + autoBtn.innerHTML = '정지'; + autoBtn.classList.add('active'); + isAutoRotating = true; + } } }); - // VR 버튼 - vrBtn.addEventListener('click', () => { - if (currentAFrameScene) { - const vrButton = currentAFrameScene.querySelector('[vr-mode-ui]'); - if (vrButton) { - // VR 모드 진입 - currentAFrameScene.enterVR(); - } + // 확대 버튼 + zoomInBtn.addEventListener('click', () => { + if (current360Viewer) { + current360Viewer.zoomIn(); + } + }); + + // 축소 버튼 + zoomOutBtn.addEventListener('click', () => { + if (current360Viewer) { + current360Viewer.zoomOut(); } }); // 도움말 버튼 helpBtn.addEventListener('click', () => { - helpVisible = !helpVisible; - helpText.style.display = helpVisible ? 'block' : 'none'; - helpBtn.classList.toggle('active', helpVisible); + isHelpVisible = !isHelpVisible; + helpPanel.style.display = isHelpVisible ? 'block' : 'none'; + helpBtn.classList.toggle('active', isHelpVisible); }); - // 도움말 자동 숨김 - setTimeout(() => { - helpText.style.display = 'none'; - }, 6000); + // 도움말 패널 클릭시 닫기 + helpPanel.addEventListener('click', (e) => { + if (e.target === helpPanel) { + isHelpVisible = false; + helpPanel.style.display = 'none'; + helpBtn.classList.remove('active'); + } + }); } -function openAFrameModal(imageSrc, title) { - const modal = document.getElementById('aframe-modal'); - const modalTitle = document.getElementById('aframe-modal-title'); - const viewerContainer = document.getElementById('aframe-viewer'); +function openPanoramaModal(imageSrc, title) { + const modal = document.getElementById('panorama-modal'); + const modalTitle = document.getElementById('panorama-modal-title'); + const viewerContainer = document.getElementById('panorama-viewer-container'); modalTitle.textContent = title; modal.classList.add('active'); document.body.style.overflow = 'hidden'; - // A-Frame Scene 생성 - const sceneHTML = ` - - - - - - - - - - - - - - - - - - - `; - - viewerContainer.innerHTML = sceneHTML; - currentAFrameScene = document.getElementById('aframe-scene'); - - // A-Frame 로딩 완료 대기 - currentAFrameScene.addEventListener('loaded', () => { - console.log('A-Frame 360도 뷰어 로드 완료:', title); - }); + // 간단한 360도 좌우 스크롤 뷰어 생성 + current360Viewer = new Easy360Viewer(viewerContainer, imageSrc, title); } -function startAutoRotation() { - if (!currentAFrameScene) return; +function closePanoramaModal() { + const modal = document.getElementById('panorama-modal'); + const autoBtn = document.getElementById('panorama-auto-btn'); + const helpBtn = document.getElementById('panorama-help-btn'); + const helpPanel = document.getElementById('panorama-help-panel'); - isAutoRotating = true; - const camera = currentAFrameScene.querySelector('#panorama-camera'); - let currentRotationY = 0; - - autoRotationInterval = setInterval(() => { - if (camera && isAutoRotating) { - currentRotationY += 0.5; // 초당 0.5도 회전 - if (currentRotationY >= 360) currentRotationY = 0; - - const currentRotation = camera.getAttribute('rotation'); - camera.setAttribute('rotation', `${currentRotation.x} ${currentRotationY} ${currentRotation.z}`); - } - }, 50); // 20fps -} - -function stopAutoRotation() { - isAutoRotating = false; - if (autoRotationInterval) { - clearInterval(autoRotationInterval); - autoRotationInterval = null; + // WebGL 뷰어 정리 + if (current360Viewer) { + current360Viewer.destroy(); + current360Viewer = null; } -} - -function closeAFrameModal() { - const modal = document.getElementById('aframe-modal'); - const autoBtn = document.getElementById('aframe-auto-btn'); - // 자동 회전 정지 - stopAutoRotation(); - - // 버튼 초기화 - autoBtn.innerHTML = ' 자동회전'; + // UI 초기화 + autoBtn.innerHTML = '자동회전'; autoBtn.classList.remove('active'); - - // A-Frame Scene 정리 - if (currentAFrameScene) { - currentAFrameScene.destroy(); - currentAFrameScene = null; - } + helpBtn.classList.remove('active'); + helpPanel.style.display = 'none'; modal.classList.remove('active'); document.body.style.overflow = ''; // 컨테이너 정리 - document.getElementById('aframe-viewer').innerHTML = ''; + document.getElementById('panorama-viewer-container').innerHTML = ''; } \ No newline at end of file