Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 1x 1x 1x 1x 2x 1x 1x 1x 1x 1x 1x 1x | import { ObserverOptions, IntersectionObserverEntryType, Props } from './types'
const defaultObserverOptions: ObserverOptions = {
root: undefined,
rootMargin: undefined,
threshold: undefined
}
const observerKeys: ObserverOptions[] = []
const observers = new WeakMap<ObserverOptions, IntersectionObserver>()
const images = new WeakMap<Element, { observer: IntersectionObserver; options: Props }>()
export const call = fn => fn && fn()
export const isNull = <T>(obj: T | null): obj is null => obj === null
const createIntersectionObserver = (options: ObserverOptions) =>
new IntersectionObserver(loadingCallback, options)
function loadingCallback(entrys: IntersectionObserverEntryType[]) {
entrys.filter(entry => entry.isIntersecting).forEach(entry => {
const target = entry.target as HTMLImageElement | HTMLDivElement
const metaData = images.get(target)
if (!metaData) {
console.warn('Could not find meta data for image')
return
}
metaData.observer.unobserve(target)
loadImage(metaData.options.image)
.catch(() => {
if (metaData.options.errorImage) {
return loadImage(metaData.options.errorImage)
}
return Promise.resolve(metaData.options.defaultImage)
})
.catch(() => metaData.options.defaultImage)
.then((imagePath: string) => {
setImage(target as HTMLImageElement, imagePath)
addCssClassName(target, 'lazy-loaded')
call(metaData.options.onLoaded)
})
})
}
export function setImage(element: HTMLImageElement | HTMLDivElement, imagePath: string) {
if (isImageElement(element)) {
element.src = imagePath
} else {
element.style.backgroundImage = `url('${imagePath}')`
}
}
export function loadImage(imagePath: string): Promise<string> {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = imagePath
img.onload = () => resolve(imagePath)
img.onerror = err => reject(err)
})
}
export function isImageElement(
element: HTMLImageElement | HTMLDivElement
): element is HTMLImageElement {
return element.nodeName.toLowerCase() === 'img'
}
export function addCssClassName(
element: HTMLImageElement | HTMLDivElement,
cssClassName: string
) {
if (!element.className.includes(cssClassName)) {
element.className += ` ${cssClassName}`
}
}
export function registerImageToLazyLoad(element: Element, metadata: Props) {
const options = metadata.options || defaultObserverOptions
let observerKey = observerKeys.find(
oKey =>
oKey.root === options.root &&
oKey.rootMargin === options.rootMargin &&
oKey.threshold === options.threshold
)
if (!observerKey) {
observerKey = options
observerKeys.push(observerKey)
}
let observer = observers.get(observerKey)
if (!observer) {
observer = createIntersectionObserver(observerKey)
observers.set(observerKey, observer)
}
images.set(element, {
observer,
options: metadata
})
observer.observe(element)
}
|