Le @load event ne fire pas (ou tardivement) sur l'iframe AEP enfermee dans un wrapper avec transform scale(0.42) + viewport simulee 1440px. Resultat : skeleton 'Chargement de la carte AEP...' reste affiche indefiniment, masquant l'iframe meme si elle se charge. Fix : - setTimeout 2.5s dans onMounted qui force revealIframe() inconditionnellement - onIframeLoad clear le timer si l'event fire dans les temps (cas nominal) - retrait du z-10 sur le skeleton (defense en profondeur : si bug residuel, l'iframe sera quand meme visible derriere) - factorisation revealIframe() partagee entre @load et fallback - cleanup du timer dans onUnmounted Build SSR : 5 pages, 0 warning, ~4s. Tests browser manuels a faire par Jules pour confirmer disparition skeleton.
90 lines
2.5 KiB
Vue
90 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
|
|
const iframeRef = ref<HTMLIFrameElement | null>(null)
|
|
const wrapperRef = ref<HTMLDivElement | null>(null)
|
|
const skeletonHidden = ref(false)
|
|
|
|
// Force rendu desktop de l'iframe AEP : viewport simulee 1440px + scale dynamique
|
|
const VIEWPORT_W = 1440
|
|
const iframeScale = ref(0.42)
|
|
let resizeObs: ResizeObserver | null = null
|
|
|
|
const updateScale = () => {
|
|
if (!wrapperRef.value) return
|
|
const w = wrapperRef.value.clientWidth
|
|
if (w > 0) iframeScale.value = w / VIEWPORT_W
|
|
}
|
|
|
|
const iframeStyle = computed(() => ({
|
|
width: VIEWPORT_W + 'px',
|
|
height: (100 / iframeScale.value) + '%',
|
|
transform: `scale(${iframeScale.value})`,
|
|
transformOrigin: '0 0',
|
|
}))
|
|
|
|
let fallbackTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
const revealIframe = () => {
|
|
if (iframeRef.value) {
|
|
iframeRef.value.classList.remove('opacity-0')
|
|
iframeRef.value.classList.add('opacity-100')
|
|
}
|
|
skeletonHidden.value = true
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (wrapperRef.value && typeof ResizeObserver !== 'undefined') {
|
|
updateScale()
|
|
resizeObs = new ResizeObserver(updateScale)
|
|
resizeObs.observe(wrapperRef.value)
|
|
}
|
|
// Fallback : si @load ne fire pas dans 2.5s (transform/scale peut bloquer l'event),
|
|
// on revele quand meme l'iframe pour ne pas laisser le skeleton infini.
|
|
fallbackTimer = setTimeout(() => {
|
|
revealIframe()
|
|
}, 2500)
|
|
})
|
|
onUnmounted(() => {
|
|
resizeObs?.disconnect()
|
|
resizeObs = null
|
|
if (fallbackTimer) {
|
|
clearTimeout(fallbackTimer)
|
|
fallbackTimer = null
|
|
}
|
|
})
|
|
|
|
const onIframeLoad = () => {
|
|
if (fallbackTimer) {
|
|
clearTimeout(fallbackTimer)
|
|
fallbackTimer = null
|
|
}
|
|
revealIframe()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="embed-dynamique h-full flex flex-col relative">
|
|
<div class="h-full">
|
|
<div ref="wrapperRef" class="relative h-full bg-neutral-100 overflow-hidden">
|
|
<div
|
|
v-if="!skeletonHidden"
|
|
id="embed-skeleton"
|
|
class="absolute inset-0 flex items-center justify-center bg-neutral-50 animate-pulse"
|
|
>
|
|
<span class="text-neutral-400 text-sm">Chargement de la carte AEP...</span>
|
|
</div>
|
|
<iframe
|
|
src="https://aep.trans-former.fr/agences"
|
|
title="Carte AEP"
|
|
class="absolute top-0 left-0 border-0 opacity-0 transition-opacity duration-500"
|
|
:style="iframeStyle"
|
|
sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
|
|
@load="onIframeLoad"
|
|
ref="iframeRef"
|
|
></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|