perf(reader): virtual rendering avec IntersectionObserver en mode scroll
Remplace le rendu de tous les composants ReaderPage par un système de virtual rendering : seules les pages dans la zone ±1000px du viewport sont montées, les autres sont remplacées par un placeholder dimensionné. - InfiniteReader : ajout visibilityObserver + mountedPageIndices (Set réactif), helper getPlaceholderHeight(), suppression de 5 console.log - ReaderPage : prop windowWidth injectable depuis le parent, listener resize conditionnel, suppression de 3 console.log de debug
This commit is contained in:
parent
c268b2c312
commit
aba8e36231
@@ -15,7 +15,6 @@
|
||||
:alt="`Page ${pageNumber} (Double page)`"
|
||||
class="page-image rotated"
|
||||
:style="doublePageRotatedStyle"
|
||||
:loading="loading"
|
||||
@load="handleImageLoad"
|
||||
ref="imageRef" />
|
||||
<div class="rotation-hint">
|
||||
@@ -34,7 +33,6 @@
|
||||
:alt="`Page ${pageNumber} (Double page)`"
|
||||
class="page-image scrollable"
|
||||
:style="doublePageScrollStyle"
|
||||
:loading="loading"
|
||||
@load="handleImageLoad"
|
||||
ref="imageRef" />
|
||||
</div>
|
||||
@@ -54,7 +52,6 @@
|
||||
:alt="`Page ${pageNumber}`"
|
||||
class="page-image"
|
||||
:style="imageStyle"
|
||||
:loading="loading"
|
||||
@load="handleImageLoad"
|
||||
ref="imageRef" />
|
||||
</div>
|
||||
@@ -82,9 +79,9 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
default: 'rotate', // 'rotate', 'scroll', 'normal'
|
||||
validator: (value) => ['rotate', 'scroll', 'normal'].includes(value)
|
||||
},
|
||||
loading: {
|
||||
type: String,
|
||||
default: 'lazy',
|
||||
windowWidth: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
@@ -103,8 +100,11 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
const scrollContainerRef = ref(null);
|
||||
const naturalWidth = ref(0);
|
||||
const naturalHeight = ref(0);
|
||||
const windowWidth = ref(window.innerWidth);
|
||||
const isMobile = computed(() => windowWidth.value < 768);
|
||||
const localWindowWidth = ref(window.innerWidth);
|
||||
const effectiveWindowWidth = computed(() =>
|
||||
props.windowWidth !== null ? props.windowWidth : localWindowWidth.value
|
||||
);
|
||||
const isMobile = computed(() => effectiveWindowWidth.value < 768);
|
||||
const imageLoaded = ref(false);
|
||||
|
||||
const imageSource = computed(() => {
|
||||
@@ -123,17 +123,13 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
// Utiliser d'abord les dimensions de l'API si disponibles
|
||||
if (props.pageData?.dimensions?.width && props.pageData?.dimensions?.height) {
|
||||
const ratio = props.pageData.dimensions.width / props.pageData.dimensions.height;
|
||||
const isDouble = ratio > threshold;
|
||||
console.log(`API Dimensions - Page ${props.pageNumber}: ${props.pageData.dimensions.width}x${props.pageData.dimensions.height}, ratio: ${ratio.toFixed(2)}, isDouble: ${isDouble}`);
|
||||
return isDouble;
|
||||
return ratio > threshold;
|
||||
}
|
||||
|
||||
// Fallback sur les dimensions naturelles de l'image (seulement si l'image est chargée)
|
||||
if (imageLoaded.value && naturalWidth.value && naturalHeight.value) {
|
||||
const ratio = naturalWidth.value / naturalHeight.value;
|
||||
const isDouble = ratio > threshold;
|
||||
console.log(`Natural Dimensions - Page ${props.pageNumber}: ${naturalWidth.value}x${naturalHeight.value}, ratio: ${ratio.toFixed(2)}, isDouble: ${isDouble}`);
|
||||
return isDouble;
|
||||
return ratio > threshold;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -144,7 +140,6 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
naturalWidth.value = imageRef.value.naturalWidth;
|
||||
naturalHeight.value = imageRef.value.naturalHeight;
|
||||
imageLoaded.value = true;
|
||||
console.log(`Image loaded - Page ${props.pageNumber}: ${naturalWidth.value}x${naturalHeight.value}`);
|
||||
|
||||
// Positionner le scroll à droite si c'est le mode scroll
|
||||
if (props.doublePageMode === 'scroll' && scrollContainerRef.value) {
|
||||
@@ -195,7 +190,7 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
|
||||
if (!width || !height) return null;
|
||||
|
||||
const availableWidth = windowWidth.value;
|
||||
const availableWidth = effectiveWindowWidth.value;
|
||||
|
||||
// Si la largeur disponible est < 1200px : utiliser 95% de la largeur
|
||||
if (availableWidth < 1200) {
|
||||
@@ -244,7 +239,7 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
if (!width || !height) return {};
|
||||
|
||||
// En mode rotation : maximiser l'utilisation de l'espace
|
||||
const availableWidth = windowWidth.value;
|
||||
const availableWidth = effectiveWindowWidth.value;
|
||||
const availableHeight = window.innerHeight - 100; // Laisser un peu d'espace pour les contrôles
|
||||
|
||||
// Après rotation, la largeur originale devient la hauteur affichée
|
||||
@@ -294,20 +289,18 @@ import { useReaderStore } from '../../application/store/readerStore';
|
||||
};
|
||||
});
|
||||
|
||||
// Gestion du redimensionnement de la fenêtre
|
||||
const handleResize = () => {
|
||||
windowWidth.value = window.innerWidth;
|
||||
};
|
||||
let ownResizeHandler = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (imageRef.value && imageRef.value.complete) {
|
||||
handleImageLoad();
|
||||
if (props.windowWidth === null) {
|
||||
ownResizeHandler = () => { localWindowWidth.value = window.innerWidth; };
|
||||
window.addEventListener('resize', ownResizeHandler, { passive: true });
|
||||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
if (imageRef.value?.complete) handleImageLoad();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (ownResizeHandler) window.removeEventListener('resize', ownResizeHandler);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user