LazyLoader.svelte
1 <!-- 2 LazyLoader Component 3 This component provides loading="lazy" 4 functionality for browsers that do not support it. 5 It uses Intersection Observers to evaluate 6 if an image needs to be loaded. 7 8 DO NOT USE DIRECTLY use LoaderSelector 9 --> 10 <script context="module" lang="ts"> 11 import { get } from 'svelte/store'; 12 import { shouldUseLazyLoader } from '@amp/web-app-components/src/components/Artwork/constants'; 13 import { createArtworkLoaderStore } from '@amp/web-app-components/src/components/Artwork/stores/artworkLoader'; 14 import type { ArtworkLoaderStore } from '@amp/web-app-components/src/components/Artwork/stores/artworkLoader'; 15 import { getRafQueue } from '@amp/web-app-components/src/utils/rafQueue'; 16 17 const rafQueue = getRafQueue(); 18 19 let artworkLookupTable: ArtworkLoaderStore | null = null; 20 let observer: IntersectionObserver | null = null; 21 22 const setupObserver = () => { 23 let options = { 24 root: null, // go off viewport 25 rootMargin: '0px', 26 threshold: 0.0, 27 }; 28 29 return new IntersectionObserver((entries) => { 30 entries.forEach((item) => { 31 rafQueue.add(() => { 32 const storeValue = get(artworkLookupTable); 33 const isItemAlreadyVisible = storeValue.get(item.target); 34 if (!isItemAlreadyVisible) { 35 artworkLookupTable.addEntry( 36 item.target, 37 item.isIntersecting, 38 ); 39 } 40 }); 41 }); 42 }, options); 43 }; 44 if (shouldUseLazyLoader) { 45 observer = setupObserver(); 46 artworkLookupTable = createArtworkLoaderStore(); 47 } 48 </script> 49 50 <script lang="ts"> 51 import { onDestroy } from 'svelte'; 52 53 let isSubscribed = false; 54 55 let container: Element; 56 let isVisible: boolean = false; 57 let unsubscribeToStore: () => void = () => {}; 58 59 const cleanup = () => { 60 unsubscribeToStore(); 61 observer.unobserve(container); 62 artworkLookupTable.cleanupEntry(container); 63 }; 64 65 $: { 66 if (isVisible && isSubscribed) { 67 cleanup(); 68 isSubscribed = false; 69 } 70 } 71 72 export function onSlotMount(artworkComponent: Element) { 73 container = artworkComponent; 74 isSubscribed = true; 75 observer.observe(container); 76 77 unsubscribeToStore = artworkLookupTable.subscribe((map) => { 78 isVisible = map.get(container); 79 }); 80 } 81 82 onDestroy(() => { 83 if (isSubscribed) { 84 cleanup(); 85 } 86 }); 87 </script> 88 89 <slot {isVisible} />