Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a0422aa00 | |||
| 00bde46c5e |
+57
-4
@@ -111,11 +111,13 @@ html, body {
|
||||
|
||||
#close-viewer-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 60px;
|
||||
top: 5px;
|
||||
left: 40px;
|
||||
padding: 12px 24px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: white solid 1px;
|
||||
border-radius: 10px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
@@ -125,6 +127,57 @@ html, body {
|
||||
|
||||
#close-viewer-btn:hover {
|
||||
background: white;
|
||||
color: rgba(44, 44, 44, 0.9);
|
||||
}
|
||||
|
||||
/* Viewer Info Overlay */
|
||||
#viewer-info-overlay {
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
background: rgba(30, 30, 30, 0.5); /* Nền xám tối transparent 0.5 - Tăng độ rõ */
|
||||
border: 1px solid rgba(255, 255, 255, 0.5); /* Viền trắng mờ - Tăng độ rõ */
|
||||
border-radius: 10px;
|
||||
padding: 15px 20px;
|
||||
max-width: 300px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
|
||||
z-index: 3002; /* Trên viewer, dưới các modal */
|
||||
pointer-events: none; /* Không chặn tương tác chuột với viewer */
|
||||
opacity: 0; /* Khởi tạo ẩn */
|
||||
transform: translateY(20px); /* Hiệu ứng trượt lên */
|
||||
transition: opacity 0.3s ease-out, transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
#viewer-info-overlay.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
#viewer-info-overlay .info-content h4 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
color: #00d4ff; /* Màu xanh nổi bật cho tiêu đề */
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
#viewer-info-overlay .info-content p {
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
#viewer-info-overlay .info-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
/* Modal Overlay */
|
||||
.modal-overlay {
|
||||
|
||||
+84
-3
@@ -369,6 +369,14 @@ function formatSystemDate(dateString) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kiểm tra xem người dùng có đang dùng thiết bị di động hay không
|
||||
*/
|
||||
function isMobileDevice() {
|
||||
return (window.innerWidth <= 768) ||
|
||||
(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the full-screen Leaflet Map
|
||||
*/
|
||||
@@ -388,7 +396,11 @@ function initMap() {
|
||||
if (isNaN(startZoom)) startZoom = 13;
|
||||
|
||||
// Khởi tạo bản đồ với zoomControl và tắt attribution mặc định của Leaflet
|
||||
map = L.map('map', { zoomControl: true, attributionControl: false }).setView([startLat, startLng], startZoom);
|
||||
map = L.map('map', {
|
||||
zoomControl: !isMobileDevice(), // Ẩn nút +/- trên mobile để lấy thêm không gian hiển thị
|
||||
attributionControl: false,
|
||||
tap: true // Hỗ trợ click trên thiết bị cảm ứng tốt hơn
|
||||
}).setView([startLat, startLng], startZoom);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
@@ -1056,7 +1068,7 @@ async function loadScenes(urlToken = null) {
|
||||
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
||||
if (token) thumbUrl += `?token=${token}`;
|
||||
else if (scene.privacy === 'shared' && scene.shareToken) thumbUrl += `?token=${scene.shareToken}`;
|
||||
thumbHtml = `<img src="${thumbUrl}" alt="${sceneName}">`;
|
||||
thumbHtml = `<img src="${thumbUrl}" alt="${sceneName}" loading="lazy">`;
|
||||
}
|
||||
|
||||
const calloutIcon = L.divIcon({
|
||||
@@ -1423,6 +1435,9 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
|
||||
// Initialize 3D Viewer with secure, referer-protected image stream
|
||||
initPanoramaViewer(secureImageUrl, hotspots || [], sceneOwnerId, initialPitch, initialYaw);
|
||||
|
||||
// Hiển thị thông tin overlay góc màn hình
|
||||
updateViewerInfoOverlay(scene);
|
||||
|
||||
// Sau khi mở thành công từ URL trực tiếp, xóa tham số để làm sạch thanh địa chỉ (URL chuyên nghiệp)
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.has('sceneId') || window.location.pathname.includes('/api/share/')) {
|
||||
@@ -1453,6 +1468,72 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cập nhật overlay thông tin cảnh đang xem (Góc dưới bên trái)
|
||||
*/
|
||||
async function updateViewerInfoOverlay(scene) {
|
||||
if (!scene) {
|
||||
console.warn("updateViewerInfoOverlay: Scene object is null or undefined. Cannot display info.");
|
||||
return;
|
||||
}
|
||||
|
||||
let overlay = document.getElementById('viewer-info-overlay');
|
||||
if (!overlay) {
|
||||
overlay = document.createElement('div');
|
||||
overlay.id = 'viewer-info-overlay';
|
||||
// Append to body to allow independent positioning and animation
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
const lang = systemSettings.language || 'vi';
|
||||
const name = scene.name || scene.title || (lang === 'vi' ? "Cảnh không tên" : "Untitled Scene");
|
||||
const desc = scene.description || "";
|
||||
const author = scene.createdBy?.username || (lang === 'vi' ? "Ẩn danh" : "Anonymous");
|
||||
const date = formatSystemDate(scene.createdAt);
|
||||
|
||||
// Lấy tọa độ để truy vấn địa chỉ
|
||||
const lat = scene.gps?.lat || scene.lat;
|
||||
const lng = scene.gps?.lng || scene.lng;
|
||||
let locationText = lang === 'vi' ? "Đang xác định vị trí..." : "Locating...";
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div class="info-content">
|
||||
<h4>${name}</h4>
|
||||
${desc ? `<p>${desc}</p>` : ''}
|
||||
<div class="info-meta">
|
||||
<span>👤 ${author}</span>
|
||||
<span>📸 ${lang === 'vi' ? 'Ngày chụp' : 'Date taken'}: ${date}</span>
|
||||
<span id="overlay-address">📍 ${locationText}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
overlay.classList.add('show'); // Make it visible with transition
|
||||
|
||||
// Thực hiện Reverse Geocoding để lấy địa chỉ từ tọa độ
|
||||
try {
|
||||
const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`);
|
||||
const data = await res.json();
|
||||
const address = data.display_name || `${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
||||
const addrElem = document.getElementById('overlay-address');
|
||||
if (addrElem) addrElem.innerText = `📍 ${address}`;
|
||||
} catch (e) {
|
||||
const addrElem = document.getElementById('overlay-address');
|
||||
if (addrElem) addrElem.innerText = `📍 ${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ẩn overlay thông tin cảnh đang xem
|
||||
*/
|
||||
window.hideViewerInfoOverlay = function() {
|
||||
const overlay = document.getElementById('viewer-info-overlay');
|
||||
if (overlay) {
|
||||
overlay.classList.remove('show'); // Hide it with transition
|
||||
// Optionally remove from DOM after transition if not needed
|
||||
// setTimeout(() => overlay.remove(), 300); // Match CSS transition duration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Khôi phục Scene đang xem từ localStorage sau khi reload trang
|
||||
*/
|
||||
@@ -1951,7 +2032,7 @@ async function loadMyTours() {
|
||||
if (assetId) {
|
||||
let thumbUrl = `${API_BASE_URL}/assets/view/${assetId}`;
|
||||
if (token) thumbUrl += `?token=${token}`;
|
||||
card.style.backgroundImage = `url('${thumbUrl}')`;
|
||||
card.innerHTML = `<img class="tour-card-bg" src="${thumbUrl}" loading="lazy">`;
|
||||
} else {
|
||||
card.style.backgroundColor = '#1a1a1a';
|
||||
}
|
||||
|
||||
@@ -119,9 +119,13 @@ function initPanoramaViewer(imageUrl, hotspots = [], ownerId = null, initialPitc
|
||||
activeViewer = pannellum.viewer('panorama-viewer', {
|
||||
"type": "equirectangular",
|
||||
"panorama": imageUrl,
|
||||
"autoRotate": 0,
|
||||
"autoRotateInactivityDelay": 5000,
|
||||
"autoLoad": true,
|
||||
"pitch": initialPitch,
|
||||
"yaw": initialYaw,
|
||||
"orientationOnByDefault": true,
|
||||
"draggable": true,
|
||||
"showControls": true,
|
||||
"compass": false,
|
||||
"mouseZoom": true,
|
||||
@@ -140,6 +144,11 @@ function initPanoramaViewer(imageUrl, hotspots = [], ownerId = null, initialPitc
|
||||
function closeViewer() {
|
||||
document.getElementById('viewer-container').style.display = 'none';
|
||||
|
||||
// Ẩn overlay thông tin cảnh nếu có
|
||||
if (typeof window.hideViewerInfoOverlay === 'function') {
|
||||
window.hideViewerInfoOverlay();
|
||||
}
|
||||
|
||||
// Xóa trạng thái Scene đang hoạt động khi đóng viewer
|
||||
localStorage.removeItem('activeSceneId');
|
||||
localStorage.removeItem('activeScenePrivacy');
|
||||
|
||||
Reference in New Issue
Block a user