playcanvas_viewer / interface.js
MikaFil's picture
Update interface.js
5b7c41e verified
// fullscreen.js
(function () {
// ─── 1. Localiser la balise <script> ────────────────────────────────────────
const scriptTag = document.currentScript || (function () {
const all = document.getElementsByTagName('script');
for (let i = all.length - 1; i >= 0; i--) {
if (all[i].src && all[i].src.includes('fullscreen.js')) return all[i];
}
return all[all.length - 1];
})();
const playcanvasUrl = scriptTag.getAttribute('data-src');
if (!playcanvasUrl) return;
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
const isMobile = isIOS || /Android/i.test(navigator.userAgent);
const id = Math.random().toString(36).substr(2, 8);
// ─── 4. Calcul des ratios (Desktop & Mobile Portrait) ────────────────────────
function computeAspectPadding(aspectStr) {
if (!aspectStr) return null;
if (aspectStr.includes(':')) {
const [w, h] = aspectStr.split(':').map(Number);
return (w > 0 && h > 0) ? (h / w) * 100 + '%' : null;
}
const v = parseFloat(aspectStr);
return (v > 0) ? (100 / v) + '%' : null;
}
const desktopPadding = computeAspectPadding(scriptTag.getAttribute('data-aspect')) || '56.25%';
const mobilePadding = computeAspectPadding(scriptTag.getAttribute('data-aspect-mobile')) || desktopPadding;
// ─── 5. Injection du CSS avec Media Queries ──────────────────────────────────
const style = document.createElement('style');
style.textContent = `
.pc-embed-wrapper-${id} {
position: relative;
width: 100%;
height: 0;
overflow: hidden;
background: #000;
box-sizing: border-box;
/* Ratio par dΓ©faut (Paysage / Desktop) */
padding-bottom: ${desktopPadding};
}
/* Ratio Mobile Portrait : appliquΓ© uniquement si l'Γ©cran est plus haut que large */
@media (orientation: portrait) and (max-width: 768px) {
.pc-embed-wrapper-${id} {
padding-bottom: ${mobilePadding};
}
}
.pc-embed-wrapper-${id}.fake-fullscreen {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100vw !important;
height: 100vh !important;
height: 100dvh !important;
max-width: 100vw !important;
max-height: 100dvh !important;
padding-bottom: 0 !important;
margin: 0 !important;
z-index: 99999;
}
.pc-embed-inner-${id} {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
}
.pc-embed-inner-${id} iframe {
width: 100%; height: 100%;
border: none;
display: block;
}
/* ── Boutons (fullscreen + help) ── */
.pc-fs-btn-${id},
.pc-help-btn-${id} {
position: absolute;
top: 10px;
z-index: 10;
width: 36px;
height: 36px;
min-width: 36px;
min-height: 36px;
border-radius: 50%;
border: none;
background: #2E2E2EB3;
color: #fff;
font-size: 18px;
line-height: 1;
padding: 0;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
-webkit-appearance: none;
appearance: none;
cursor: pointer;
user-select: none;
}
.pc-fs-btn-${id} {
right: 10px;
}
.pc-help-btn-${id} {
right: 54px; /* 10px + 36px + 8px de marge */
}
/* ── Panneau d'aide ── */
.pc-help-panel-${id} {
display: none;
position: absolute;
top: 30%;
left: 50%;
transform: translateX(-50%);
z-index: 20;
background: #2E2E2EB3;
color: #fff;
border-radius: 8px;
padding: 10px 14px 10px 14px;
font-size: 13px;
line-height: 1.7;
min-width: 220px;
max-width: 80%;
box-sizing: border-box;
}
.pc-help-panel-${id}.visible {
display: block;
}
.pc-help-close-${id} {
position: absolute;
top: 4px;
right: 10px;
cursor: pointer;
font-size: 18px;
line-height: 1;
color: #fff;
user-select: none;
}
.pc-help-text-${id} {
margin-top: 4px;
}
`;
document.head.appendChild(style);
// ─── 6. Construction du DOM ──────────────────────────────────────────────────
const wrapper = document.createElement('div');
wrapper.className = `pc-embed-wrapper-${id}`;
const inner = document.createElement('div');
inner.className = `pc-embed-inner-${id}`;
const iframe = document.createElement('iframe');
const urlObj = new URL(playcanvasUrl);
urlObj.searchParams.set('overlay', 'false');
iframe.src = urlObj.href;
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allow', 'autoplay; fullscreen');
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-pointer-lock allow-popups allow-forms');
const fsBtn = document.createElement('button');
fsBtn.className = `pc-fs-btn-${id}`;
fsBtn.textContent = '⇱';
const helpBtn = document.createElement('button');
helpBtn.className = `pc-help-btn-${id}`;
helpBtn.textContent = '?';
// Panneau d'aide
const helpPanel = document.createElement('div');
helpPanel.className = `pc-help-panel-${id}`;
const helpClose = document.createElement('span');
helpClose.className = `pc-help-close-${id}`;
helpClose.textContent = 'Γ—';
const helpText = document.createElement('div');
helpText.className = `pc-help-text-${id}`;
if (isMobile) {
helpText.innerHTML =
'- Se déplacer avec les flèches.<br>' +
'- Tourner avec un doigt.<br>' +
'- Zoomer avec deux doigts.<br>' +
'- ⇱ plein Γ©cran.<br>' +
'- Modifier la qualitΓ© avec le menu Γ  gauche.';
} else {
helpText.innerHTML =
'- Se déplacer avec les flèches ou Z, Q, S, D.<br>' +
'- Tourner avec clic gauche.<br>' +
'- Zoomer avec la molette.<br>' +
'- ⇱ plein Γ©cran.<br>' +
'- Modifier la qualitΓ© avec le menu Γ  gauche.';
}
helpPanel.appendChild(helpClose);
helpPanel.appendChild(helpText);
inner.appendChild(iframe);
wrapper.appendChild(inner);
wrapper.appendChild(helpBtn);
wrapper.appendChild(fsBtn);
wrapper.appendChild(helpPanel);
scriptTag.parentNode.insertBefore(wrapper, scriptTag.nextSibling);
// ─── 7. Γ‰tat & Helper ────────────────────────────────────────────────────────
let isFullscreen = false;
let savedParent = null;
let savedNextSibling = null;
function getHeightUnit() {
return (CSS && CSS.supports && CSS.supports('height', '100dvh')) ? '100dvh' : '100vh';
}
// ─── Gestion du panneau d'aide ───────────────────────────────────────────────
function toggleHelpPanel(e) {
e.stopPropagation();
helpPanel.classList.toggle('visible');
}
function hideHelpPanel() {
helpPanel.classList.remove('visible');
}
helpBtn.addEventListener('click', toggleHelpPanel);
helpClose.addEventListener('click', (e) => {
e.stopPropagation();
hideHelpPanel();
});
// ─── 8. Styles Plein Γ‰cran ───────────────────────────────────────────────────
function applyFullscreenStyles() {
const h = isIOS ? getHeightUnit() : '100vh';
wrapper.style.position = 'fixed';
wrapper.style.top = '0';
wrapper.style.left = '0';
wrapper.style.width = '100vw';
wrapper.style.height = h;
wrapper.style.maxHeight = h;
wrapper.style.paddingBottom = '0';
wrapper.style.margin = '0';
wrapper.style.zIndex = '99999';
wrapper.classList.add('fake-fullscreen');
fsBtn.textContent = '⇲';
isFullscreen = true;
}
function applyFakeFullscreenStyles() {
savedParent = wrapper.parentNode;
savedNextSibling = wrapper.nextSibling;
document.body.appendChild(wrapper);
applyFullscreenStyles();
}
function restoreStyles() {
wrapper.style.cssText = '';
// Le CSS via les Media Queries reprendra le dessus automatiquement ici
wrapper.classList.remove('fake-fullscreen');
fsBtn.textContent = '⇱';
isFullscreen = false;
if (savedParent) {
savedParent.insertBefore(wrapper, savedNextSibling);
savedParent = null;
savedNextSibling = null;
}
}
// ─── 9. Gestionnaires d'Γ©vΓ©nements ───────────────────────────────────────────
function enterFullscreen() {
hideHelpPanel();
if (isIOS) {
applyFakeFullscreenStyles();
document.body.style.overflow = 'hidden';
} else {
const el = wrapper;
const req = el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen || el.msRequestFullscreen;
if (req) {
req.call(el).catch(() => {
applyFakeFullscreenStyles();
document.body.style.overflow = 'hidden';
});
} else {
applyFakeFullscreenStyles();
document.body.style.overflow = 'hidden';
}
}
}
function exitFullscreen() {
if (document.fullscreenElement || document.webkitFullscreenElement) {
(document.exitFullscreen || document.webkitExitFullscreen || function(){}).call(document);
}
restoreStyles();
document.body.style.overflow = '';
}
fsBtn.addEventListener('click', (e) => {
e.stopPropagation();
isFullscreen ? exitFullscreen() : enterFullscreen();
});
document.addEventListener('fullscreenchange', () => {
const fsEl = document.fullscreenElement || document.webkitFullscreenElement;
if (!fsEl && isFullscreen) exitFullscreen();
else if (fsEl === wrapper && !isFullscreen) applyFullscreenStyles();
});
window.addEventListener('resize', () => {
if (isFullscreen) {
const h = isIOS ? getHeightUnit() : '100vh';
wrapper.style.height = h;
wrapper.style.maxHeight = h;
}
});
})();