Spaces:
Running
Running
Update js_scripts/index.js
Browse files- js_scripts/index.js +92 -33
js_scripts/index.js
CHANGED
@@ -61,6 +61,7 @@ const currentScriptTag = document.currentScript;
|
|
61 |
let viewerInitialized = false;
|
62 |
let wheelHandlers = [];
|
63 |
let resizeHandler = null;
|
|
|
64 |
|
65 |
// Generate a unique identifier for this widget instance.
|
66 |
const instanceId = Math.random().toString(36).substr(2, 8);
|
@@ -151,7 +152,7 @@ const currentScriptTag = document.currentScript;
|
|
151 |
<img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
|
152 |
</div>
|
153 |
<!-- Viewer Container -->
|
154 |
-
<div id="viewer-container-${instanceId}" class="viewer-container">
|
155 |
<canvas id="canvas-${instanceId}" class="ply-canvas"></canvas>
|
156 |
<div id="progress-dialog-${instanceId}" class="progress-dialog">
|
157 |
<progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
|
@@ -207,31 +208,46 @@ const currentScriptTag = document.currentScript;
|
|
207 |
`;
|
208 |
}
|
209 |
|
210 |
-
//
|
211 |
-
// Otherwise, hide the preview container, show the viewer immediately,
|
212 |
-
// and hide the "close" button since there's no preview to return to.
|
213 |
if (gifUrl) {
|
214 |
previewImage.src = gifUrl;
|
215 |
-
viewerContainer.style.display = 'none';
|
216 |
gifPreview.style.display = 'block';
|
|
|
217 |
} else {
|
218 |
gifPreview.style.display = 'none';
|
219 |
viewerContainer.style.display = 'block';
|
220 |
closeBtn.style.display = 'none';
|
221 |
-
|
|
|
|
|
|
|
222 |
}
|
223 |
|
224 |
// --- Button Event Handlers ---
|
225 |
if (gifUrl) {
|
|
|
226 |
gifPreview.addEventListener('click', function() {
|
|
|
227 |
gifPreview.style.display = 'none';
|
228 |
viewerContainer.style.display = 'block';
|
229 |
-
|
|
|
|
|
|
|
|
|
230 |
});
|
231 |
}
|
232 |
|
233 |
-
// Function to clean up the viewer
|
234 |
function cleanupViewer() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
// Remove wheel event listeners
|
236 |
for (const handler of wheelHandlers) {
|
237 |
const [element, func] = handler;
|
@@ -245,24 +261,31 @@ const currentScriptTag = document.currentScript;
|
|
245 |
resizeHandler = null;
|
246 |
}
|
247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
// Destroy PlayCanvas app if it exists
|
249 |
if (app) {
|
250 |
-
|
|
|
|
|
|
|
|
|
251 |
app = null;
|
252 |
}
|
253 |
|
254 |
// Reset entities
|
255 |
cameraEntity = null;
|
256 |
modelEntity = null;
|
257 |
-
viewerInitialized = false;
|
258 |
-
|
259 |
-
// Reset the canvas
|
260 |
-
if (canvas) {
|
261 |
-
const ctx = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
262 |
-
if (ctx) {
|
263 |
-
ctx.getExtension('WEBGL_lose_context')?.loseContext();
|
264 |
-
}
|
265 |
-
}
|
266 |
|
267 |
// Mark the viewer as not initialized
|
268 |
viewerInitialized = false;
|
@@ -270,7 +293,10 @@ const currentScriptTag = document.currentScript;
|
|
270 |
console.log("Viewer cleanup complete");
|
271 |
}
|
272 |
|
|
|
273 |
closeBtn.addEventListener('click', function() {
|
|
|
|
|
274 |
// Handle fullscreen exit
|
275 |
if (document.fullscreenElement === widgetContainer) {
|
276 |
if (document.exitFullscreen) {
|
@@ -290,6 +316,7 @@ const currentScriptTag = document.currentScript;
|
|
290 |
gifPreview.style.display = 'block';
|
291 |
});
|
292 |
|
|
|
293 |
fullscreenToggle.addEventListener('click', function() {
|
294 |
if (isIOS) {
|
295 |
if (!widgetContainer.classList.contains('fake-fullscreen')) {
|
@@ -333,6 +360,7 @@ const currentScriptTag = document.currentScript;
|
|
333 |
}
|
334 |
});
|
335 |
|
|
|
336 |
helpToggle.addEventListener('click', function(e) {
|
337 |
e.stopPropagation();
|
338 |
menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
|
@@ -340,12 +368,18 @@ const currentScriptTag = document.currentScript;
|
|
340 |
|
341 |
// --- Camera Reset Function ---
|
342 |
function resetCamera() {
|
343 |
-
if (!cameraEntity || !modelEntity || !app)
|
|
|
|
|
|
|
344 |
|
345 |
try {
|
346 |
// Get the orbit camera script
|
347 |
const orbitCam = cameraEntity.script.orbitCamera;
|
348 |
-
if (!orbitCam)
|
|
|
|
|
|
|
349 |
|
350 |
// Store model position
|
351 |
const modelPos = modelEntity.getPosition();
|
@@ -399,16 +433,20 @@ const currentScriptTag = document.currentScript;
|
|
399 |
// Clean up
|
400 |
tempEntity.destroy();
|
401 |
|
|
|
|
|
402 |
} catch (error) {
|
403 |
console.error("Error resetting camera:", error);
|
404 |
}
|
405 |
}
|
406 |
|
|
|
407 |
resetCameraBtn.addEventListener('click', function() {
|
408 |
-
console.log("Reset camera button clicked
|
409 |
resetCamera();
|
410 |
});
|
411 |
|
|
|
412 |
document.addEventListener('keydown', function(e) {
|
413 |
if (e.key === 'Escape' || e.key === 'Esc') {
|
414 |
let wasFullscreen = false;
|
@@ -472,18 +510,21 @@ const currentScriptTag = document.currentScript;
|
|
472 |
// Store handlers for later cleanup
|
473 |
wheelHandlers.push([viewerContainer, handleWheel]);
|
474 |
wheelHandlers.push([canvas, handleWheel]);
|
|
|
|
|
475 |
}
|
476 |
|
477 |
// --- Initialize the 3D PLY Viewer using PlayCanvas ---
|
478 |
async function initializeViewer() {
|
479 |
// Skip initialization if already initialized
|
480 |
-
if (viewerInitialized) {
|
481 |
-
console.log("Viewer already initialized, skipping");
|
482 |
return;
|
483 |
}
|
484 |
|
485 |
console.log("Initializing PLY viewer...");
|
486 |
progressDialog.style.display = 'block';
|
|
|
487 |
|
488 |
// Initialize PlayCanvas
|
489 |
const deviceType = "webgl2";
|
@@ -502,8 +543,8 @@ const currentScriptTag = document.currentScript;
|
|
502 |
// Create app
|
503 |
const createOptions = new pc.AppOptions();
|
504 |
createOptions.graphicsDevice = device;
|
505 |
-
createOptions.mouse = new pc.Mouse(canvas);
|
506 |
-
createOptions.touch = new pc.TouchDevice(canvas);
|
507 |
createOptions.componentSystems = [
|
508 |
pc.RenderComponentSystem,
|
509 |
pc.CameraComponentSystem,
|
@@ -539,9 +580,17 @@ const currentScriptTag = document.currentScript;
|
|
539 |
// Store resize handler for cleanup
|
540 |
resizeHandler = resize;
|
541 |
window.addEventListener('resize', resizeHandler);
|
|
|
|
|
542 |
app.on('destroy', () => {
|
543 |
-
|
544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
545 |
});
|
546 |
|
547 |
// Load required assets
|
@@ -557,18 +606,27 @@ const currentScriptTag = document.currentScript;
|
|
557 |
let lastProgress = 0;
|
558 |
assets.model.on('load', (asset) => {
|
559 |
progressDialog.style.display = 'none';
|
|
|
560 |
});
|
561 |
|
562 |
assets.model.on('error', (err) => {
|
563 |
console.error("Error loading PLY file:", err);
|
564 |
progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
|
|
|
565 |
});
|
566 |
|
567 |
// Set up progress monitoring
|
568 |
const checkProgress = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
if (app && assets.model.resource) {
|
570 |
progressIndicator.value = 100;
|
571 |
clearInterval(progressChecker);
|
|
|
572 |
progressDialog.style.display = 'none';
|
573 |
} else if (assets.model.loading) {
|
574 |
// Increment progress for visual feedback
|
@@ -578,15 +636,15 @@ const currentScriptTag = document.currentScript;
|
|
578 |
}
|
579 |
};
|
580 |
|
581 |
-
|
582 |
-
|
583 |
-
// Store the interval for cleanup
|
584 |
-
app.once('destroy', () => {
|
585 |
-
clearInterval(progressChecker);
|
586 |
-
});
|
587 |
|
588 |
// Load assets and set up scene
|
589 |
assetListLoader.load(() => {
|
|
|
|
|
|
|
|
|
|
|
590 |
app.start();
|
591 |
|
592 |
// Create model entity
|
@@ -693,6 +751,7 @@ const currentScriptTag = document.currentScript;
|
|
693 |
console.error("Error initializing PlayCanvas viewer:", error);
|
694 |
progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
|
695 |
viewerInitialized = false;
|
|
|
696 |
}
|
697 |
}
|
698 |
})();
|
|
|
61 |
let viewerInitialized = false;
|
62 |
let wheelHandlers = [];
|
63 |
let resizeHandler = null;
|
64 |
+
let progressChecker = null;
|
65 |
|
66 |
// Generate a unique identifier for this widget instance.
|
67 |
const instanceId = Math.random().toString(36).substr(2, 8);
|
|
|
152 |
<img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
|
153 |
</div>
|
154 |
<!-- Viewer Container -->
|
155 |
+
<div id="viewer-container-${instanceId}" class="viewer-container" style="display: none;">
|
156 |
<canvas id="canvas-${instanceId}" class="ply-canvas"></canvas>
|
157 |
<div id="progress-dialog-${instanceId}" class="progress-dialog">
|
158 |
<progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
|
|
|
208 |
`;
|
209 |
}
|
210 |
|
211 |
+
// Handle GIF configuration
|
|
|
|
|
212 |
if (gifUrl) {
|
213 |
previewImage.src = gifUrl;
|
|
|
214 |
gifPreview.style.display = 'block';
|
215 |
+
viewerContainer.style.display = 'none';
|
216 |
} else {
|
217 |
gifPreview.style.display = 'none';
|
218 |
viewerContainer.style.display = 'block';
|
219 |
closeBtn.style.display = 'none';
|
220 |
+
// Initialize the viewer only when needed
|
221 |
+
setTimeout(() => {
|
222 |
+
initializeViewer();
|
223 |
+
}, 100);
|
224 |
}
|
225 |
|
226 |
// --- Button Event Handlers ---
|
227 |
if (gifUrl) {
|
228 |
+
// Add click event to the GIF container
|
229 |
gifPreview.addEventListener('click', function() {
|
230 |
+
console.log("GIF preview clicked, showing 3D viewer");
|
231 |
gifPreview.style.display = 'none';
|
232 |
viewerContainer.style.display = 'block';
|
233 |
+
|
234 |
+
// Make sure we're not initializing again if already done
|
235 |
+
if (!viewerInitialized) {
|
236 |
+
initializeViewer();
|
237 |
+
}
|
238 |
});
|
239 |
}
|
240 |
|
241 |
+
// Function to clean up the viewer completely
|
242 |
function cleanupViewer() {
|
243 |
+
console.log("Starting viewer cleanup...");
|
244 |
+
|
245 |
+
// Clear any running intervals
|
246 |
+
if (progressChecker) {
|
247 |
+
clearInterval(progressChecker);
|
248 |
+
progressChecker = null;
|
249 |
+
}
|
250 |
+
|
251 |
// Remove wheel event listeners
|
252 |
for (const handler of wheelHandlers) {
|
253 |
const [element, func] = handler;
|
|
|
261 |
resizeHandler = null;
|
262 |
}
|
263 |
|
264 |
+
// Reset the canvas context
|
265 |
+
if (canvas) {
|
266 |
+
const ctx = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
267 |
+
if (ctx && ctx.getExtension('WEBGL_lose_context')) {
|
268 |
+
try {
|
269 |
+
ctx.getExtension('WEBGL_lose_context').loseContext();
|
270 |
+
} catch (e) {
|
271 |
+
console.error("Error releasing WebGL context:", e);
|
272 |
+
}
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
// Destroy PlayCanvas app if it exists
|
277 |
if (app) {
|
278 |
+
try {
|
279 |
+
app.destroy();
|
280 |
+
} catch (e) {
|
281 |
+
console.error("Error destroying PlayCanvas app:", e);
|
282 |
+
}
|
283 |
app = null;
|
284 |
}
|
285 |
|
286 |
// Reset entities
|
287 |
cameraEntity = null;
|
288 |
modelEntity = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
|
290 |
// Mark the viewer as not initialized
|
291 |
viewerInitialized = false;
|
|
|
293 |
console.log("Viewer cleanup complete");
|
294 |
}
|
295 |
|
296 |
+
// Close button event handler
|
297 |
closeBtn.addEventListener('click', function() {
|
298 |
+
console.log("Close button clicked");
|
299 |
+
|
300 |
// Handle fullscreen exit
|
301 |
if (document.fullscreenElement === widgetContainer) {
|
302 |
if (document.exitFullscreen) {
|
|
|
316 |
gifPreview.style.display = 'block';
|
317 |
});
|
318 |
|
319 |
+
// Fullscreen toggle handler
|
320 |
fullscreenToggle.addEventListener('click', function() {
|
321 |
if (isIOS) {
|
322 |
if (!widgetContainer.classList.contains('fake-fullscreen')) {
|
|
|
360 |
}
|
361 |
});
|
362 |
|
363 |
+
// Help toggle button
|
364 |
helpToggle.addEventListener('click', function(e) {
|
365 |
e.stopPropagation();
|
366 |
menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
|
|
|
368 |
|
369 |
// --- Camera Reset Function ---
|
370 |
function resetCamera() {
|
371 |
+
if (!cameraEntity || !modelEntity || !app) {
|
372 |
+
console.log("Cannot reset camera - missing entities or app");
|
373 |
+
return;
|
374 |
+
}
|
375 |
|
376 |
try {
|
377 |
// Get the orbit camera script
|
378 |
const orbitCam = cameraEntity.script.orbitCamera;
|
379 |
+
if (!orbitCam) {
|
380 |
+
console.log("Cannot reset camera - missing orbit camera script");
|
381 |
+
return;
|
382 |
+
}
|
383 |
|
384 |
// Store model position
|
385 |
const modelPos = modelEntity.getPosition();
|
|
|
433 |
// Clean up
|
434 |
tempEntity.destroy();
|
435 |
|
436 |
+
console.log("Camera reset complete");
|
437 |
+
|
438 |
} catch (error) {
|
439 |
console.error("Error resetting camera:", error);
|
440 |
}
|
441 |
}
|
442 |
|
443 |
+
// Reset camera button event handler
|
444 |
resetCameraBtn.addEventListener('click', function() {
|
445 |
+
console.log("Reset camera button clicked");
|
446 |
resetCamera();
|
447 |
});
|
448 |
|
449 |
+
// Escape key handler for fullscreen exit
|
450 |
document.addEventListener('keydown', function(e) {
|
451 |
if (e.key === 'Escape' || e.key === 'Esc') {
|
452 |
let wasFullscreen = false;
|
|
|
510 |
// Store handlers for later cleanup
|
511 |
wheelHandlers.push([viewerContainer, handleWheel]);
|
512 |
wheelHandlers.push([canvas, handleWheel]);
|
513 |
+
|
514 |
+
console.log("Wheel handlers set up");
|
515 |
}
|
516 |
|
517 |
// --- Initialize the 3D PLY Viewer using PlayCanvas ---
|
518 |
async function initializeViewer() {
|
519 |
// Skip initialization if already initialized
|
520 |
+
if (viewerInitialized || app) {
|
521 |
+
console.log("Viewer already initialized or app exists, skipping initialization");
|
522 |
return;
|
523 |
}
|
524 |
|
525 |
console.log("Initializing PLY viewer...");
|
526 |
progressDialog.style.display = 'block';
|
527 |
+
progressIndicator.value = 0;
|
528 |
|
529 |
// Initialize PlayCanvas
|
530 |
const deviceType = "webgl2";
|
|
|
543 |
// Create app
|
544 |
const createOptions = new pc.AppOptions();
|
545 |
createOptions.graphicsDevice = device;
|
546 |
+
createOptions.mouse = new pc.Mouse(canvas);
|
547 |
+
createOptions.touch = new pc.TouchDevice(canvas);
|
548 |
createOptions.componentSystems = [
|
549 |
pc.RenderComponentSystem,
|
550 |
pc.CameraComponentSystem,
|
|
|
580 |
// Store resize handler for cleanup
|
581 |
resizeHandler = resize;
|
582 |
window.addEventListener('resize', resizeHandler);
|
583 |
+
|
584 |
+
// Add cleanup when app is destroyed
|
585 |
app.on('destroy', () => {
|
586 |
+
if (resizeHandler) {
|
587 |
+
window.removeEventListener('resize', resizeHandler);
|
588 |
+
resizeHandler = null;
|
589 |
+
}
|
590 |
+
if (progressChecker) {
|
591 |
+
clearInterval(progressChecker);
|
592 |
+
progressChecker = null;
|
593 |
+
}
|
594 |
});
|
595 |
|
596 |
// Load required assets
|
|
|
606 |
let lastProgress = 0;
|
607 |
assets.model.on('load', (asset) => {
|
608 |
progressDialog.style.display = 'none';
|
609 |
+
console.log("Model loaded successfully");
|
610 |
});
|
611 |
|
612 |
assets.model.on('error', (err) => {
|
613 |
console.error("Error loading PLY file:", err);
|
614 |
progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
|
615 |
+
viewerInitialized = false;
|
616 |
});
|
617 |
|
618 |
// Set up progress monitoring
|
619 |
const checkProgress = () => {
|
620 |
+
if (!app) {
|
621 |
+
clearInterval(progressChecker);
|
622 |
+
progressChecker = null;
|
623 |
+
return;
|
624 |
+
}
|
625 |
+
|
626 |
if (app && assets.model.resource) {
|
627 |
progressIndicator.value = 100;
|
628 |
clearInterval(progressChecker);
|
629 |
+
progressChecker = null;
|
630 |
progressDialog.style.display = 'none';
|
631 |
} else if (assets.model.loading) {
|
632 |
// Increment progress for visual feedback
|
|
|
636 |
}
|
637 |
};
|
638 |
|
639 |
+
progressChecker = setInterval(checkProgress, 100);
|
|
|
|
|
|
|
|
|
|
|
640 |
|
641 |
// Load assets and set up scene
|
642 |
assetListLoader.load(() => {
|
643 |
+
if (!app) {
|
644 |
+
console.log("App was destroyed during asset loading");
|
645 |
+
return;
|
646 |
+
}
|
647 |
+
|
648 |
app.start();
|
649 |
|
650 |
// Create model entity
|
|
|
751 |
console.error("Error initializing PlayCanvas viewer:", error);
|
752 |
progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
|
753 |
viewerInitialized = false;
|
754 |
+
app = null;
|
755 |
}
|
756 |
}
|
757 |
})();
|