Spaces:
Running
Running
Update index_sans_gif.js
Browse files- index_sans_gif.js +51 -22
index_sans_gif.js
CHANGED
@@ -26,13 +26,18 @@
|
|
26 |
const instanceId = Math.random().toString(36).substr(2, 8);
|
27 |
|
28 |
// Read required URLs and parameters from the config.
|
|
|
29 |
var plyUrl = config.ply_url;
|
|
|
|
|
|
|
30 |
var minZoom = parseFloat(config.minZoom || "0");
|
31 |
var maxZoom = parseFloat(config.maxZoom || "20");
|
32 |
var minAngle = parseFloat(config.minAngle || "0");
|
33 |
var maxAngle = parseFloat(config.maxAngle || "360");
|
34 |
|
35 |
// Determine the aspect ratio.
|
|
|
36 |
var aspectPercent = "100%";
|
37 |
if (config.aspect) {
|
38 |
if (config.aspect.indexOf(":") !== -1) {
|
@@ -49,6 +54,7 @@
|
|
49 |
}
|
50 |
}
|
51 |
} else {
|
|
|
52 |
var parentContainer = scriptTag.parentNode;
|
53 |
var containerWidth = parentContainer.offsetWidth;
|
54 |
var containerHeight = parentContainer.offsetHeight;
|
@@ -57,9 +63,10 @@
|
|
57 |
}
|
58 |
}
|
59 |
|
|
|
60 |
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
61 |
|
62 |
-
// Inject CSS styles into the document head.
|
63 |
var styleEl = document.createElement('style');
|
64 |
styleEl.textContent = `
|
65 |
/* Widget container styling */
|
@@ -81,6 +88,7 @@
|
|
81 |
}
|
82 |
/* Viewer Container styling */
|
83 |
#viewer-container-${instanceId} {
|
|
|
84 |
display: block;
|
85 |
position: absolute;
|
86 |
top: 0;
|
@@ -97,7 +105,7 @@
|
|
97 |
height: 100%;
|
98 |
display: block;
|
99 |
}
|
100 |
-
/* Progress dialog styling */
|
101 |
#progress-dialog-${instanceId} {
|
102 |
position: absolute;
|
103 |
top: 50%;
|
@@ -110,7 +118,7 @@
|
|
110 |
z-index: 1000;
|
111 |
display: none;
|
112 |
}
|
113 |
-
/* Menu content styling */
|
114 |
#menu-content-${instanceId} {
|
115 |
display: none;
|
116 |
position: absolute;
|
@@ -139,6 +147,8 @@
|
|
139 |
align-items: center;
|
140 |
justify-content: center;
|
141 |
}
|
|
|
|
|
142 |
#fullscreen-toggle-${instanceId} {
|
143 |
top: 17px;
|
144 |
right: 15px;
|
@@ -154,16 +164,18 @@
|
|
154 |
line-height: 1;
|
155 |
padding: 0;
|
156 |
}
|
|
|
157 |
.reset-icon {
|
158 |
display: inline-block;
|
159 |
}
|
160 |
`;
|
161 |
document.head.appendChild(styleEl);
|
162 |
|
163 |
-
// Create the widget container.
|
164 |
var widgetContainer = document.createElement('div');
|
165 |
widgetContainer.id = 'ply-widget-container-' + instanceId;
|
166 |
widgetContainer.innerHTML = `
|
|
|
167 |
<div id="viewer-container-${instanceId}">
|
168 |
<canvas id="canvas-${instanceId}"></canvas>
|
169 |
<div id="progress-dialog-${instanceId}">
|
@@ -183,7 +195,7 @@
|
|
183 |
`;
|
184 |
scriptTag.parentNode.appendChild(widgetContainer);
|
185 |
|
186 |
-
// Grab element references.
|
187 |
var viewerContainer = document.getElementById('viewer-container-' + instanceId);
|
188 |
var fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
|
189 |
var helpToggle = document.getElementById('help-toggle-' + instanceId);
|
@@ -194,21 +206,40 @@
|
|
194 |
var progressIndicator = document.getElementById('progress-indicator-' + instanceId);
|
195 |
|
196 |
// --- Button Event Handlers ---
|
|
|
197 |
fullscreenToggle.addEventListener('click', function() {
|
198 |
if (isIOS) {
|
199 |
-
widgetContainer.classList.
|
|
|
|
|
|
|
|
|
200 |
fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
|
201 |
} else {
|
202 |
if (!document.fullscreenElement) {
|
203 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
} else {
|
205 |
-
|
|
|
|
|
206 |
}
|
207 |
}
|
208 |
});
|
209 |
|
210 |
document.addEventListener('fullscreenchange', function() {
|
211 |
-
|
|
|
|
|
|
|
|
|
212 |
});
|
213 |
|
214 |
helpToggle.addEventListener('click', function(e) {
|
@@ -221,8 +252,12 @@
|
|
221 |
if (cameraInstance && initialCameraPosition && initialCameraRotation) {
|
222 |
cameraInstance.position = initialCameraPosition.clone();
|
223 |
cameraInstance.rotation = initialCameraRotation.clone();
|
224 |
-
if (typeof cameraInstance.update === 'function')
|
225 |
-
|
|
|
|
|
|
|
|
|
226 |
}
|
227 |
});
|
228 |
|
@@ -230,29 +265,23 @@
|
|
230 |
async function initializeViewer() {
|
231 |
const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
|
232 |
progressDialog.style.display = 'block';
|
233 |
-
|
234 |
const renderer = new SPLAT.WebGLRenderer(canvas);
|
235 |
const scene = new SPLAT.Scene();
|
236 |
-
|
237 |
-
// Create the camera.
|
238 |
const camera = new SPLAT.Camera();
|
239 |
-
|
|
|
|
|
240 |
if (config.cameraPosition &&
|
241 |
Array.isArray(config.cameraPosition) &&
|
242 |
config.cameraPosition.length === 3) {
|
|
|
243 |
camera.position.x = config.cameraPosition[0];
|
244 |
camera.position.y = config.cameraPosition[1];
|
245 |
camera.position.z = config.cameraPosition[2];
|
246 |
-
// Optionally, update the camera if needed.
|
247 |
-
if (typeof camera.update === 'function') camera.update();
|
248 |
}
|
249 |
-
|
250 |
-
// Create OrbitControls AFTER the camera's position is set.
|
251 |
-
const controls = new SPLAT.OrbitControls(camera, canvas);
|
252 |
-
|
253 |
cameraInstance = camera;
|
254 |
controlsInstance = controls;
|
255 |
-
// Capture the adjusted initial camera values.
|
256 |
initialCameraPosition = camera.position.clone();
|
257 |
initialCameraRotation = camera.rotation.clone();
|
258 |
|
|
|
26 |
const instanceId = Math.random().toString(36).substr(2, 8);
|
27 |
|
28 |
// Read required URLs and parameters from the config.
|
29 |
+
// The gifUrl is no longer used.
|
30 |
var plyUrl = config.ply_url;
|
31 |
+
|
32 |
+
// Optional parameters for zoom and rotation limits.
|
33 |
+
// Defaults: zoom from 0 to 20; rotation from 0 to 360.
|
34 |
var minZoom = parseFloat(config.minZoom || "0");
|
35 |
var maxZoom = parseFloat(config.maxZoom || "20");
|
36 |
var minAngle = parseFloat(config.minAngle || "0");
|
37 |
var maxAngle = parseFloat(config.maxAngle || "360");
|
38 |
|
39 |
// Determine the aspect ratio.
|
40 |
+
// Default aspect: 1:1 (i.e. 100% padding-bottom)
|
41 |
var aspectPercent = "100%";
|
42 |
if (config.aspect) {
|
43 |
if (config.aspect.indexOf(":") !== -1) {
|
|
|
54 |
}
|
55 |
}
|
56 |
} else {
|
57 |
+
// If no aspect parameter is provided, compute the aspect ratio from the parent element.
|
58 |
var parentContainer = scriptTag.parentNode;
|
59 |
var containerWidth = parentContainer.offsetWidth;
|
60 |
var containerHeight = parentContainer.offsetHeight;
|
|
|
63 |
}
|
64 |
}
|
65 |
|
66 |
+
// Detect if the device is iOS.
|
67 |
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
68 |
|
69 |
+
// Inject CSS styles into the document head, scoped with the unique id.
|
70 |
var styleEl = document.createElement('style');
|
71 |
styleEl.textContent = `
|
72 |
/* Widget container styling */
|
|
|
88 |
}
|
89 |
/* Viewer Container styling */
|
90 |
#viewer-container-${instanceId} {
|
91 |
+
/* Display the viewer by default */
|
92 |
display: block;
|
93 |
position: absolute;
|
94 |
top: 0;
|
|
|
105 |
height: 100%;
|
106 |
display: block;
|
107 |
}
|
108 |
+
/* Progress dialog styling (as a centered div) */
|
109 |
#progress-dialog-${instanceId} {
|
110 |
position: absolute;
|
111 |
top: 50%;
|
|
|
118 |
z-index: 1000;
|
119 |
display: none;
|
120 |
}
|
121 |
+
/* Menu (instructions) content styling */
|
122 |
#menu-content-${instanceId} {
|
123 |
display: none;
|
124 |
position: absolute;
|
|
|
147 |
align-items: center;
|
148 |
justify-content: center;
|
149 |
}
|
150 |
+
/* Positions: fullscreen at top-right, help (instructions) below fullscreen,
|
151 |
+
and reset camera below help */
|
152 |
#fullscreen-toggle-${instanceId} {
|
153 |
top: 17px;
|
154 |
right: 15px;
|
|
|
164 |
line-height: 1;
|
165 |
padding: 0;
|
166 |
}
|
167 |
+
/* Adjust the ⟲ icon position within the reset camera button */
|
168 |
.reset-icon {
|
169 |
display: inline-block;
|
170 |
}
|
171 |
`;
|
172 |
document.head.appendChild(styleEl);
|
173 |
|
174 |
+
// Create the widget container and set its inner HTML.
|
175 |
var widgetContainer = document.createElement('div');
|
176 |
widgetContainer.id = 'ply-widget-container-' + instanceId;
|
177 |
widgetContainer.innerHTML = `
|
178 |
+
<!-- Viewer Container (displayed directly) -->
|
179 |
<div id="viewer-container-${instanceId}">
|
180 |
<canvas id="canvas-${instanceId}"></canvas>
|
181 |
<div id="progress-dialog-${instanceId}">
|
|
|
195 |
`;
|
196 |
scriptTag.parentNode.appendChild(widgetContainer);
|
197 |
|
198 |
+
// Grab element references using the unique IDs.
|
199 |
var viewerContainer = document.getElementById('viewer-container-' + instanceId);
|
200 |
var fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
|
201 |
var helpToggle = document.getElementById('help-toggle-' + instanceId);
|
|
|
206 |
var progressIndicator = document.getElementById('progress-indicator-' + instanceId);
|
207 |
|
208 |
// --- Button Event Handlers ---
|
209 |
+
|
210 |
fullscreenToggle.addEventListener('click', function() {
|
211 |
if (isIOS) {
|
212 |
+
if (!widgetContainer.classList.contains('fake-fullscreen')) {
|
213 |
+
widgetContainer.classList.add('fake-fullscreen');
|
214 |
+
} else {
|
215 |
+
widgetContainer.classList.remove('fake-fullscreen');
|
216 |
+
}
|
217 |
fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
|
218 |
} else {
|
219 |
if (!document.fullscreenElement) {
|
220 |
+
if (widgetContainer.requestFullscreen) {
|
221 |
+
widgetContainer.requestFullscreen();
|
222 |
+
} else if (widgetContainer.webkitRequestFullscreen) {
|
223 |
+
widgetContainer.webkitRequestFullscreen();
|
224 |
+
} else if (widgetContainer.mozRequestFullScreen) {
|
225 |
+
widgetContainer.mozRequestFullScreen();
|
226 |
+
} else if (widgetContainer.msRequestFullscreen) {
|
227 |
+
widgetContainer.msRequestFullscreen();
|
228 |
+
}
|
229 |
} else {
|
230 |
+
if (document.exitFullscreen) {
|
231 |
+
document.exitFullscreen();
|
232 |
+
}
|
233 |
}
|
234 |
}
|
235 |
});
|
236 |
|
237 |
document.addEventListener('fullscreenchange', function() {
|
238 |
+
if (document.fullscreenElement === widgetContainer) {
|
239 |
+
fullscreenToggle.textContent = '⇲';
|
240 |
+
} else {
|
241 |
+
fullscreenToggle.textContent = '⇱';
|
242 |
+
}
|
243 |
});
|
244 |
|
245 |
helpToggle.addEventListener('click', function(e) {
|
|
|
252 |
if (cameraInstance && initialCameraPosition && initialCameraRotation) {
|
253 |
cameraInstance.position = initialCameraPosition.clone();
|
254 |
cameraInstance.rotation = initialCameraRotation.clone();
|
255 |
+
if (typeof cameraInstance.update === 'function') {
|
256 |
+
cameraInstance.update();
|
257 |
+
}
|
258 |
+
if (controlsInstance && typeof controlsInstance.update === 'function') {
|
259 |
+
controlsInstance.update();
|
260 |
+
}
|
261 |
}
|
262 |
});
|
263 |
|
|
|
265 |
async function initializeViewer() {
|
266 |
const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
|
267 |
progressDialog.style.display = 'block';
|
|
|
268 |
const renderer = new SPLAT.WebGLRenderer(canvas);
|
269 |
const scene = new SPLAT.Scene();
|
|
|
|
|
270 |
const camera = new SPLAT.Camera();
|
271 |
+
const controls = new SPLAT.OrbitControls(camera, canvas);
|
272 |
+
|
273 |
+
// Set the initial camera position from the config.
|
274 |
if (config.cameraPosition &&
|
275 |
Array.isArray(config.cameraPosition) &&
|
276 |
config.cameraPosition.length === 3) {
|
277 |
+
// Since camera.position.set is not available, assign each coordinate.
|
278 |
camera.position.x = config.cameraPosition[0];
|
279 |
camera.position.y = config.cameraPosition[1];
|
280 |
camera.position.z = config.cameraPosition[2];
|
|
|
|
|
281 |
}
|
282 |
+
|
|
|
|
|
|
|
283 |
cameraInstance = camera;
|
284 |
controlsInstance = controls;
|
|
|
285 |
initialCameraPosition = camera.position.clone();
|
286 |
initialCameraRotation = camera.rotation.clone();
|
287 |
|