MikaFil commited on
Commit
f339265
·
verified ·
1 Parent(s): 50241ae

Update viewer_ar.js

Browse files
Files changed (1) hide show
  1. viewer_ar.js +49 -48
viewer_ar.js CHANGED
@@ -61,13 +61,8 @@
61
  throw new Error("UMD failed");
62
  };
63
 
64
- if (esmFirst) {
65
- try { return await tryESM(); } catch (_) {}
66
- return await tryUMD();
67
- } else {
68
- try { return await tryUMD(); } catch (_) {}
69
- return await tryESM();
70
- }
71
  }
72
 
73
  // =========================
@@ -92,11 +87,12 @@
92
  font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
93
  pointer-events: auto; width: 56px; display: flex; flex-direction: column; align-items: center; gap: 8px;
94
  box-shadow: 0 6px 18px rgba(0,0,0,.25); backdrop-filter: blur(4px);
 
95
  }
96
  .ar-ui .label { font-size: 12px; text-align: center; opacity: 0.9; }
97
  .ar-ui input[type="range"].rotY {
98
  -webkit-appearance: none; width: 220px; height: 28px; transform: rotate(-90deg);
99
- outline: none; background: transparent; touch-action: none;
100
  }
101
  .ar-ui input[type="range"].rotY::-webkit-slider-thumb {
102
  -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%;
@@ -122,6 +118,7 @@
122
  }
123
  const overlayRoot = ensureOverlayRoot();
124
 
 
125
  function message(msg) {
126
  let el = overlayRoot.querySelector(".pc-ar-msg");
127
  if (!el) {
@@ -253,58 +250,57 @@
253
 
254
  // --- Chargement GLB ---
255
  app.assets.loadFromUrl(GLB_URL, "container", (err, asset) => {
256
- if (err) {
257
- console.error(err);
258
- message("Échec du chargement du modèle GLB.");
259
- return;
260
- }
261
- const instance = asset.resource.instantiateRenderEntity({
262
- castShadows: false,
263
- receiveShadows: false
264
- });
265
  modelRoot.addChild(instance);
266
  modelRoot.setLocalScale(0.2, 0.2, 0.2);
267
-
268
  modelLoaded = true;
269
  message("Modèle chargé. Touchez l’écran pour démarrer l’AR.");
270
  });
271
 
272
  // --- Vérifs WebXR ---
273
- if (!app.xr.supported) {
274
- message("WebXR n’est pas supporté sur cet appareil.");
275
- return;
276
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
  // --- Démarrage AR (CameraComponent.startXr + DOM Overlay) ---
279
  const activateAR = () => {
280
- if (!app.xr.isAvailable(pc.XRTYPE_AR)) {
281
- message("AR immersive indisponible sur cet appareil.");
282
- return;
283
- }
284
 
285
  // DOM overlay root (notre conteneur avec slider + messages)
286
  app.xr.domOverlay.root = document.getElementById("xr-overlay-root");
287
 
288
- // Démarre via la Camera Component (plus compatible selon versions moteur)
289
  camera.camera.startXr(pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
290
  requiredFeatures: ["hit-test", "dom-overlay"],
291
  domOverlay: { root: app.xr.domOverlay.root },
292
  callback: (err) => {
293
- if (err) {
294
- console.error("Échec du démarrage AR :", err);
295
- message(`Échec du démarrage AR : ${err.message || err}`);
296
- }
297
  }
298
  });
299
  };
300
 
301
- // Premier tap / clic → démarrer AR
302
- app.mouse.on("mousedown", () => { if (!app.xr.active) activateAR(); });
303
  if (app.touch) {
304
  app.touch.on("touchend", (evt) => {
305
- if (!app.xr.active) activateAR();
306
- evt.event.preventDefault();
307
- evt.event.stopPropagation();
308
  });
309
  }
310
 
@@ -325,12 +321,9 @@
325
  if (modelLoaded && !placedOnce) {
326
  modelRoot.enabled = true;
327
  modelRoot.setPosition(pos);
328
- const euler = new pc.Vec3();
329
- rot.getEulerAngles(euler);
330
- rotationYDeg = norm360(euler.y);
331
- applyRotationY(rotationYDeg);
332
- placedOnce = true;
333
- rotYInput.disabled = false;
334
  message("Objet placé. Glissez pour déplacer, tournez-le avec le slider →");
335
  }
336
  });
@@ -344,32 +337,40 @@
344
  let lastMouseX = 0;
345
  const ROTATE_SENSITIVITY = 0.25;
346
 
347
- // Déplacement (input XR : maintien/drag)
348
  app.xr.input.on("add", (inputSource) => {
349
  inputSource.on("selectstart", () => {
 
350
  if (!placedOnce || !modelLoaded) return;
 
351
  inputSource.hitTestStart({
352
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
353
  callback: (err, transientSource) => {
354
  if (err) return;
355
  isDragging = true;
356
- transientSource.on("result", (pos) => { if (isDragging) modelRoot.setPosition(pos); });
 
 
 
 
 
357
  transientSource.once("remove", () => { isDragging = false; });
358
  }
359
  });
360
  });
 
361
  inputSource.on("selectend", () => { isDragging = false; });
362
  });
363
 
364
- // Souris : déplacement (gauche) & rotation (droit ou Shift+gauche)
365
  app.mouse.on("mousedown", (e) => {
366
- if (!app.xr.active || !placedOnce) return;
367
  if (e.button === 0 && !e.shiftKey) isDragging = true;
368
  else if (e.button === 2 || (e.button === 0 && e.shiftKey)) { rotateMode = true; lastMouseX = e.x; }
369
  });
370
 
371
  app.mouse.on("mousemove", (e) => {
372
- if (!app.xr.active || !placedOnce) return;
373
  if (isDragging) { if (reticle.enabled) modelRoot.setPosition(reticle.getPosition()); }
374
  else if (rotateMode && modelRoot.enabled) {
375
  const dx = e.x - lastMouseX; lastMouseX = e.x;
@@ -381,7 +382,7 @@
381
  window.addEventListener("contextmenu", (e) => e.preventDefault());
382
 
383
  // --- Slider rotation (DOM overlay) ---
384
- rotYInput.disabled = true;
385
  rotYInput.addEventListener("input", (e) => {
386
  if (!modelRoot.enabled) return;
387
  const deg = parseFloat(e.target.value || "0");
 
61
  throw new Error("UMD failed");
62
  };
63
 
64
+ if (esmFirst) { try { return await tryESM(); } catch (_) {} return await tryUMD(); }
65
+ else { try { return await tryUMD(); } catch (_) {} return await tryESM(); }
 
 
 
 
 
66
  }
67
 
68
  // =========================
 
87
  font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
88
  pointer-events: auto; width: 56px; display: flex; flex-direction: column; align-items: center; gap: 8px;
89
  box-shadow: 0 6px 18px rgba(0,0,0,.25); backdrop-filter: blur(4px);
90
+ touch-action: none; /* important pour éviter le scroll/zoom natif */
91
  }
92
  .ar-ui .label { font-size: 12px; text-align: center; opacity: 0.9; }
93
  .ar-ui input[type="range"].rotY {
94
  -webkit-appearance: none; width: 220px; height: 28px; transform: rotate(-90deg);
95
+ outline: none; background: transparent; touch-action: none; /* bloque gestures tactiles */
96
  }
97
  .ar-ui input[type="range"].rotY::-webkit-slider-thumb {
98
  -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%;
 
118
  }
119
  const overlayRoot = ensureOverlayRoot();
120
 
121
+ // Messages dans l'overlay
122
  function message(msg) {
123
  let el = overlayRoot.querySelector(".pc-ar-msg");
124
  if (!el) {
 
250
 
251
  // --- Chargement GLB ---
252
  app.assets.loadFromUrl(GLB_URL, "container", (err, asset) => {
253
+ if (err) { console.error(err); message("Échec du chargement du modèle GLB."); return; }
254
+ const instance = asset.resource.instantiateRenderEntity({ castShadows: false, receiveShadows: false });
 
 
 
 
 
 
 
255
  modelRoot.addChild(instance);
256
  modelRoot.setLocalScale(0.2, 0.2, 0.2);
 
257
  modelLoaded = true;
258
  message("Modèle chargé. Touchez l’écran pour démarrer l’AR.");
259
  });
260
 
261
  // --- Vérifs WebXR ---
262
+ if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
263
+
264
+ // ====== NOUVEAU : gestion anti-fuite d'événements UI ======
265
+ let uiInteracting = false;
266
+ const startUIHold = (e) => { uiInteracting = true; e.stopPropagation(); e.preventDefault(); };
267
+ const endUIHold = (e) => { uiInteracting = false; e.stopPropagation(); e.preventDefault(); };
268
+ const swallow = (e) => { e.stopPropagation(); e.preventDefault(); };
269
+
270
+ // Piégeage complet sur le panneau ET le slider
271
+ ["pointerdown","pointermove","pointerup","pointercancel",
272
+ "touchstart","touchmove","touchend","touchcancel",
273
+ "mousedown","mousemove","mouseup","wheel","click"].forEach(evt => {
274
+ uiPanel.addEventListener(evt, evt === "pointerdown" || evt === "touchstart" || evt === "mousedown" ? startUIHold :
275
+ (evt === "pointerup" || evt === "pointercancel" || evt === "touchend" || evt === "touchcancel" || evt === "mouseup" ? endUIHold : swallow),
276
+ { passive: false });
277
+ rotYInput.addEventListener(evt, evt === "pointerdown" || evt === "touchstart" || evt === "mousedown" ? startUIHold :
278
+ (evt === "pointerup" || evt === "pointercancel" || evt === "touchend" || evt === "touchcancel" || evt === "mouseup" ? endUIHold : swallow),
279
+ { passive: false });
280
+ });
281
 
282
  // --- Démarrage AR (CameraComponent.startXr + DOM Overlay) ---
283
  const activateAR = () => {
284
+ if (!app.xr.isAvailable(pc.XRTYPE_AR)) { message("AR immersive indisponible sur cet appareil."); return; }
 
 
 
285
 
286
  // DOM overlay root (notre conteneur avec slider + messages)
287
  app.xr.domOverlay.root = document.getElementById("xr-overlay-root");
288
 
 
289
  camera.camera.startXr(pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
290
  requiredFeatures: ["hit-test", "dom-overlay"],
291
  domOverlay: { root: app.xr.domOverlay.root },
292
  callback: (err) => {
293
+ if (err) { console.error("Échec du démarrage AR :", err); message(`Échec du démarrage AR : ${err.message || err}`); }
 
 
 
294
  }
295
  });
296
  };
297
 
298
+ // Premier tap / clic → démarrer AR (ignorer si on tape sur l'UI)
299
+ app.mouse.on("mousedown", (e) => { if (!app.xr.active && !uiInteracting) activateAR(); });
300
  if (app.touch) {
301
  app.touch.on("touchend", (evt) => {
302
+ if (!app.xr.active && !uiInteracting) activateAR();
303
+ evt.event.preventDefault(); evt.event.stopPropagation();
 
304
  });
305
  }
306
 
 
321
  if (modelLoaded && !placedOnce) {
322
  modelRoot.enabled = true;
323
  modelRoot.setPosition(pos);
324
+ const euler = new pc.Vec3(); rot.getEulerAngles(euler);
325
+ rotationYDeg = norm360(euler.y); applyRotationY(rotationYDeg);
326
+ placedOnce = true; rotYInput.disabled = false;
 
 
 
327
  message("Objet placé. Glissez pour déplacer, tournez-le avec le slider →");
328
  }
329
  });
 
337
  let lastMouseX = 0;
338
  const ROTATE_SENSITIVITY = 0.25;
339
 
340
+ // Déplacement (input XR : maintien/drag) — IGNORE si UI en cours
341
  app.xr.input.on("add", (inputSource) => {
342
  inputSource.on("selectstart", () => {
343
+ if (uiInteracting) return; // <<< bloque le drag si doigt sur le slider
344
  if (!placedOnce || !modelLoaded) return;
345
+
346
  inputSource.hitTestStart({
347
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
348
  callback: (err, transientSource) => {
349
  if (err) return;
350
  isDragging = true;
351
+
352
+ transientSource.on("result", (pos) => {
353
+ if (!isDragging) return;
354
+ modelRoot.setPosition(pos);
355
+ });
356
+
357
  transientSource.once("remove", () => { isDragging = false; });
358
  }
359
  });
360
  });
361
+
362
  inputSource.on("selectend", () => { isDragging = false; });
363
  });
364
 
365
+ // Souris : déplacement (gauche) & rotation (droit ou Shift+gauche) — IGNORE si UI en cours
366
  app.mouse.on("mousedown", (e) => {
367
+ if (!app.xr.active || !placedOnce || uiInteracting) return;
368
  if (e.button === 0 && !e.shiftKey) isDragging = true;
369
  else if (e.button === 2 || (e.button === 0 && e.shiftKey)) { rotateMode = true; lastMouseX = e.x; }
370
  });
371
 
372
  app.mouse.on("mousemove", (e) => {
373
+ if (!app.xr.active || !placedOnce || uiInteracting) return;
374
  if (isDragging) { if (reticle.enabled) modelRoot.setPosition(reticle.getPosition()); }
375
  else if (rotateMode && modelRoot.enabled) {
376
  const dx = e.x - lastMouseX; lastMouseX = e.x;
 
382
  window.addEventListener("contextmenu", (e) => e.preventDefault());
383
 
384
  // --- Slider rotation (DOM overlay) ---
385
+ rotYInput.disabled = true; // activé au 1er placement
386
  rotYInput.addEventListener("input", (e) => {
387
  if (!modelRoot.enabled) return;
388
  const deg = parseFloat(e.target.value || "0");