bilca commited on
Commit
07f865f
·
verified ·
1 Parent(s): be12020

Update index_2.js

Browse files
Files changed (1) hide show
  1. index_2.js +236 -121
index_2.js CHANGED
@@ -1,37 +1,90 @@
1
- (function() {
2
- // Helper: Get query parameters from THIS script’s src URL.
3
- function getScriptQueryParam(param) {
4
- var params = new URLSearchParams("");
5
- if (document.currentScript && document.currentScript.src.indexOf('?') !== -1) {
6
- var queryString = document.currentScript.src.split('?')[1];
7
- params = new URLSearchParams(queryString);
8
- } else {
9
- params = new URLSearchParams(window.location.search);
 
 
 
10
  }
11
- return params.get(param);
 
 
12
  }
13
 
14
- // Read required URLs and optional limits from the query parameters.
15
- var gifUrl = getScriptQueryParam("gif_url");
16
- var plyUrl = getScriptQueryParam("ply_url");
17
- // Optional parameters for zoom and angle limits:
18
- var minZoom = parseFloat(getScriptQueryParam("minZoom")) || 1.5;
19
- var maxZoom = parseFloat(getScriptQueryParam("maxZoom")) || 5;
20
- var minAngle = parseFloat(getScriptQueryParam("minAngle")) || 0;
21
- var maxAngle = parseFloat(getScriptQueryParam("maxAngle")) || 90;
 
 
 
 
 
 
 
 
22
 
23
- // Inject CSS styles into the document head.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  var styleEl = document.createElement('style');
25
  styleEl.textContent = `
26
  /* Widget container styling */
27
- #ply-widget-container {
28
  position: relative;
29
  width: 100%;
30
  height: 0;
31
- padding-bottom: 100%;
 
 
 
 
 
 
 
 
 
 
32
  }
33
  /* GIF Preview styling */
34
- #gif-preview-container {
35
  position: absolute;
36
  top: 0;
37
  left: 0;
@@ -42,31 +95,31 @@
42
  overflow: hidden;
43
  cursor: pointer;
44
  }
45
- #gif-preview-container img {
46
  width: 100%;
47
  height: 100%;
48
  object-fit: cover;
49
  }
50
  /* Viewer Container styling */
51
- #viewer-container {
52
  display: none;
53
  position: absolute;
54
  top: 0;
55
  left: 0;
56
  width: 100%;
57
  height: 100%;
58
- background: #ffffff; /* white background */
59
  border: 1px solid #474558;
60
  border-radius: 10px;
61
  }
62
  /* Canvas fills the viewer container */
63
- #canvas {
64
  width: 100%;
65
  height: 100%;
66
  display: block;
67
  }
68
  /* Progress dialog styling (as a centered div) */
69
- #progress-dialog {
70
  position: absolute;
71
  top: 50%;
72
  left: 50%;
@@ -79,11 +132,11 @@
79
  display: none;
80
  }
81
  /* Menu (instructions) content styling */
82
- #menu-content {
83
  display: none;
84
  position: absolute;
85
- top: 62px;
86
- right: 70px;
87
  background: #FFFEF4;
88
  border: 1px solid #474558;
89
  border-radius: 5px;
@@ -107,107 +160,138 @@
107
  align-items: center;
108
  justify-content: center;
109
  }
110
- /* Positions: Close at top-left, fullscreen at top-right, help (instructions) below fullscreen */
111
- #close-btn {
112
  top: 17px;
113
  left: 15px;
114
  }
115
- #fullscreen-toggle {
116
  top: 17px;
117
  right: 15px;
118
  }
119
- #help-toggle {
120
- top: 72px;
121
- right: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
  `;
124
  document.head.appendChild(styleEl);
125
-
126
  // Create the widget container and set its inner HTML.
127
  var widgetContainer = document.createElement('div');
128
- widgetContainer.id = 'ply-widget-container';
129
  widgetContainer.innerHTML = `
130
  <!-- GIF Preview Container -->
131
- <div id="gif-preview-container">
132
- <img id="preview-image" alt="Preview" crossorigin="anonymous">
133
  </div>
134
  <!-- Viewer Container -->
135
- <div id="viewer-container">
136
- <canvas id="canvas"></canvas>
137
- <div id="progress-dialog">
138
- <progress id="progress-indicator" max="100" value="0"></progress>
139
  </div>
140
- <button id="close-btn" class="widget-button">X</button>
141
- <button id="fullscreen-toggle" class="widget-button">⇱</button>
142
- <button id="help-toggle" class="widget-button">?</button>
143
- <div id="menu-content">
 
 
 
144
  - Rotate with right click<br>
145
  - Zoom in/out with middle click<br>
146
  - Translate with left click
147
  </div>
148
  </div>
149
  `;
150
- // Append widgetContainer to the current script's parent so it appears in place.
151
- document.currentScript.parentNode.appendChild(widgetContainer);
152
-
153
  // Grab element references.
154
- var gifPreview = document.getElementById('gif-preview-container');
155
- var viewerContainer = document.getElementById('viewer-container');
156
- var previewImage = document.getElementById('preview-image');
157
- var closeBtn = document.getElementById('close-btn');
158
- var fullscreenToggle = document.getElementById('fullscreen-toggle');
159
- var helpToggle = document.getElementById('help-toggle');
160
- var menuContent = document.getElementById('menu-content');
161
- var canvas = document.getElementById('canvas');
162
- var progressDialog = document.getElementById('progress-dialog');
163
- var progressIndicator = document.getElementById('progress-indicator');
164
-
165
- // Set the preview image if provided.
 
 
 
166
  if (gifUrl) {
167
  previewImage.src = gifUrl;
168
- }
169
-
170
- // --- Button Event Handlers ---
171
-
172
- // When the preview image is clicked, hide it, show the viewer, and initialize the 3D viewer.
173
- gifPreview.addEventListener('click', function() {
174
  gifPreview.style.display = 'none';
175
  viewerContainer.style.display = 'block';
 
 
176
  initializeViewer();
177
- });
178
-
179
- // Close button: hide the viewer and show the preview.
 
 
 
 
 
 
 
 
 
180
  closeBtn.addEventListener('click', function() {
181
- // Exit fullscreen if active.
182
  if (document.fullscreenElement === widgetContainer) {
183
  if (document.exitFullscreen) {
184
  document.exitFullscreen();
185
  }
186
  }
 
187
  viewerContainer.style.display = 'none';
188
  gifPreview.style.display = 'block';
189
  });
190
-
191
- // Fullscreen toggle: toggle fullscreen on the widget container.
192
  fullscreenToggle.addEventListener('click', function() {
193
- if (!document.fullscreenElement) {
194
- if (widgetContainer.requestFullscreen) {
195
- widgetContainer.requestFullscreen();
196
- } else if (widgetContainer.webkitRequestFullscreen) {
197
- widgetContainer.webkitRequestFullscreen();
198
- } else if (widgetContainer.mozRequestFullScreen) {
199
- widgetContainer.mozRequestFullScreen();
200
- } else if (widgetContainer.msRequestFullscreen) {
201
- widgetContainer.msRequestFullscreen();
202
  }
 
203
  } else {
204
- if (document.exitFullscreen) {
205
- document.exitFullscreen();
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
  }
208
  });
209
-
210
- // Update the fullscreen button icon on fullscreen change.
211
  document.addEventListener('fullscreenchange', function() {
212
  if (document.fullscreenElement === widgetContainer) {
213
  fullscreenToggle.textContent = '⇲';
@@ -215,47 +299,78 @@
215
  fullscreenToggle.textContent = '⇱';
216
  }
217
  });
218
-
219
- // Help (instructions) toggle: show/hide the instructions.
220
  helpToggle.addEventListener('click', function(e) {
221
  e.stopPropagation();
222
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
223
  });
224
-
225
- // --- Initialize the 3D PLY Viewer using PlayCanvas Model Viewer ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  async function initializeViewer() {
227
- // Dynamically import the PlayCanvas Model Viewer module.
228
- const { ModelViewer } = await import("https://cdn.jsdelivr.net/npm/@playcanvas/model-viewer/dist/model-viewer.module.js");
229
-
230
- // Display the progress dialog.
231
  progressDialog.style.display = 'block';
232
-
233
- // Create a new instance of the ModelViewer.
234
- // The options below set a white background and disable the grid.
235
- const viewer = new ModelViewer(canvas, {
236
- backgroundColor: '#ffffff',
237
- grid: false,
238
- // Pass zoom and orbit (angle) limits; note that the property names here
239
- // assume the ModelViewer accepts these options. Adjust names as per the library docs.
240
- minZoom: minZoom,
241
- maxZoom: maxZoom,
242
- minOrbitPitch: minAngle,
243
- maxOrbitPitch: maxAngle
244
- });
245
-
246
- // Load the PLY model from the provided URL.
247
- // Assume that the `load` method accepts a progress callback.
248
- viewer.load(plyUrl, function(progress) {
249
- progressIndicator.value = progress * 100;
250
- }).then(function() {
251
- // Hide the progress dialog once the model is loaded.
 
 
 
 
 
 
 
252
  progressDialog.style.display = 'none';
253
- }).catch(function(error) {
254
  console.error("Error loading PLY file:", error);
255
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
256
- });
257
-
258
- // Start the viewer's render loop.
259
- viewer.start();
 
 
 
 
 
 
 
 
 
 
 
260
  }
 
 
 
261
  })();
 
1
+ (async function() {
2
+ // Retrieve the current script tag and load the JSON configuration file from the data-config 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);
9
+ config = await response.json();
10
+ } catch (error) {
11
+ console.error("Error loading config file:", error);
12
+ return;
13
  }
14
+ } else {
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;
22
+ let initialCameraPosition = null;
23
+ let initialCameraRotation = null;
24
+
25
+ // Generate a unique identifier for this widget instance.
26
+ const instanceId = Math.random().toString(36).substr(2, 8);
27
+
28
+ // Read configuration values from the JSON file.
29
+ var gifUrl = config.gif_url;
30
+ var plyUrl = config.ply_url;
31
+ var minZoom = parseFloat(config.minZoom || "0");
32
+ var maxZoom = parseFloat(config.maxZoom || "20");
33
+ var minAngle = parseFloat(config.minAngle || "0");
34
+ var maxAngle = parseFloat(config.maxAngle || "360");
35
 
36
+ // Determine the aspect ratio.
37
+ // Default: 1:1 (i.e. 100% padding-bottom)
38
+ var aspectPercent = "100%";
39
+ if (config.aspect) {
40
+ if (config.aspect.indexOf(":") !== -1) {
41
+ var parts = config.aspect.split(":");
42
+ var w = parseFloat(parts[0]);
43
+ var h = parseFloat(parts[1]);
44
+ if (!isNaN(w) && !isNaN(h) && w > 0) {
45
+ aspectPercent = (h / w * 100) + "%";
46
+ }
47
+ } else {
48
+ var aspectValue = parseFloat(config.aspect);
49
+ if (!isNaN(aspectValue) && aspectValue > 0) {
50
+ aspectPercent = (100 / aspectValue) + "%";
51
+ }
52
+ }
53
+ } else {
54
+ // If no aspect is provided, compute from the parent container.
55
+ var parentContainer = scriptTag.parentNode;
56
+ var containerWidth = parentContainer.offsetWidth;
57
+ var containerHeight = parentContainer.offsetHeight;
58
+ if (containerWidth > 0 && containerHeight > 0) {
59
+ aspectPercent = (containerHeight / containerWidth * 100) + "%";
60
+ }
61
+ }
62
+
63
+ // Detect if the device is iOS.
64
+ var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
65
+
66
+ // Inject CSS styles into the document head, scoped with the unique id.
67
  var styleEl = document.createElement('style');
68
  styleEl.textContent = `
69
  /* Widget container styling */
70
+ #ply-widget-container-${instanceId} {
71
  position: relative;
72
  width: 100%;
73
  height: 0;
74
+ padding-bottom: ${aspectPercent};
75
+ }
76
+ /* When in fake fullscreen mode (iOS fallback) */
77
+ #ply-widget-container-${instanceId}.fake-fullscreen {
78
+ position: fixed !important;
79
+ top: 0 !important;
80
+ left: 0 !important;
81
+ width: 100vw !important;
82
+ height: 100vh !important;
83
+ padding-bottom: 0 !important;
84
+ z-index: 9999 !important;
85
  }
86
  /* GIF Preview styling */
87
+ #gif-preview-container-${instanceId} {
88
  position: absolute;
89
  top: 0;
90
  left: 0;
 
95
  overflow: hidden;
96
  cursor: pointer;
97
  }
98
+ #gif-preview-container-${instanceId} img {
99
  width: 100%;
100
  height: 100%;
101
  object-fit: cover;
102
  }
103
  /* Viewer Container styling */
104
+ #viewer-container-${instanceId} {
105
  display: none;
106
  position: absolute;
107
  top: 0;
108
  left: 0;
109
  width: 100%;
110
  height: 100%;
111
+ background: #FEFEFD;
112
  border: 1px solid #474558;
113
  border-radius: 10px;
114
  }
115
  /* Canvas fills the viewer container */
116
+ #canvas-${instanceId} {
117
  width: 100%;
118
  height: 100%;
119
  display: block;
120
  }
121
  /* Progress dialog styling (as a centered div) */
122
+ #progress-dialog-${instanceId} {
123
  position: absolute;
124
  top: 50%;
125
  left: 50%;
 
132
  display: none;
133
  }
134
  /* Menu (instructions) content styling */
135
+ #menu-content-${instanceId} {
136
  display: none;
137
  position: absolute;
138
+ top: 70px;
139
+ right: 15px;
140
  background: #FFFEF4;
141
  border: 1px solid #474558;
142
  border-radius: 5px;
 
160
  align-items: center;
161
  justify-content: center;
162
  }
163
+ /* Positions: Close at top-left, fullscreen at top-right, help and reset buttons */
164
+ #close-btn-${instanceId} {
165
  top: 17px;
166
  left: 15px;
167
  }
168
+ #fullscreen-toggle-${instanceId} {
169
  top: 17px;
170
  right: 15px;
171
  }
172
+ #help-toggle-${instanceId} {
173
+ top: 17px;
174
+ right: 70px;
175
+ font-size: 22px;
176
+ }
177
+ #reset-camera-btn-${instanceId} {
178
+ top: 17px;
179
+ right: 123px;
180
+ font-size: 22px;
181
+ line-height: normal;
182
+ padding: 0;
183
+ }
184
+ /* Adjust the reset icon position: move it slightly upward */
185
+ .reset-icon {
186
+ display: inline-block;
187
+ transform: translateY(-3px);
188
  }
189
  `;
190
  document.head.appendChild(styleEl);
191
+
192
  // Create the widget container and set its inner HTML.
193
  var widgetContainer = document.createElement('div');
194
+ widgetContainer.id = 'ply-widget-container-' + instanceId;
195
  widgetContainer.innerHTML = `
196
  <!-- GIF Preview Container -->
197
+ <div id="gif-preview-container-${instanceId}">
198
+ <img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
199
  </div>
200
  <!-- Viewer Container -->
201
+ <div id="viewer-container-${instanceId}">
202
+ <canvas id="canvas-${instanceId}"></canvas>
203
+ <div id="progress-dialog-${instanceId}">
204
+ <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
205
  </div>
206
+ <button id="close-btn-${instanceId}" class="widget-button">X</button>
207
+ <button id="fullscreen-toggle-${instanceId}" class="widget-button">⇱</button>
208
+ <button id="help-toggle-${instanceId}" class="widget-button">?</button>
209
+ <button id="reset-camera-btn-${instanceId}" class="widget-button">
210
+ <span class="reset-icon">⟲</span>
211
+ </button>
212
+ <div id="menu-content-${instanceId}">
213
  - Rotate with right click<br>
214
  - Zoom in/out with middle click<br>
215
  - Translate with left click
216
  </div>
217
  </div>
218
  `;
219
+ scriptTag.parentNode.appendChild(widgetContainer);
220
+
 
221
  // Grab element references.
222
+ var gifPreview = document.getElementById('gif-preview-container-' + instanceId);
223
+ var viewerContainer = document.getElementById('viewer-container-' + instanceId);
224
+ var previewImage = document.getElementById('preview-image-' + instanceId);
225
+ var closeBtn = document.getElementById('close-btn-' + instanceId);
226
+ var fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
227
+ var helpToggle = document.getElementById('help-toggle-' + instanceId);
228
+ var resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
229
+ var menuContent = document.getElementById('menu-content-' + instanceId);
230
+ var canvas = document.getElementById('canvas-' + instanceId);
231
+ var progressDialog = document.getElementById('progress-dialog-' + instanceId);
232
+ var progressIndicator = document.getElementById('progress-indicator-' + instanceId);
233
+
234
+ // If a gif_url is provided, set the preview image.
235
+ // Otherwise, hide the preview container, show the viewer immediately,
236
+ // and hide the "close" button since there's no preview to return to.
237
  if (gifUrl) {
238
  previewImage.src = gifUrl;
239
+ } else {
 
 
 
 
 
240
  gifPreview.style.display = 'none';
241
  viewerContainer.style.display = 'block';
242
+ closeBtn.style.display = 'none';
243
+ // Start the viewer immediately.
244
  initializeViewer();
245
+ }
246
+
247
+ // --- Button Event Handlers ---
248
+ // Only add the preview click handler if a gif_url exists.
249
+ if (gifUrl) {
250
+ gifPreview.addEventListener('click', function() {
251
+ gifPreview.style.display = 'none';
252
+ viewerContainer.style.display = 'block';
253
+ initializeViewer();
254
+ });
255
+ }
256
+
257
  closeBtn.addEventListener('click', function() {
 
258
  if (document.fullscreenElement === widgetContainer) {
259
  if (document.exitFullscreen) {
260
  document.exitFullscreen();
261
  }
262
  }
263
+ widgetContainer.classList.remove('fake-fullscreen');
264
  viewerContainer.style.display = 'none';
265
  gifPreview.style.display = 'block';
266
  });
267
+
 
268
  fullscreenToggle.addEventListener('click', function() {
269
+ if (isIOS) {
270
+ if (!widgetContainer.classList.contains('fake-fullscreen')) {
271
+ widgetContainer.classList.add('fake-fullscreen');
272
+ } else {
273
+ widgetContainer.classList.remove('fake-fullscreen');
 
 
 
 
274
  }
275
+ fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
276
  } else {
277
+ if (!document.fullscreenElement) {
278
+ if (widgetContainer.requestFullscreen) {
279
+ widgetContainer.requestFullscreen();
280
+ } else if (widgetContainer.webkitRequestFullscreen) {
281
+ widgetContainer.webkitRequestFullscreen();
282
+ } else if (widgetContainer.mozRequestFullScreen) {
283
+ widgetContainer.mozRequestFullScreen();
284
+ } else if (widgetContainer.msRequestFullscreen) {
285
+ widgetContainer.msRequestFullscreen();
286
+ }
287
+ } else {
288
+ if (document.exitFullscreen) {
289
+ document.exitFullscreen();
290
+ }
291
  }
292
  }
293
  });
294
+
 
295
  document.addEventListener('fullscreenchange', function() {
296
  if (document.fullscreenElement === widgetContainer) {
297
  fullscreenToggle.textContent = '⇲';
 
299
  fullscreenToggle.textContent = '⇱';
300
  }
301
  });
302
+
 
303
  helpToggle.addEventListener('click', function(e) {
304
  e.stopPropagation();
305
  menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
306
  });
307
+
308
+ resetCameraBtn.addEventListener('click', function() {
309
+ console.log("Reset camera button clicked.");
310
+ if (cameraInstance && initialCameraPosition && initialCameraRotation) {
311
+ cameraInstance.position = initialCameraPosition.clone();
312
+ cameraInstance.rotation = initialCameraRotation.clone();
313
+ if (typeof cameraInstance.update === 'function') {
314
+ cameraInstance.update();
315
+ }
316
+ if (controlsInstance && typeof controlsInstance.update === 'function') {
317
+ controlsInstance.update();
318
+ }
319
+ }
320
+ });
321
+
322
+ // --- Initialize the 3D PLY Viewer ---
323
  async function initializeViewer() {
324
+ const SPLAT = await import("https://cdn.jsdelivr.net/npm/gsplat@latest");
 
 
 
325
  progressDialog.style.display = 'block';
326
+ const renderer = new SPLAT.WebGLRenderer(canvas);
327
+ const scene = new SPLAT.Scene();
328
+ const camera = new SPLAT.Camera();
329
+ const controls = new SPLAT.OrbitControls(camera, canvas);
330
+
331
+ cameraInstance = camera;
332
+ controlsInstance = controls;
333
+ // Capture the camera's default initial position and rotation.
334
+ initialCameraPosition = camera.position.clone();
335
+ initialCameraRotation = camera.rotation.clone();
336
+
337
+ canvas.style.background = "#FEFEFD";
338
+ controls.maxZoom = maxZoom;
339
+ controls.minZoom = minZoom;
340
+ controls.minAngle = minAngle;
341
+ controls.maxAngle = maxAngle;
342
+
343
+ controls.update();
344
+
345
+ try {
346
+ await SPLAT.PLYLoader.LoadAsync(
347
+ plyUrl,
348
+ scene,
349
+ (progress) => {
350
+ progressIndicator.value = progress * 100;
351
+ }
352
+ );
353
  progressDialog.style.display = 'none';
354
+ } catch (error) {
355
  console.error("Error loading PLY file:", error);
356
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
357
+ }
358
+
359
+ const frame = () => {
360
+ controls.update();
361
+ renderer.render(scene, camera);
362
+ requestAnimationFrame(frame);
363
+ };
364
+
365
+ const handleResize = () => {
366
+ renderer.setSize(canvas.clientWidth, canvas.clientHeight);
367
+ };
368
+
369
+ handleResize();
370
+ window.addEventListener("resize", handleResize);
371
+ requestAnimationFrame(frame);
372
  }
373
+
374
+ // If a gif_url exists, the viewer is started on preview click;
375
+ // otherwise, it was already started above.
376
  })();