Update viewer_ar_ios.js
Browse files- viewer_ar_ios.js +39 -80
viewer_ar_ios.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
| 4 |
- Android/Desktop : WebXR AR (horizontaux uniquement) + slider yaw + blob shadow
|
| 5 |
- Éclairage PBR par défaut (sans WebXR light estimation)
|
| 6 |
- Monté dans un conteneur choisi (data-mount="#ar-mount") pour Squarespace
|
| 7 |
-
- Bouton "Lancer l'AR" (Android & iOS)
|
| 8 |
*/
|
| 9 |
|
| 10 |
(function () {
|
| 11 |
// ============ Utils: script tag / config / platform ============
|
| 12 |
function getCurrentScript() {
|
|
|
|
| 13 |
return document.currentScript || (function () {
|
| 14 |
var scripts = document.getElementsByTagName('script');
|
| 15 |
return scripts[scripts.length - 1] || null;
|
|
@@ -118,12 +118,9 @@
|
|
| 118 |
".ar-ui input[type=\"range\"].rotY{position:absolute;opacity:0;pointer-events:none;width:0;height:0}",
|
| 119 |
".ar-ui .val{font-size:12px;opacity:.95}",
|
| 120 |
|
| 121 |
-
/*
|
| 122 |
-
"#
|
| 123 |
-
"#
|
| 124 |
-
|
| 125 |
-
/* Anchor QuickLook caché (déclenché par le bouton sur iOS) */
|
| 126 |
-
"#ios-quicklook-anchor{position:absolute;left:-9999px;top:-9999px;height:1px;width:1px;overflow:hidden}"
|
| 127 |
].join("\n");
|
| 128 |
var styleTag = document.createElement("style");
|
| 129 |
styleTag.textContent = css;
|
|
@@ -164,20 +161,43 @@
|
|
| 164 |
}
|
| 165 |
}
|
| 166 |
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
function ensureCanvas() {
|
| 169 |
var scriptEl = getCurrentScript();
|
| 170 |
var mountSel = scriptEl && scriptEl.getAttribute('data-mount');
|
| 171 |
var desiredHeight = (scriptEl && scriptEl.getAttribute('data-height')) || '70vh';
|
| 172 |
|
|
|
|
| 173 |
var mountEl = null;
|
| 174 |
if (mountSel) mountEl = document.querySelector(mountSel);
|
| 175 |
if (!mountEl) {
|
|
|
|
| 176 |
mountEl = document.createElement('div');
|
| 177 |
mountEl.id = 'ar-mount-fallback';
|
| 178 |
document.body.insertBefore(mountEl, document.body.firstChild);
|
| 179 |
}
|
| 180 |
|
|
|
|
| 181 |
var mountStyle = mountEl.style;
|
| 182 |
if (!mountStyle.position) mountStyle.position = 'relative';
|
| 183 |
mountStyle.width = mountStyle.width || '100%';
|
|
@@ -185,6 +205,7 @@
|
|
| 185 |
mountStyle.touchAction = mountStyle.touchAction || 'manipulation';
|
| 186 |
mountStyle.webkitTapHighlightColor = 'transparent';
|
| 187 |
|
|
|
|
| 188 |
var canvas = mountEl.querySelector('#application-canvas');
|
| 189 |
if (!canvas) {
|
| 190 |
canvas = document.createElement('canvas');
|
|
@@ -192,6 +213,7 @@
|
|
| 192 |
mountEl.appendChild(canvas);
|
| 193 |
}
|
| 194 |
|
|
|
|
| 195 |
var cs = canvas.style;
|
| 196 |
cs.position = 'absolute';
|
| 197 |
cs.left = '0';
|
|
@@ -200,6 +222,7 @@
|
|
| 200 |
cs.height = '100%';
|
| 201 |
cs.display = 'block';
|
| 202 |
|
|
|
|
| 203 |
try {
|
| 204 |
mountEl.scrollIntoView({ behavior: 'instant', block: 'start' });
|
| 205 |
} catch (_) {}
|
|
@@ -224,36 +247,6 @@
|
|
| 224 |
return p;
|
| 225 |
}
|
| 226 |
|
| 227 |
-
// Bouton "Lancer l'AR" + (iOS) ancre rel=ar cachée
|
| 228 |
-
function ensureLaunchControls(USDZ_URL) {
|
| 229 |
-
// bouton visible (toujours présent)
|
| 230 |
-
var btn = document.getElementById('ar-launch-btn');
|
| 231 |
-
if (!btn) {
|
| 232 |
-
btn = document.createElement('button');
|
| 233 |
-
btn.id = 'ar-launch-btn';
|
| 234 |
-
btn.type = 'button';
|
| 235 |
-
btn.textContent = 'Lancer l’AR';
|
| 236 |
-
// pointer-events auto par défaut ; stopper la propagation pour éviter le drag de la scène
|
| 237 |
-
btn.addEventListener('pointerdown', function (e) { e.stopPropagation(); }, { passive: true });
|
| 238 |
-
btn.addEventListener('click', function (e) { e.stopPropagation(); }, false);
|
| 239 |
-
document.body.appendChild(btn);
|
| 240 |
-
}
|
| 241 |
-
|
| 242 |
-
var anchor = document.getElementById('ios-quicklook-anchor');
|
| 243 |
-
if (isIOS()) {
|
| 244 |
-
if (!anchor) {
|
| 245 |
-
anchor = document.createElement('a');
|
| 246 |
-
anchor.id = 'ios-quicklook-anchor';
|
| 247 |
-
anchor.setAttribute('rel', 'ar');
|
| 248 |
-
document.body.appendChild(anchor);
|
| 249 |
-
}
|
| 250 |
-
if (USDZ_URL) {
|
| 251 |
-
anchor.setAttribute('href', buildQuickLookHref(USDZ_URL));
|
| 252 |
-
}
|
| 253 |
-
}
|
| 254 |
-
return { btn: btn, anchor: anchor || null };
|
| 255 |
-
}
|
| 256 |
-
|
| 257 |
// ============ Boot : charge config, route iOS vs Android/Desktop ============
|
| 258 |
(async function () {
|
| 259 |
var cfgUrl = findConfigUrl();
|
|
@@ -265,56 +258,28 @@
|
|
| 265 |
cfg.usdz_url :
|
| 266 |
null;
|
| 267 |
|
| 268 |
-
// Toujours créer le bouton (même iOS)
|
| 269 |
-
var { btn: launchBtn, anchor: iosAnchor } = ensureLaunchControls(USDZ_URL);
|
| 270 |
-
|
| 271 |
if (isIOS()) {
|
| 272 |
if (USDZ_URL) {
|
| 273 |
-
|
| 274 |
-
message("iOS détecté :
|
| 275 |
-
launchBtn.onclick = function (e) {
|
| 276 |
-
e.preventDefault();
|
| 277 |
-
e.stopPropagation();
|
| 278 |
-
// délencher l’ancre rel=ar (meilleure compatibilité iOS)
|
| 279 |
-
if (iosAnchor && iosAnchor.href) {
|
| 280 |
-
iosAnchor.click();
|
| 281 |
-
}
|
| 282 |
-
};
|
| 283 |
} else {
|
| 284 |
message("iOS détecté, mais aucun 'usdz_url' dans config.json.");
|
| 285 |
-
launchBtn.disabled = true;
|
| 286 |
-
launchBtn.style.opacity = '0.5';
|
| 287 |
}
|
| 288 |
-
// (Optionnel) tu peux ajouter un viewer 3D non-AR ici si besoin.
|
| 289 |
return;
|
| 290 |
}
|
| 291 |
|
| 292 |
-
// Android / Desktop : charger PlayCanvas et initialiser l’app
|
| 293 |
try {
|
| 294 |
await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
|
| 295 |
} catch (e) {
|
| 296 |
console.error("Chargement PlayCanvas échoué ->", e);
|
| 297 |
message("Impossible de charger PlayCanvas (réseau/CDN). Réessaie plus tard.");
|
| 298 |
-
launchBtn.disabled = true;
|
| 299 |
-
launchBtn.style.opacity = '0.5';
|
| 300 |
return;
|
| 301 |
}
|
| 302 |
-
|
| 303 |
-
// init de l’app WebXR, puis bouton déclencheur
|
| 304 |
-
var activateARRef = { fn: null }; // on branche le bouton une fois activateAR défini
|
| 305 |
-
initARApp(GLB_URL, activateARRef);
|
| 306 |
-
|
| 307 |
-
launchBtn.onclick = function (e) {
|
| 308 |
-
e.preventDefault();
|
| 309 |
-
e.stopPropagation();
|
| 310 |
-
if (typeof activateARRef.fn === 'function') {
|
| 311 |
-
activateARRef.fn();
|
| 312 |
-
}
|
| 313 |
-
};
|
| 314 |
})();
|
| 315 |
|
| 316 |
// ============ App Android/Desktop (WebXR) ============
|
| 317 |
-
function initARApp(GLB_URL
|
| 318 |
var pc = window.pc;
|
| 319 |
var canvas = ensureCanvas();
|
| 320 |
var ui = ensureSliderUI();
|
|
@@ -430,7 +395,7 @@
|
|
| 430 |
return e;
|
| 431 |
}
|
| 432 |
|
| 433 |
-
// Euler de base
|
| 434 |
var baseEulerX = 0, baseEulerZ = 0;
|
| 435 |
|
| 436 |
// Rotation via slider (0..360, 360 en haut / 0 en bas)
|
|
@@ -483,7 +448,7 @@
|
|
| 483 |
baseEulerX = initE.x; baseEulerZ = initE.z;
|
| 484 |
|
| 485 |
modelLoaded = true;
|
| 486 |
-
message("Modèle chargé.
|
| 487 |
});
|
| 488 |
|
| 489 |
if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
|
|
@@ -537,7 +502,7 @@
|
|
| 537 |
document.addEventListener("pointerup", endDrag, { capture: true, passive: false });
|
| 538 |
document.addEventListener("pointercancel", endDrag, { capture: true, passive: false });
|
| 539 |
|
| 540 |
-
// --- Démarrage AR (Android/Desktop)
|
| 541 |
function activateAR() {
|
| 542 |
if (!app.xr.isAvailable(pc.XRTYPE_AR)) { message("AR immersive indisponible sur cet appareil."); return; }
|
| 543 |
if (!app.xr.domOverlay) app.xr.domOverlay = {};
|
|
@@ -553,12 +518,6 @@
|
|
| 553 |
}
|
| 554 |
});
|
| 555 |
}
|
| 556 |
-
// on branche la référence pour le bouton "Lancer l'AR"
|
| 557 |
-
if (activateARRef && typeof activateARRef === 'object') {
|
| 558 |
-
activateARRef.fn = activateAR;
|
| 559 |
-
}
|
| 560 |
-
|
| 561 |
-
// On garde la possibilité de démarrer par tap écran si tu veux (désactivable)
|
| 562 |
app.mouse.on("mousedown", function () { if (!app.xr.active && !uiInteracting) activateAR(); });
|
| 563 |
if (app.touch) {
|
| 564 |
app.touch.on("touchend", function (evt) {
|
|
@@ -681,7 +640,7 @@
|
|
| 681 |
app.xr.on("available:" + pc.XRTYPE_AR, function (a) {
|
| 682 |
if (!a) message("AR immersive indisponible.");
|
| 683 |
else if (!app.xr.hitTest.supported) message("AR Hit Test non supporté.");
|
| 684 |
-
else message(modelLoaded ? "Touchez l’écran
|
| 685 |
});
|
| 686 |
|
| 687 |
if (!app.xr.isAvailable(pc.XRTYPE_AR)) message("AR immersive indisponible.");
|
|
|
|
| 4 |
- Android/Desktop : WebXR AR (horizontaux uniquement) + slider yaw + blob shadow
|
| 5 |
- Éclairage PBR par défaut (sans WebXR light estimation)
|
| 6 |
- Monté dans un conteneur choisi (data-mount="#ar-mount") pour Squarespace
|
|
|
|
| 7 |
*/
|
| 8 |
|
| 9 |
(function () {
|
| 10 |
// ============ Utils: script tag / config / platform ============
|
| 11 |
function getCurrentScript() {
|
| 12 |
+
// robuste même si currentScript n'est pas dispo
|
| 13 |
return document.currentScript || (function () {
|
| 14 |
var scripts = document.getElementsByTagName('script');
|
| 15 |
return scripts[scripts.length - 1] || null;
|
|
|
|
| 118 |
".ar-ui input[type=\"range\"].rotY{position:absolute;opacity:0;pointer-events:none;width:0;height:0}",
|
| 119 |
".ar-ui .val{font-size:12px;opacity:.95}",
|
| 120 |
|
| 121 |
+
/* iOS Quick Look button */
|
| 122 |
+
"#ios-quicklook-btn{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:10003;display:inline-block;pointer-events:auto}",
|
| 123 |
+
"#ios-quicklook-btn img{display:block;height:44px;width:auto}"
|
|
|
|
|
|
|
|
|
|
| 124 |
].join("\n");
|
| 125 |
var styleTag = document.createElement("style");
|
| 126 |
styleTag.textContent = css;
|
|
|
|
| 161 |
}
|
| 162 |
}
|
| 163 |
|
| 164 |
+
function ensureQuickLookButton(USDZ_URL) {
|
| 165 |
+
var btn = document.getElementById("ios-quicklook-btn");
|
| 166 |
+
if (btn) return btn;
|
| 167 |
+
|
| 168 |
+
var anchor = document.createElement("a");
|
| 169 |
+
anchor.id = "ios-quicklook-btn";
|
| 170 |
+
anchor.setAttribute("rel", "ar");
|
| 171 |
+
anchor.setAttribute("href", buildQuickLookHref(USDZ_URL));
|
| 172 |
+
|
| 173 |
+
var img = document.createElement("img");
|
| 174 |
+
img.alt = "Voir en AR";
|
| 175 |
+
img.src =
|
| 176 |
+
"data:image/svg+xml;charset=utf-8," +
|
| 177 |
+
encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="160" height="44"><rect rx="8" ry="8" width="160" height="44" fill="black"/><text x="80" y="28" font-size="14" text-anchor="middle" fill="white" font-family="system-ui, -apple-system, Segoe UI, Roboto">Voir en AR</text></svg>');
|
| 178 |
+
|
| 179 |
+
anchor.appendChild(img);
|
| 180 |
+
document.body.appendChild(anchor);
|
| 181 |
+
return anchor;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
// ============ Canvas monté dans un conteneur contrôlé (Squarespace) ============
|
| 185 |
function ensureCanvas() {
|
| 186 |
var scriptEl = getCurrentScript();
|
| 187 |
var mountSel = scriptEl && scriptEl.getAttribute('data-mount');
|
| 188 |
var desiredHeight = (scriptEl && scriptEl.getAttribute('data-height')) || '70vh';
|
| 189 |
|
| 190 |
+
// Trouve le conteneur cible
|
| 191 |
var mountEl = null;
|
| 192 |
if (mountSel) mountEl = document.querySelector(mountSel);
|
| 193 |
if (!mountEl) {
|
| 194 |
+
// fallback : tout en haut du body (pour éviter "sous le footer")
|
| 195 |
mountEl = document.createElement('div');
|
| 196 |
mountEl.id = 'ar-mount-fallback';
|
| 197 |
document.body.insertBefore(mountEl, document.body.firstChild);
|
| 198 |
}
|
| 199 |
|
| 200 |
+
// Style du conteneur
|
| 201 |
var mountStyle = mountEl.style;
|
| 202 |
if (!mountStyle.position) mountStyle.position = 'relative';
|
| 203 |
mountStyle.width = mountStyle.width || '100%';
|
|
|
|
| 205 |
mountStyle.touchAction = mountStyle.touchAction || 'manipulation';
|
| 206 |
mountStyle.webkitTapHighlightColor = 'transparent';
|
| 207 |
|
| 208 |
+
// Réutilise ou crée le canvas
|
| 209 |
var canvas = mountEl.querySelector('#application-canvas');
|
| 210 |
if (!canvas) {
|
| 211 |
canvas = document.createElement('canvas');
|
|
|
|
| 213 |
mountEl.appendChild(canvas);
|
| 214 |
}
|
| 215 |
|
| 216 |
+
// Le canvas remplit le conteneur
|
| 217 |
var cs = canvas.style;
|
| 218 |
cs.position = 'absolute';
|
| 219 |
cs.left = '0';
|
|
|
|
| 222 |
cs.height = '100%';
|
| 223 |
cs.display = 'block';
|
| 224 |
|
| 225 |
+
// Optionnel : ramener le viewport sur le viewer
|
| 226 |
try {
|
| 227 |
mountEl.scrollIntoView({ behavior: 'instant', block: 'start' });
|
| 228 |
} catch (_) {}
|
|
|
|
| 247 |
return p;
|
| 248 |
}
|
| 249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
// ============ Boot : charge config, route iOS vs Android/Desktop ============
|
| 251 |
(async function () {
|
| 252 |
var cfgUrl = findConfigUrl();
|
|
|
|
| 258 |
cfg.usdz_url :
|
| 259 |
null;
|
| 260 |
|
|
|
|
|
|
|
|
|
|
| 261 |
if (isIOS()) {
|
| 262 |
if (USDZ_URL) {
|
| 263 |
+
ensureQuickLookButton(USDZ_URL);
|
| 264 |
+
message("iOS détecté : utilisez le bouton « Voir en AR » (AR Quick Look).");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
} else {
|
| 266 |
message("iOS détecté, mais aucun 'usdz_url' dans config.json.");
|
|
|
|
|
|
|
| 267 |
}
|
|
|
|
| 268 |
return;
|
| 269 |
}
|
| 270 |
|
|
|
|
| 271 |
try {
|
| 272 |
await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
|
| 273 |
} catch (e) {
|
| 274 |
console.error("Chargement PlayCanvas échoué ->", e);
|
| 275 |
message("Impossible de charger PlayCanvas (réseau/CDN). Réessaie plus tard.");
|
|
|
|
|
|
|
| 276 |
return;
|
| 277 |
}
|
| 278 |
+
initARApp(GLB_URL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
})();
|
| 280 |
|
| 281 |
// ============ App Android/Desktop (WebXR) ============
|
| 282 |
+
function initARApp(GLB_URL) {
|
| 283 |
var pc = window.pc;
|
| 284 |
var canvas = ensureCanvas();
|
| 285 |
var ui = ensureSliderUI();
|
|
|
|
| 395 |
return e;
|
| 396 |
}
|
| 397 |
|
| 398 |
+
// Euler de base (évite inversions)
|
| 399 |
var baseEulerX = 0, baseEulerZ = 0;
|
| 400 |
|
| 401 |
// Rotation via slider (0..360, 360 en haut / 0 en bas)
|
|
|
|
| 448 |
baseEulerX = initE.x; baseEulerZ = initE.z;
|
| 449 |
|
| 450 |
modelLoaded = true;
|
| 451 |
+
message("Modèle chargé. Touchez l’écran pour démarrer l’AR.");
|
| 452 |
});
|
| 453 |
|
| 454 |
if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
|
|
|
|
| 502 |
document.addEventListener("pointerup", endDrag, { capture: true, passive: false });
|
| 503 |
document.addEventListener("pointercancel", endDrag, { capture: true, passive: false });
|
| 504 |
|
| 505 |
+
// --- Démarrage AR (Android/Desktop)
|
| 506 |
function activateAR() {
|
| 507 |
if (!app.xr.isAvailable(pc.XRTYPE_AR)) { message("AR immersive indisponible sur cet appareil."); return; }
|
| 508 |
if (!app.xr.domOverlay) app.xr.domOverlay = {};
|
|
|
|
| 518 |
}
|
| 519 |
});
|
| 520 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
app.mouse.on("mousedown", function () { if (!app.xr.active && !uiInteracting) activateAR(); });
|
| 522 |
if (app.touch) {
|
| 523 |
app.touch.on("touchend", function (evt) {
|
|
|
|
| 640 |
app.xr.on("available:" + pc.XRTYPE_AR, function (a) {
|
| 641 |
if (!a) message("AR immersive indisponible.");
|
| 642 |
else if (!app.xr.hitTest.supported) message("AR Hit Test non supporté.");
|
| 643 |
+
else message(modelLoaded ? "Touchez l’écran pour démarrer l’AR." : "Chargement du modèle…");
|
| 644 |
});
|
| 645 |
|
| 646 |
if (!app.xr.isAvailable(pc.XRTYPE_AR)) message("AR immersive indisponible.");
|