Fix : 갤러리 업데이트
This commit is contained in:
parent
c1a85bba85
commit
e50cfa698f
@ -40,8 +40,8 @@
|
|||||||
<!-- 아이콘 폰트 (이모지 대체용) -->
|
<!-- 아이콘 폰트 (이모지 대체용) -->
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Photo Sphere Viewer -->
|
<!-- A-Frame VR Framework -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5/index.css">
|
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="css/common.css">
|
<link rel="stylesheet" href="css/common.css">
|
||||||
<link rel="stylesheet" href="css/gallery.css">
|
<link rel="stylesheet" href="css/gallery.css">
|
||||||
@ -171,8 +171,6 @@
|
|||||||
|
|
||||||
<div id="footer-placeholder"></div>
|
<div id="footer-placeholder"></div>
|
||||||
<script src="js/common.js"></script>
|
<script src="js/common.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@photo-sphere-viewer/core@5/index.js"></script>
|
|
||||||
<script src="js/gallery.js"></script>
|
<script src="js/gallery.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
269
js/gallery.js
269
js/gallery.js
@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
initGallery();
|
initGallery();
|
||||||
initLightbox();
|
initLightbox();
|
||||||
initGalleryAnimations();
|
initGalleryAnimations();
|
||||||
initPhotoSphereViewers();
|
initAFrame360Viewers();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 갤러리 초기화
|
// 갤러리 초기화
|
||||||
@ -267,20 +267,22 @@ function handleSwipe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Photo Sphere Viewer - 진짜 360도 VR 뷰어
|
// A-Frame 기반 360도 VR 뷰어 - 안정적이고 강력한 VR 체험
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
let currentPhotoSphereViewer = null;
|
let currentAFrameScene = null;
|
||||||
|
let autoRotationInterval = null;
|
||||||
|
let isAutoRotating = false;
|
||||||
|
|
||||||
function initPhotoSphereViewers() {
|
function initAFrame360Viewers() {
|
||||||
// 모달 생성
|
// 모달 생성
|
||||||
createPhotoSphereModal();
|
createAFrameModal();
|
||||||
|
|
||||||
// 미리보기 설정
|
// 미리보기 설정
|
||||||
initPhotoSpherePreviews();
|
initAFramePreviews();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPhotoSpherePreviews() {
|
function initAFramePreviews() {
|
||||||
const containers = document.querySelectorAll('.panorama-clickable');
|
const containers = document.querySelectorAll('.panorama-clickable');
|
||||||
|
|
||||||
containers.forEach(container => {
|
containers.forEach(container => {
|
||||||
@ -289,7 +291,7 @@ function initPhotoSpherePreviews() {
|
|||||||
|
|
||||||
// 클릭 이벤트
|
// 클릭 이벤트
|
||||||
container.addEventListener('click', () => {
|
container.addEventListener('click', () => {
|
||||||
openPhotoSphereModal(imageSrc, title);
|
openAFrameModal(imageSrc, title);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 도움말 자동 숨김
|
// 도움말 자동 숨김
|
||||||
@ -303,39 +305,41 @@ function initPhotoSpherePreviews() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPhotoSphereModal() {
|
function createAFrameModal() {
|
||||||
const modalHTML = `
|
const modalHTML = `
|
||||||
<div id="photosphere-modal" class="panorama-modal">
|
<div id="aframe-modal" class="panorama-modal">
|
||||||
<div class="panorama-modal-content">
|
<div class="panorama-modal-content">
|
||||||
<div id="photosphere-viewer" class="panorama-modal-viewer"></div>
|
<div id="aframe-viewer" class="panorama-modal-viewer">
|
||||||
|
<!-- A-Frame Scene will be injected here -->
|
||||||
|
</div>
|
||||||
<div class="panorama-modal-controls">
|
<div class="panorama-modal-controls">
|
||||||
<div id="photosphere-modal-title" class="panorama-modal-title"></div>
|
<div id="aframe-modal-title" class="panorama-modal-title"></div>
|
||||||
<div class="panorama-modal-buttons">
|
<div class="panorama-modal-buttons">
|
||||||
<button class="panorama-modal-btn" id="photosphere-reset-btn">
|
<button class="panorama-modal-btn" id="aframe-reset-btn">
|
||||||
<i class="fas fa-home"></i> 리셋
|
<i class="fas fa-home"></i> 리셋
|
||||||
</button>
|
</button>
|
||||||
<button class="panorama-modal-btn" id="photosphere-auto-btn">
|
<button class="panorama-modal-btn" id="aframe-auto-btn">
|
||||||
<i class="fas fa-sync-alt"></i> 자동회전
|
<i class="fas fa-sync-alt"></i> 자동회전
|
||||||
</button>
|
</button>
|
||||||
<button class="panorama-modal-btn" id="photosphere-fullscreen-btn">
|
<button class="panorama-modal-btn" id="aframe-vr-btn">
|
||||||
<i class="fas fa-expand"></i> 전체화면
|
<i class="fas fa-vr-cardboard"></i> VR모드
|
||||||
</button>
|
</button>
|
||||||
<button class="panorama-modal-btn" id="photosphere-help-btn">
|
<button class="panorama-modal-btn" id="aframe-help-btn">
|
||||||
<i class="fas fa-question-circle"></i> 도움말
|
<i class="fas fa-question-circle"></i> 도움말
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="panorama-modal-close" id="photosphere-modal-close">
|
<button class="panorama-modal-close" id="aframe-modal-close">
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="panorama-modal-help-text" id="photosphere-help-text">
|
<div class="panorama-modal-help-text" id="aframe-help-text">
|
||||||
<p><strong>360° VR 조작법</strong></p>
|
<p><strong>360° VR 조작법</strong></p>
|
||||||
<p>🖱️ 드래그: 전방향 회전</p>
|
<p>🖱️ 드래그: 전방향 회전</p>
|
||||||
<p>📱 터치: 터치 후 드래그</p>
|
<p>📱 터치: 터치 후 드래그</p>
|
||||||
<p>🖱️ 휠: 줌 인/아웃</p>
|
<p>🖱️ 휠: 줌 인/아웃</p>
|
||||||
<p>📱 핀치: 줌 인/아웃</p>
|
<p>📱 핀치: 줌 인/아웃</p>
|
||||||
<p>⌨️ 화살표: 방향 이동</p>
|
<p>⌨️ WASD: 이동</p>
|
||||||
<p>⌨️ +/-: 줌 조절</p>
|
<p>📱 VR모드: VR 헤드셋 지원</p>
|
||||||
<p>⌨️ ESC: 닫기</p>
|
<p>⌨️ ESC: 닫기</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -343,67 +347,65 @@ function createPhotoSphereModal() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||||
setupPhotoSphereModalListeners();
|
setupAFrameModalListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPhotoSphereModalListeners() {
|
function setupAFrameModalListeners() {
|
||||||
const modal = document.getElementById('photosphere-modal');
|
const modal = document.getElementById('aframe-modal');
|
||||||
const closeBtn = document.getElementById('photosphere-modal-close');
|
const closeBtn = document.getElementById('aframe-modal-close');
|
||||||
const resetBtn = document.getElementById('photosphere-reset-btn');
|
const resetBtn = document.getElementById('aframe-reset-btn');
|
||||||
const autoBtn = document.getElementById('photosphere-auto-btn');
|
const autoBtn = document.getElementById('aframe-auto-btn');
|
||||||
const fullscreenBtn = document.getElementById('photosphere-fullscreen-btn');
|
const vrBtn = document.getElementById('aframe-vr-btn');
|
||||||
const helpBtn = document.getElementById('photosphere-help-btn');
|
const helpBtn = document.getElementById('aframe-help-btn');
|
||||||
const helpText = document.getElementById('photosphere-help-text');
|
const helpText = document.getElementById('aframe-help-text');
|
||||||
|
|
||||||
let isAutoRotating = false;
|
|
||||||
let helpVisible = false;
|
let helpVisible = false;
|
||||||
|
|
||||||
// 모달 닫기
|
// 모달 닫기
|
||||||
closeBtn.addEventListener('click', closePhotoSphereModal);
|
closeBtn.addEventListener('click', closeAFrameModal);
|
||||||
modal.addEventListener('click', (e) => {
|
modal.addEventListener('click', (e) => {
|
||||||
if (e.target === modal) closePhotoSphereModal();
|
if (e.target === modal) closeAFrameModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESC 키
|
// ESC 키
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
||||||
closePhotoSphereModal();
|
closeAFrameModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 리셋 버튼
|
// 리셋 버튼
|
||||||
resetBtn.addEventListener('click', () => {
|
resetBtn.addEventListener('click', () => {
|
||||||
if (currentPhotoSphereViewer) {
|
if (currentAFrameScene) {
|
||||||
currentPhotoSphereViewer.animate({
|
const camera = currentAFrameScene.querySelector('[camera]');
|
||||||
yaw: 0,
|
if (camera) {
|
||||||
pitch: 0,
|
camera.setAttribute('rotation', '0 0 0');
|
||||||
zoom: 50,
|
camera.setAttribute('position', '0 0 0');
|
||||||
speed: '2rpm'
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 자동 회전 버튼
|
// 자동 회전 버튼
|
||||||
autoBtn.addEventListener('click', () => {
|
autoBtn.addEventListener('click', () => {
|
||||||
if (currentPhotoSphereViewer) {
|
if (isAutoRotating) {
|
||||||
if (isAutoRotating) {
|
stopAutoRotation();
|
||||||
currentPhotoSphereViewer.stopAutorotate();
|
autoBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 자동회전';
|
||||||
autoBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 자동회전';
|
autoBtn.classList.remove('active');
|
||||||
autoBtn.classList.remove('active');
|
} else {
|
||||||
isAutoRotating = false;
|
startAutoRotation();
|
||||||
} else {
|
autoBtn.innerHTML = '<i class="fas fa-pause"></i> 정지';
|
||||||
currentPhotoSphereViewer.startAutorotate();
|
autoBtn.classList.add('active');
|
||||||
autoBtn.innerHTML = '<i class="fas fa-pause"></i> 정지';
|
|
||||||
autoBtn.classList.add('active');
|
|
||||||
isAutoRotating = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 전체화면 버튼
|
// VR 버튼
|
||||||
fullscreenBtn.addEventListener('click', () => {
|
vrBtn.addEventListener('click', () => {
|
||||||
if (currentPhotoSphereViewer) {
|
if (currentAFrameScene) {
|
||||||
currentPhotoSphereViewer.enterFullscreen();
|
const vrButton = currentAFrameScene.querySelector('[vr-mode-ui]');
|
||||||
|
if (vrButton) {
|
||||||
|
// VR 모드 진입
|
||||||
|
currentAFrameScene.enterVR();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -420,84 +422,113 @@ function setupPhotoSphereModalListeners() {
|
|||||||
}, 6000);
|
}, 6000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPhotoSphereModal(imageSrc, title) {
|
function openAFrameModal(imageSrc, title) {
|
||||||
const modal = document.getElementById('photosphere-modal');
|
const modal = document.getElementById('aframe-modal');
|
||||||
const modalTitle = document.getElementById('photosphere-modal-title');
|
const modalTitle = document.getElementById('aframe-modal-title');
|
||||||
const viewerContainer = document.getElementById('photosphere-viewer');
|
const viewerContainer = document.getElementById('aframe-viewer');
|
||||||
|
|
||||||
modalTitle.textContent = title;
|
modalTitle.textContent = title;
|
||||||
modal.classList.add('active');
|
modal.classList.add('active');
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
// Photo Sphere Viewer 생성
|
// A-Frame Scene 생성
|
||||||
try {
|
const sceneHTML = `
|
||||||
currentPhotoSphereViewer = new PhotoSphereViewer.Viewer({
|
<a-scene
|
||||||
container: viewerContainer,
|
id="aframe-scene"
|
||||||
panorama: imageSrc,
|
embedded
|
||||||
caption: title,
|
style="height: 100%; width: 100%;"
|
||||||
loadingImg: imageSrc, // 로딩 중 미리보기
|
vr-mode-ui="enabled: true"
|
||||||
touchmoveTwoFingers: true,
|
background="color: #000000">
|
||||||
mousewheelCtrlKey: false,
|
|
||||||
defaultYaw: 0,
|
<!-- 360도 이미지 스카이박스 -->
|
||||||
defaultPitch: 0,
|
<a-sky
|
||||||
defaultZoomLvl: 50,
|
id="panorama-sky"
|
||||||
minFov: 30,
|
src="${imageSrc}"
|
||||||
maxFov: 90,
|
rotation="0 -90 0">
|
||||||
autorotateDelay: null,
|
</a-sky>
|
||||||
autorotateIdle: false,
|
|
||||||
autorotateSpeed: '2rpm',
|
<!-- 카메라 (사용자 시점) -->
|
||||||
fisheye: false,
|
<a-camera
|
||||||
navbar: [
|
id="panorama-camera"
|
||||||
'zoom',
|
look-controls="enabled: true; touchEnabled: true"
|
||||||
'move',
|
wasd-controls="enabled: false"
|
||||||
'fullscreen',
|
position="0 0 0"
|
||||||
{
|
rotation="0 0 0"
|
||||||
title: '자동회전',
|
fov="80"
|
||||||
content: '🔄',
|
near="0.1"
|
||||||
onClick: () => {
|
far="1000">
|
||||||
const autoBtn = document.getElementById('photosphere-auto-btn');
|
|
||||||
autoBtn.click();
|
<!-- 커서 (VR 모드용) -->
|
||||||
}
|
<a-cursor
|
||||||
}
|
animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150"
|
||||||
]
|
animation__fusing="property: scale; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500"
|
||||||
});
|
raycaster="objects: .clickable"
|
||||||
|
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
|
||||||
// 이벤트 리스너
|
material="color: white; shader: flat">
|
||||||
currentPhotoSphereViewer.addEventListener('ready', () => {
|
</a-cursor>
|
||||||
console.log('Photo Sphere Viewer 준비 완료');
|
</a-camera>
|
||||||
});
|
|
||||||
|
<!-- 조명 -->
|
||||||
currentPhotoSphereViewer.addEventListener('autorotate', () => {
|
<a-light type="ambient" color="#404040" intensity="0.4"></a-light>
|
||||||
console.log('자동 회전 시작');
|
<a-light type="directional" position="1 1 1" color="#ffffff" intensity="0.6"></a-light>
|
||||||
});
|
</a-scene>
|
||||||
|
`;
|
||||||
currentPhotoSphereViewer.addEventListener('autorotate-stop', () => {
|
|
||||||
console.log('자동 회전 정지');
|
viewerContainer.innerHTML = sceneHTML;
|
||||||
});
|
currentAFrameScene = document.getElementById('aframe-scene');
|
||||||
|
|
||||||
} catch (error) {
|
// A-Frame 로딩 완료 대기
|
||||||
console.error('Photo Sphere Viewer 초기화 실패:', error);
|
currentAFrameScene.addEventListener('loaded', () => {
|
||||||
alert('360도 뷰어를 로드할 수 없습니다. 페이지를 새로고침해주세요.');
|
console.log('A-Frame 360도 뷰어 로드 완료:', title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAutoRotation() {
|
||||||
|
if (!currentAFrameScene) return;
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closePhotoSphereModal() {
|
function closeAFrameModal() {
|
||||||
const modal = document.getElementById('photosphere-modal');
|
const modal = document.getElementById('aframe-modal');
|
||||||
const autoBtn = document.getElementById('photosphere-auto-btn');
|
const autoBtn = document.getElementById('aframe-auto-btn');
|
||||||
|
|
||||||
// 뷰어 정리
|
// 자동 회전 정지
|
||||||
if (currentPhotoSphereViewer) {
|
stopAutoRotation();
|
||||||
currentPhotoSphereViewer.destroy();
|
|
||||||
currentPhotoSphereViewer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 버튼 초기화
|
// 버튼 초기화
|
||||||
autoBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 자동회전';
|
autoBtn.innerHTML = '<i class="fas fa-sync-alt"></i> 자동회전';
|
||||||
autoBtn.classList.remove('active');
|
autoBtn.classList.remove('active');
|
||||||
|
|
||||||
|
// A-Frame Scene 정리
|
||||||
|
if (currentAFrameScene) {
|
||||||
|
currentAFrameScene.destroy();
|
||||||
|
currentAFrameScene = null;
|
||||||
|
}
|
||||||
|
|
||||||
modal.classList.remove('active');
|
modal.classList.remove('active');
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
|
|
||||||
// 컨테이너 정리
|
// 컨테이너 정리
|
||||||
document.getElementById('photosphere-viewer').innerHTML = '';
|
document.getElementById('aframe-viewer').innerHTML = '';
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user