MikaFil commited on
Commit
28e2b17
Β·
verified Β·
1 Parent(s): 1b64a80

Update viewer.js

Browse files
Files changed (1) hide show
  1. viewer.js +4 -411
viewer.js CHANGED
@@ -153,7 +153,7 @@ export async function initializeViewer(config, instanceId) {
153
  orbit: new pc.Asset('script', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" }),
154
  galerie: new pc.Asset('galerie', 'container', { url: glbUrl }),
155
  hdr: new pc.Asset('hdr', 'texture', {
156
- url: https://huggingface.co/datasets/bilca/ply_files/resolve/main/galeries/blanc.png
157
  }, { type: pc.TEXTURETYPE_RGBP, mipmaps: false })
158
  };
159
 
@@ -162,7 +162,7 @@ export async function initializeViewer(config, instanceId) {
162
  assets.model.on('load', () => progressDialog.style.display = 'none');
163
  assets.model.on('error', err => {
164
  console.error("Error loading PLY file:", err);
165
- progressDialog.innerHTML = <p style="color: red">Error loading model: ${err}</p>;
166
  });
167
 
168
  const progCheck = setInterval(() => {
@@ -291,7 +291,7 @@ export async function initializeViewer(config, instanceId) {
291
 
292
  } catch (error) {
293
  console.error("Error initializing PlayCanvas viewer:", error);
294
- progressDialog.innerHTML = <p style="color: red">Error loading viewer: ${error.message}</p>;
295
  }
296
  }
297
 
@@ -357,411 +357,4 @@ export function cleanupViewer() {
357
  resizeObserver.disconnect();
358
  resizeObserver = null;
359
  }
360
- }
361
-
362
- interface.js :
363
-
364
- // interface.js
365
- // ==============================
366
- // interface.js
367
- // ==============================
368
-
369
- // Store a reference to the <script> tag that loaded this file
370
- const currentScriptTag = document.currentScript;
371
-
372
- (async function() {
373
- // ─── 1. Locate the <script> and read data-config ───────────────────────────────
374
- let scriptTag = currentScriptTag;
375
- if (!scriptTag) {
376
- const scripts = document.getElementsByTagName('script');
377
- for (let i = 0; i < scripts.length; i++) {
378
- if (scripts[i].src.includes('interface.js') && scripts[i].hasAttribute('data-config')) {
379
- scriptTag = scripts[i];
380
- break;
381
- }
382
- }
383
- if (!scriptTag && scripts.length > 0) {
384
- scriptTag = scripts[scripts.length - 1];
385
- }
386
- }
387
-
388
- const configUrl = scriptTag.getAttribute('data-config');
389
- let config = {};
390
- if (configUrl) {
391
- try {
392
- const response = await fetch(configUrl);
393
- config = await response.json();
394
- } catch (error) {
395
- console.error("Error loading config file:", error);
396
- return;
397
- }
398
- } else {
399
- console.error("No config file provided. Please set a data-config attribute on the <script> tag.");
400
- return;
401
- }
402
-
403
- // ─── 2. If config.css_url is provided, inject a <link> to that CSS ─────────────
404
- if (config.css_url) {
405
- const linkEl = document.createElement('link');
406
- linkEl.rel = "stylesheet";
407
- linkEl.href = config.css_url;
408
- document.head.appendChild(linkEl);
409
- }
410
-
411
- // ─── 3. Generate a unique instanceId for this widget ───────────────────────────
412
- const instanceId = Math.random().toString(36).substr(2, 8);
413
-
414
- // ─── 4. Compute the aspect ratio (padding-bottom %) ────────────────────────────
415
- let aspectPercent = "100%";
416
- if (config.aspect) {
417
- if (config.aspect.includes(":")) {
418
- const parts = config.aspect.split(":");
419
- const w = parseFloat(parts[0]);
420
- const h = parseFloat(parts[1]);
421
- if (!isNaN(w) && !isNaN(h) && w > 0) {
422
- aspectPercent = (h / w * 100) + "%";
423
- }
424
- } else {
425
- const aspectValue = parseFloat(config.aspect);
426
- if (!isNaN(aspectValue) && aspectValue > 0) {
427
- aspectPercent = (100 / aspectValue) + "%";
428
- }
429
- }
430
- } else {
431
- const parentContainer = scriptTag.parentNode;
432
- const containerWidth = parentContainer.offsetWidth;
433
- const containerHeight = parentContainer.offsetHeight;
434
- if (containerWidth > 0 && containerHeight > 0) {
435
- aspectPercent = (containerHeight / containerWidth * 100) + "%";
436
- }
437
- }
438
-
439
- // ─── 5. Create the widget container (no GIF preview, no close button) ───────────
440
- const widgetContainer = document.createElement('div');
441
- widgetContainer.id = 'ply-widget-container-' + instanceId;
442
- widgetContainer.classList.add('ply-widget-container');
443
- widgetContainer.style.height = "0";
444
- widgetContainer.style.paddingBottom = aspectPercent;
445
- widgetContainer.setAttribute('data-original-aspect', aspectPercent);
446
-
447
- // Conditionally include the β€œtooltips-toggle” button only if config.tooltips_url is defined
448
- const tooltipsButtonHTML = config.tooltips_url
449
- ? <button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">β¦Ώ</button>
450
- : '';
451
-
452
- // Add the 3D-viewer HTML + tooltip + help HTML
453
- widgetContainer.innerHTML =
454
- <div id="viewer-container-${instanceId}" class="viewer-container">
455
- <div id="progress-dialog-${instanceId}" class="progress-dialog">
456
- <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
457
- </div>
458
- <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
459
- <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
460
- <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn">
461
- <span class="reset-icon">⟲</span>
462
- </button>
463
- ${tooltipsButtonHTML}
464
- <div id="menu-content-${instanceId}" class="menu-content">
465
- <span id="help-close-${instanceId}" class="help-close">Γ—</span>
466
- <div class="help-text"></div>
467
- </div>
468
- </div>
469
- <div id="tooltip-panel" class="tooltip-panel" style="display: none;">
470
- <div class="tooltip-content">
471
- <span id="tooltip-close" class="tooltip-close">Γ—</span>
472
- <div id="tooltip-text" class="tooltip-text"></div>
473
- <img id="tooltip-image" class="tooltip-image" src="" alt="" style="display: none;" />
474
- </div>
475
- </div>
476
- ;
477
-
478
- // Append the widget container immediately after the <script> tag
479
- scriptTag.parentNode.appendChild(widgetContainer);
480
-
481
- // ─── 6. Grab references to new DOM elements ──────────────────────────────────
482
- const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
483
- const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
484
- const helpToggle = document.getElementById('help-toggle-' + instanceId);
485
- const helpCloseBtn = document.getElementById('help-close-' + instanceId);
486
- const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
487
- const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
488
- const menuContent = document.getElementById('menu-content-' + instanceId);
489
- const helpTextDiv = menuContent.querySelector('.help-text');
490
-
491
- // Tooltip panel elements
492
- const tooltipPanel = document.getElementById('tooltip-panel');
493
- const tooltipTextDiv = document.getElementById('tooltip-text');
494
- const tooltipImage = document.getElementById('tooltip-image');
495
- const tooltipCloseBtn = document.getElementById('tooltip-close');
496
-
497
- // ─── 6a. Detect mobile vs. desktop ────────────────────────────────────────────
498
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
499
- const isMobile = isIOS || /Android/i.test(navigator.userAgent);
500
-
501
- // Conditionally include the French tooltip instruction line if tooltips_url exists
502
- const tooltipInstruction = config.tooltips_url
503
- ? '- Cliquez sur β¦Ώ pour afficher/masquer les tooltips.<br>'
504
- : '';
505
-
506
- // Fill help text with instructions plus the two new French lines
507
- if (isMobile) {
508
- helpTextDiv.innerHTML =
509
- - Pour vous dΓ©placer, glissez deux doigts sur l'Γ©cran.<br>
510
- - Pour orbiter, utilisez un doigt.<br>
511
- - Pour zoomer, pincez avec deux doigts.<br>
512
- ${tooltipInstruction}
513
- - ⟲ Réinitialise la caméra.<br>
514
- - ⇱ Passe en plein Γ©cran.<br>
515
- ;
516
- } else {
517
- helpTextDiv.innerHTML =
518
- - orbitez avec le clic droit<br>
519
- - zoomez avec la molette<br>
520
- - dΓ©placez vous avec le clic gauche<br>
521
- ${tooltipInstruction}
522
- - ⟲ Réinitialise la caméra.<br>
523
- - ⇱ Passe en plein Γ©cran.<br>
524
- ;
525
- }
526
-
527
- // Ensure instructions panel is visible by default
528
- menuContent.style.display = 'block';
529
- viewerContainerElem.style.display = 'block';
530
-
531
- // Variable to hold the drag-hide listener reference
532
- let dragHide = null;
533
-
534
- // Utilities to hide panels
535
- function hideTooltipPanel() {
536
- if (dragHide) {
537
- viewerContainerElem.removeEventListener('pointermove', dragHide);
538
- dragHide = null;
539
- }
540
- tooltipPanel.style.display = 'none';
541
- }
542
- function hideHelpPanel() {
543
- menuContent.style.display = 'none';
544
- }
545
-
546
- // ─── 7. Dynamically load viewer.js ─────────────────────────────────────────
547
- let viewerModule;
548
- try {
549
- viewerModule = await import('https://mikafil-viewer-gs.static.hf.space/viewer.js');
550
- await viewerModule.initializeViewer(config, instanceId);
551
- } catch (err) {
552
- console.error("Failed to load viewer.js or initialize the 3D viewer:", err);
553
- return;
554
- }
555
-
556
- const canvasId = 'canvas-' + instanceId;
557
- const canvasEl = document.getElementById(canvasId);
558
-
559
- // ─── 8. Conditional display of tooltips-toggle button ─────────────────────────
560
- if (tooltipsToggleBtn) {
561
- if (!config.tooltips_url) {
562
- tooltipsToggleBtn.style.display = 'none';
563
- } else {
564
- fetch(config.tooltips_url)
565
- .then(resp => { if (!resp.ok) tooltipsToggleBtn.style.display = 'none'; })
566
- .catch(() => { tooltipsToggleBtn.style.display = 'none'; });
567
- }
568
- }
569
-
570
- // ─── 9. Fullscreen / state-preservation logic ───────────────────────────────
571
- let isFullscreen = false;
572
- let savedState = null;
573
-
574
- function saveCurrentState() {
575
- if (isFullscreen) return;
576
- const computedWidget = window.getComputedStyle(widgetContainer);
577
- const computedViewer = window.getComputedStyle(viewerContainerElem);
578
- const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
579
- savedState = {
580
- widget: {
581
- position: widgetContainer.style.position,
582
- top: widgetContainer.style.top,
583
- left: widgetContainer.style.left,
584
- width: widgetContainer.style.width,
585
- height: widgetContainer.style.height,
586
- maxWidth: widgetContainer.style.maxWidth,
587
- maxHeight:widgetContainer.style.maxHeight,
588
- paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
589
- margin: widgetContainer.style.margin,
590
- },
591
- viewer: {
592
- borderRadius: viewerContainerElem.style.borderRadius,
593
- border: viewerContainerElem.style.border,
594
- }
595
- };
596
- }
597
-
598
- function restoreOriginalStyles() {
599
- if (!savedState) return;
600
- const aspectToUse = savedState.widget.paddingBottom;
601
- widgetContainer.style.position = savedState.widget.position || "";
602
- widgetContainer.style.top = savedState.widget.top || "";
603
- widgetContainer.style.left = savedState.widget.left || "";
604
- widgetContainer.style.width = "100%";
605
- widgetContainer.style.height = "0";
606
- widgetContainer.style.maxWidth = savedState.widget.maxWidth || "";
607
- widgetContainer.style.maxHeight = savedState.widget.maxHeight || "";
608
- widgetContainer.style.paddingBottom= aspectToUse;
609
- widgetContainer.style.margin = savedState.widget.margin || "";
610
- widgetContainer.classList.remove('fake-fullscreen');
611
-
612
- viewerContainerElem.style.position = "absolute";
613
- viewerContainerElem.style.top = "0";
614
- viewerContainerElem.style.left = "0";
615
- viewerContainerElem.style.right = "0";
616
- viewerContainerElem.style.bottom = "0";
617
- viewerContainerElem.style.width = "100%";
618
- viewerContainerElem.style.height = "100%";
619
- viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || "";
620
- viewerContainerElem.style.border = savedState.viewer.border || "";
621
-
622
- if (viewerModule.app) {
623
- viewerModule.app.resizeCanvas(
624
- viewerContainerElem.clientWidth,
625
- viewerContainerElem.clientHeight
626
- );
627
- }
628
-
629
- savedState = null;
630
- }
631
-
632
- function applyFullscreenStyles() {
633
- widgetContainer.style.position = 'fixed';
634
- widgetContainer.style.top = '0';
635
- widgetContainer.style.left = '0';
636
- widgetContainer.style.width = '100vw';
637
- widgetContainer.style.height = '100vh';
638
- widgetContainer.style.maxWidth = '100vw';
639
- widgetContainer.style.maxHeight = '100vh';
640
- widgetContainer.style.paddingBottom = '0';
641
- widgetContainer.style.margin = '0';
642
- widgetContainer.style.border = 'none';
643
- widgetContainer.style.borderRadius = '0';
644
-
645
- viewerContainerElem.style.width = '100%';
646
- viewerContainerElem.style.height = '100%';
647
- viewerContainerElem.style.borderRadius= '0';
648
- viewerContainerElem.style.border = 'none';
649
-
650
- if (viewerModule.app) {
651
- viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
652
- }
653
-
654
- fullscreenToggle.textContent = '⇲';
655
- isFullscreen = true;
656
- }
657
-
658
- function enterFullscreen() {
659
- if (!savedState) saveCurrentState();
660
- if (isIOS) {
661
- applyFullscreenStyles();
662
- widgetContainer.classList.add('fake-fullscreen');
663
- } else if (widgetContainer.requestFullscreen) {
664
- widgetContainer.requestFullscreen()
665
- .then(applyFullscreenStyles)
666
- .catch(() => {
667
- applyFullscreenStyles();
668
- widgetContainer.classList.add('fake-fullscreen');
669
- });
670
- } else {
671
- applyFullscreenStyles();
672
- widgetContainer.classList.add('fake-fullscreen');
673
- }
674
- }
675
-
676
- function exitFullscreen() {
677
- if (document.fullscreenElement === widgetContainer && document.exitFullscreen) {
678
- document.exitFullscreen().catch(() => {});
679
- }
680
- widgetContainer.classList.remove('fake-fullscreen');
681
- restoreOriginalStyles();
682
- isFullscreen = false;
683
- }
684
-
685
- // ─── 10. Hook up event listeners ───────────────────────────────────────────
686
- fullscreenToggle.addEventListener('click', () => {
687
- hideTooltipPanel();
688
- isFullscreen ? exitFullscreen() : enterFullscreen();
689
- });
690
- document.addEventListener('fullscreenchange', () => {
691
- if (!document.fullscreenElement && isFullscreen) {
692
- isFullscreen = false;
693
- restoreOriginalStyles();
694
- }
695
- });
696
-
697
- helpToggle.addEventListener('click', (e) => {
698
- hideTooltipPanel();
699
- e.stopPropagation();
700
- menuContent.style.display = menuContent.style.display === 'block' ? 'none' : 'block';
701
- });
702
- helpCloseBtn.addEventListener('click', hideHelpPanel);
703
-
704
- resetCameraBtn.addEventListener('click', () => {
705
- hideTooltipPanel();
706
- if (viewerModule.resetViewerCamera) {
707
- viewerModule.resetViewerCamera();
708
- }
709
- });
710
-
711
- if (tooltipsToggleBtn) {
712
- let tooltipsVisible = !!config.showTooltipsDefault;
713
- tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
714
- tooltipsToggleBtn.addEventListener('click', () => {
715
- hideTooltipPanel();
716
- tooltipsVisible = !tooltipsVisible;
717
- tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
718
- document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } }));
719
- });
720
- }
721
-
722
- tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
723
-
724
- document.addEventListener('tooltip-selected', (evt) => {
725
- const { title, description, imgUrl } = evt.detail;
726
- tooltipTextDiv.innerHTML = <strong>${title}</strong><br>${description};
727
- if (imgUrl) {
728
- tooltipImage.src = imgUrl;
729
- tooltipImage.style.display = 'block';
730
- } else {
731
- tooltipImage.style.display = 'none';
732
- }
733
- tooltipPanel.style.display = 'flex';
734
- dragHide = (e) => {
735
- if ((e.pointerType === 'mouse' && e.buttons !== 0) || e.pointerType === 'touch') {
736
- hideTooltipPanel();
737
- }
738
- };
739
- viewerContainerElem.addEventListener('pointermove', dragHide);
740
- });
741
-
742
- if (canvasEl) {
743
- canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
744
- }
745
- document.addEventListener('keydown', (e) => {
746
- if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
747
- });
748
- window.addEventListener('resize', () => {
749
- if (viewerModule.app) {
750
- if (isFullscreen) {
751
- viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
752
- } else {
753
- viewerModule.app.resizeCanvas(
754
- viewerContainerElem.clientWidth,
755
- viewerContainerElem.clientHeight
756
- );
757
- }
758
- }
759
- });
760
-
761
- // Save β€œinitial state” after a brief delay
762
- setTimeout(() => {
763
- saveCurrentState();
764
- document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } }));
765
- }, 200);
766
-
767
- })();
 
153
  orbit: new pc.Asset('script', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" }),
154
  galerie: new pc.Asset('galerie', 'container', { url: glbUrl }),
155
  hdr: new pc.Asset('hdr', 'texture', {
156
+ url: `https://huggingface.co/datasets/bilca/ply_files/resolve/main/galeries/blanc.png`
157
  }, { type: pc.TEXTURETYPE_RGBP, mipmaps: false })
158
  };
159
 
 
162
  assets.model.on('load', () => progressDialog.style.display = 'none');
163
  assets.model.on('error', err => {
164
  console.error("Error loading PLY file:", err);
165
+ progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
166
  });
167
 
168
  const progCheck = setInterval(() => {
 
291
 
292
  } catch (error) {
293
  console.error("Error initializing PlayCanvas viewer:", error);
294
+ progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
295
  }
296
  }
297
 
 
357
  resizeObserver.disconnect();
358
  resizeObserver = null;
359
  }
360
+ }