bilca commited on
Commit
6469499
·
verified ·
1 Parent(s): e345ff6

Update index_sans_gif.js

Browse files
Files changed (1) hide show
  1. index_sans_gif.js +109 -42
index_sans_gif.js CHANGED
@@ -1,10 +1,8 @@
1
  (async function() {
2
- // Get the script tag and its config attribute.
3
  const scriptTag = document.currentScript;
4
  const configUrl = scriptTag.getAttribute("data-config");
5
  let config = {};
6
-
7
- // Load configuration from the JSON file.
8
  if (configUrl) {
9
  try {
10
  const response = await fetch(configUrl);
@@ -17,7 +15,7 @@
17
  console.error("No config file provided. Please set a data-config attribute on the script tag.");
18
  return;
19
  }
20
-
21
  // --- Outer scope variables for camera state ---
22
  let cameraInstance = null;
23
  let controlsInstance = null;
@@ -26,17 +24,11 @@
26
 
27
  // Generate a unique identifier for this widget instance.
28
  const instanceId = Math.random().toString(36).substr(2, 8);
29
-
30
- // Use configuration parameters from the JSON file.
 
31
  var plyUrl = config.ply_url;
32
-
33
- // Optional parameters for zoom and rotation limits.
34
- // Defaults: zoom from 0 to 20; rotation from 0 to 360.
35
- var minZoom = parseFloat(config.minZoom || "0");
36
- var maxZoom = parseFloat(config.maxZoom || "20");
37
- var minAngle = parseFloat(config.minAngle || "0");
38
- var maxAngle = parseFloat(config.maxAngle || "360");
39
-
40
  // Determine the aspect ratio.
41
  // Default aspect: 1:1 (i.e. 100% padding-bottom)
42
  var aspectPercent = "100%";
@@ -55,7 +47,6 @@
55
  }
56
  }
57
  } else {
58
- // If no aspect parameter is provided, compute the aspect ratio from the parent element.
59
  var parentContainer = scriptTag.parentNode;
60
  var containerWidth = parentContainer.offsetWidth;
61
  var containerHeight = parentContainer.offsetHeight;
@@ -63,10 +54,16 @@
63
  aspectPercent = (containerHeight / containerWidth * 100) + "%";
64
  }
65
  }
66
-
 
 
 
 
 
 
67
  // Detect if the device is iOS.
68
  var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
69
-
70
  // Inject CSS styles into the document head, scoped with the unique id.
71
  var styleEl = document.createElement('style');
72
  styleEl.textContent = `
@@ -87,9 +84,26 @@
87
  padding-bottom: 0 !important;
88
  z-index: 9999 !important;
89
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  /* Viewer Container styling */
91
  #viewer-container-${instanceId} {
92
- display: block;
93
  position: absolute;
94
  top: 0;
95
  left: 0;
@@ -147,7 +161,11 @@
147
  align-items: center;
148
  justify-content: center;
149
  }
150
- /* Button positions */
 
 
 
 
151
  #fullscreen-toggle-${instanceId} {
152
  top: 17px;
153
  right: 15px;
@@ -156,33 +174,37 @@
156
  top: 72px;
157
  right: 15px;
158
  }
 
159
  #reset-camera-btn-${instanceId} {
160
  top: 127px;
161
  right: 15px;
162
- font-size: 22px;
163
- line-height: 1;
164
- padding: 0;
165
  }
166
  .reset-icon {
167
  display: inline-block;
 
168
  }
169
  `;
170
  document.head.appendChild(styleEl);
171
-
172
  // Create the widget container and set its inner HTML.
173
  var widgetContainer = document.createElement('div');
174
  widgetContainer.id = 'ply-widget-container-' + instanceId;
175
  widgetContainer.innerHTML = `
 
 
 
 
176
  <!-- Viewer Container -->
177
  <div id="viewer-container-${instanceId}">
178
  <canvas id="canvas-${instanceId}"></canvas>
179
  <div id="progress-dialog-${instanceId}">
180
  <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
181
  </div>
 
182
  <button id="fullscreen-toggle-${instanceId}" class="widget-button">⇱</button>
183
  <button id="help-toggle-${instanceId}" class="widget-button">?</button>
184
  <button id="reset-camera-btn-${instanceId}" class="widget-button">
185
- <span class="reset-icon">⟲</span>
186
  </button>
187
  <div id="menu-content-${instanceId}">
188
  - Rotate with right click<br>
@@ -192,9 +214,12 @@
192
  </div>
193
  `;
194
  scriptTag.parentNode.appendChild(widgetContainer);
195
-
196
  // Grab element references.
 
197
  var viewerContainer = document.getElementById('viewer-container-' + instanceId);
 
 
198
  var fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
199
  var helpToggle = document.getElementById('help-toggle-' + instanceId);
200
  var resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
@@ -202,9 +227,31 @@
202
  var canvas = document.getElementById('canvas-' + instanceId);
203
  var progressDialog = document.getElementById('progress-dialog-' + instanceId);
204
  var progressIndicator = document.getElementById('progress-indicator-' + instanceId);
205
-
 
 
 
 
 
206
  // --- Button Event Handlers ---
207
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  fullscreenToggle.addEventListener('click', function() {
209
  if (isIOS) {
210
  widgetContainer.classList.toggle('fake-fullscreen');
@@ -227,16 +274,20 @@
227
  }
228
  }
229
  });
230
-
231
  document.addEventListener('fullscreenchange', function() {
232
- fullscreenToggle.textContent = (document.fullscreenElement === widgetContainer) ? '⇲' : '⇱';
 
 
 
 
233
  });
234
-
235
  helpToggle.addEventListener('click', function(e) {
236
  e.stopPropagation();
237
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
238
  });
239
-
240
  resetCameraBtn.addEventListener('click', function() {
241
  console.log("Reset camera button clicked.");
242
  if (cameraInstance && initialCameraPosition && initialCameraRotation) {
@@ -250,29 +301,48 @@
250
  }
251
  }
252
  });
253
-
254
  // --- Initialize the 3D PLY Viewer ---
255
  async function initializeViewer() {
256
  const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
257
  progressDialog.style.display = 'block';
258
  const renderer = new SPLAT.WebGLRenderer(canvas);
259
  const scene = new SPLAT.Scene();
 
260
  const camera = new SPLAT.Camera();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  const controls = new SPLAT.OrbitControls(camera, canvas);
262
-
263
  cameraInstance = camera;
264
  controlsInstance = controls;
265
  initialCameraPosition = camera.position.clone();
266
  initialCameraRotation = camera.rotation.clone();
267
-
268
  canvas.style.background = "#FEFEFD";
269
  controls.maxZoom = maxZoom;
270
  controls.minZoom = minZoom;
271
  controls.minAngle = minAngle;
272
  controls.maxAngle = maxAngle;
273
-
274
  controls.update();
275
-
276
  try {
277
  await SPLAT.PLYLoader.LoadAsync(
278
  plyUrl,
@@ -286,22 +356,19 @@
286
  console.error("Error loading PLY file:", error);
287
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
288
  }
289
-
290
  const frame = () => {
291
  controls.update();
292
  renderer.render(scene, camera);
293
  requestAnimationFrame(frame);
294
  };
295
-
296
  const handleResize = () => {
297
  renderer.setSize(canvas.clientWidth, canvas.clientHeight);
298
  };
299
-
300
  handleResize();
301
  window.addEventListener("resize", handleResize);
302
  requestAnimationFrame(frame);
303
  }
304
-
305
- // Start the viewer.
306
- initializeViewer();
307
  })();
 
1
  (async function() {
2
+ // Retrieve the current script tag and the config URL from its data attribute.
3
  const scriptTag = document.currentScript;
4
  const configUrl = scriptTag.getAttribute("data-config");
5
  let config = {};
 
 
6
  if (configUrl) {
7
  try {
8
  const response = await fetch(configUrl);
 
15
  console.error("No config file provided. Please set a data-config attribute on the script tag.");
16
  return;
17
  }
18
+
19
  // --- Outer scope variables for camera state ---
20
  let cameraInstance = null;
21
  let controlsInstance = null;
 
24
 
25
  // Generate a unique identifier for this widget instance.
26
  const instanceId = Math.random().toString(36).substr(2, 8);
27
+
28
+ // Read required URLs from the config.
29
+ var gifUrl = config.gif_url;
30
  var plyUrl = config.ply_url;
31
+
 
 
 
 
 
 
 
32
  // Determine the aspect ratio.
33
  // Default aspect: 1:1 (i.e. 100% padding-bottom)
34
  var aspectPercent = "100%";
 
47
  }
48
  }
49
  } else {
 
50
  var parentContainer = scriptTag.parentNode;
51
  var containerWidth = parentContainer.offsetWidth;
52
  var containerHeight = parentContainer.offsetHeight;
 
54
  aspectPercent = (containerHeight / containerWidth * 100) + "%";
55
  }
56
  }
57
+
58
+ // Optional parameters for zoom and rotation limits.
59
+ var minZoom = parseFloat(config.minZoom || "0");
60
+ var maxZoom = parseFloat(config.maxZoom || "20");
61
+ var minAngle = parseFloat(config.minAngle || "0");
62
+ var maxAngle = parseFloat(config.maxAngle || "360");
63
+
64
  // Detect if the device is iOS.
65
  var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
66
+
67
  // Inject CSS styles into the document head, scoped with the unique id.
68
  var styleEl = document.createElement('style');
69
  styleEl.textContent = `
 
84
  padding-bottom: 0 !important;
85
  z-index: 9999 !important;
86
  }
87
+ /* GIF Preview styling */
88
+ #gif-preview-container-${instanceId} {
89
+ position: absolute;
90
+ top: 0;
91
+ left: 0;
92
+ width: 100%;
93
+ height: 100%;
94
+ border: 1px solid #474558;
95
+ border-radius: 10px;
96
+ overflow: hidden;
97
+ cursor: pointer;
98
+ }
99
+ #gif-preview-container-${instanceId} img {
100
+ width: 100%;
101
+ height: 100%;
102
+ object-fit: cover;
103
+ }
104
  /* Viewer Container styling */
105
  #viewer-container-${instanceId} {
106
+ display: none;
107
  position: absolute;
108
  top: 0;
109
  left: 0;
 
161
  align-items: center;
162
  justify-content: center;
163
  }
164
+ /* Positions: Close at top-left, fullscreen at top-right, help (instructions) below fullscreen */
165
+ #close-btn-${instanceId} {
166
+ top: 17px;
167
+ left: 15px;
168
+ }
169
  #fullscreen-toggle-${instanceId} {
170
  top: 17px;
171
  right: 15px;
 
174
  top: 72px;
175
  right: 15px;
176
  }
177
+ /* Reset Camera Button below the help button */
178
  #reset-camera-btn-${instanceId} {
179
  top: 127px;
180
  right: 15px;
 
 
 
181
  }
182
  .reset-icon {
183
  display: inline-block;
184
+ transform: translateY(-3px);
185
  }
186
  `;
187
  document.head.appendChild(styleEl);
188
+
189
  // Create the widget container and set its inner HTML.
190
  var widgetContainer = document.createElement('div');
191
  widgetContainer.id = 'ply-widget-container-' + instanceId;
192
  widgetContainer.innerHTML = `
193
+ <!-- GIF Preview Container -->
194
+ <div id="gif-preview-container-${instanceId}">
195
+ <img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
196
+ </div>
197
  <!-- Viewer Container -->
198
  <div id="viewer-container-${instanceId}">
199
  <canvas id="canvas-${instanceId}"></canvas>
200
  <div id="progress-dialog-${instanceId}">
201
  <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
202
  </div>
203
+ <button id="close-btn-${instanceId}" class="widget-button">X</button>
204
  <button id="fullscreen-toggle-${instanceId}" class="widget-button">⇱</button>
205
  <button id="help-toggle-${instanceId}" class="widget-button">?</button>
206
  <button id="reset-camera-btn-${instanceId}" class="widget-button">
207
+ <span class="reset-icon">🗘</span>
208
  </button>
209
  <div id="menu-content-${instanceId}">
210
  - Rotate with right click<br>
 
214
  </div>
215
  `;
216
  scriptTag.parentNode.appendChild(widgetContainer);
217
+
218
  // Grab element references.
219
+ var gifPreview = document.getElementById('gif-preview-container-' + instanceId);
220
  var viewerContainer = document.getElementById('viewer-container-' + instanceId);
221
+ var previewImage = document.getElementById('preview-image-' + instanceId);
222
+ var closeBtn = document.getElementById('close-btn-' + instanceId);
223
  var fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
224
  var helpToggle = document.getElementById('help-toggle-' + instanceId);
225
  var resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
 
227
  var canvas = document.getElementById('canvas-' + instanceId);
228
  var progressDialog = document.getElementById('progress-dialog-' + instanceId);
229
  var progressIndicator = document.getElementById('progress-indicator-' + instanceId);
230
+
231
+ // Set the preview image if provided.
232
+ if (gifUrl) {
233
+ previewImage.src = gifUrl;
234
+ }
235
+
236
  // --- Button Event Handlers ---
237
+
238
+ gifPreview.addEventListener('click', function() {
239
+ gifPreview.style.display = 'none';
240
+ viewerContainer.style.display = 'block';
241
+ initializeViewer();
242
+ });
243
+
244
+ closeBtn.addEventListener('click', function() {
245
+ if (document.fullscreenElement === widgetContainer) {
246
+ if (document.exitFullscreen) {
247
+ document.exitFullscreen();
248
+ }
249
+ }
250
+ widgetContainer.classList.remove('fake-fullscreen');
251
+ viewerContainer.style.display = 'none';
252
+ gifPreview.style.display = 'block';
253
+ });
254
+
255
  fullscreenToggle.addEventListener('click', function() {
256
  if (isIOS) {
257
  widgetContainer.classList.toggle('fake-fullscreen');
 
274
  }
275
  }
276
  });
277
+
278
  document.addEventListener('fullscreenchange', function() {
279
+ if (document.fullscreenElement === widgetContainer) {
280
+ fullscreenToggle.textContent = '⇲';
281
+ } else {
282
+ fullscreenToggle.textContent = '⇱';
283
+ }
284
  });
285
+
286
  helpToggle.addEventListener('click', function(e) {
287
  e.stopPropagation();
288
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
289
  });
290
+
291
  resetCameraBtn.addEventListener('click', function() {
292
  console.log("Reset camera button clicked.");
293
  if (cameraInstance && initialCameraPosition && initialCameraRotation) {
 
301
  }
302
  }
303
  });
304
+
305
  // --- Initialize the 3D PLY Viewer ---
306
  async function initializeViewer() {
307
  const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
308
  progressDialog.style.display = 'block';
309
  const renderer = new SPLAT.WebGLRenderer(canvas);
310
  const scene = new SPLAT.Scene();
311
+ // Create the camera instance.
312
  const camera = new SPLAT.Camera();
313
+
314
+ // If the config provides a camera position, update the camera.
315
+ if (config.cameraPosition) {
316
+ if (Array.isArray(config.cameraPosition) && config.cameraPosition.length === 3) {
317
+ camera.position.set(
318
+ config.cameraPosition[0],
319
+ config.cameraPosition[1],
320
+ config.cameraPosition[2]
321
+ );
322
+ } else if (typeof config.cameraPosition === 'object') {
323
+ camera.position.set(
324
+ config.cameraPosition.x,
325
+ config.cameraPosition.y,
326
+ config.cameraPosition.z
327
+ );
328
+ }
329
+ }
330
+
331
  const controls = new SPLAT.OrbitControls(camera, canvas);
332
+
333
  cameraInstance = camera;
334
  controlsInstance = controls;
335
  initialCameraPosition = camera.position.clone();
336
  initialCameraRotation = camera.rotation.clone();
337
+
338
  canvas.style.background = "#FEFEFD";
339
  controls.maxZoom = maxZoom;
340
  controls.minZoom = minZoom;
341
  controls.minAngle = minAngle;
342
  controls.maxAngle = maxAngle;
343
+
344
  controls.update();
345
+
346
  try {
347
  await SPLAT.PLYLoader.LoadAsync(
348
  plyUrl,
 
356
  console.error("Error loading PLY file:", error);
357
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
358
  }
359
+
360
  const frame = () => {
361
  controls.update();
362
  renderer.render(scene, camera);
363
  requestAnimationFrame(frame);
364
  };
365
+
366
  const handleResize = () => {
367
  renderer.setSize(canvas.clientWidth, canvas.clientHeight);
368
  };
369
+
370
  handleResize();
371
  window.addEventListener("resize", handleResize);
372
  requestAnimationFrame(frame);
373
  }
 
 
 
374
  })();