diff --git a/frontend/css/style.css b/frontend/css/style.css
index bc55409..ed19134 100644
--- a/frontend/css/style.css
+++ b/frontend/css/style.css
@@ -129,6 +129,56 @@ html, body {
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 {
display: none;
diff --git a/frontend/js/main_map.js b/frontend/js/main_map.js
index 981f84f..c8ffb06 100644
--- a/frontend/js/main_map.js
+++ b/frontend/js/main_map.js
@@ -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 = ``;
+ thumbHtml = `
`;
}
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 = `
+
${desc}
` : ''} + +