lazy loading, debug options and configurable step size

This commit is contained in:
cupcakearmy 2020-11-11 21:13:42 +01:00
parent 4aedf94b92
commit dfd3031825
No known key found for this signature in database
GPG Key ID: D28129AE5654D9D9

View File

@ -1,57 +1,71 @@
import { Cloudinary, Configuration, Transformation } from 'cloudinary-core' import { Cloudinary, Configuration, Transformation } from 'cloudinary-core'
let cl: Cloudinary | null = null type ElementOrString = Element | string
type Size = { width: number; height: number }
type BindType = ElementOrString | true
type BindObject = BindType | { width?: BindType; height?: BindType }
export function initialize(options: Configuration.Options) { export type InitParameters = { debug?: boolean }
cl = Cloudinary.new(options) export type ImageParameters = {
src: string
options?: Transformation | Transformation.Options
bind?: BindObject
lazy?: boolean | IntersectionObserverInit
step?: number
} }
type ElementOrString = Element | string let cl: Cloudinary | null = null
let DEBUG: boolean = false
function log(...msg: any[]) {
if (DEBUG) console.debug(...msg)
}
export function initialize(options: Configuration.Options, { debug }: InitParameters = {}) {
DEBUG = debug || false
cl = Cloudinary.new(options)
}
const defaults: Transformation | Transformation.Options = { const defaults: Transformation | Transformation.Options = {
fetchFormat: 'auto', fetchFormat: 'auto',
quality: 'auto:good', quality: 'auto:good',
} }
function calculateApproxRealSize(size: string, step = 200) { function calculateApproxRealSize(size: string, step: number): number {
const withRatio = (parseInt(size) * window.devicePixelRatio) | 0 const withRatio = (parseInt(size) * window.devicePixelRatio) | 0
return withRatio - (withRatio % step) + step const approx = withRatio - (withRatio % step) + step
log('Size', withRatio, approx, step)
return approx
} }
function getSizeOfElement(el: Element) { function getSizeOfElement(el: Element, step: number): Size {
const styles = window.getComputedStyle(el) const styles = window.getComputedStyle(el)
log('GetSizeOfElement', el, styles)
return { return {
width: calculateApproxRealSize(styles.width), width: calculateApproxRealSize(styles.width, step),
height: calculateApproxRealSize(styles.height), height: calculateApproxRealSize(styles.height, step),
} }
} }
function getSizeOfElementOrSelector(node: ElementOrString, elOrString: ElementOrString) { function getSizeOfElementOrSelector(node: ElementOrString, elOrString: ElementOrString, step: number): Size {
if (typeof elOrString === 'string') { if (typeof elOrString === 'string') {
const search = typeof node === 'string' ? window.document.querySelector(node) : node const search = typeof node === 'string' ? window.document.querySelector(node) : node
if (!search) throw new Error('Could not find element: ' + node) if (!search) throw new Error('Could not find element: ' + node)
const closest = search.closest(elOrString) const closest = search.closest(elOrString)
if (closest) return getSizeOfElement(closest) if (closest) return getSizeOfElement(closest, step)
else throw new Error('Could not find element: ' + elOrString) else throw new Error('Could not find element: ' + elOrString)
} else { } else {
return getSizeOfElement(elOrString) return getSizeOfElement(elOrString, step)
} }
} }
type BindType = ElementOrString | true export function image(node: HTMLImageElement, parameters: ImageParameters) {
type BindObject = BindType | { width?: BindType; height?: BindType } if (!parameters || !parameters.src) throw new Error('No url provided for cloudinary')
export type ImageParameters = { let { src, options, bind, lazy, step } = parameters
src: string log('Image Declared', parameters)
options?: Transformation | Transformation.Options
bind?: BindObject
}
export function image(node: HTMLImageElement, parameters?: ImageParameters) {
if (!parameters) throw new Error('No url provided for cloudinary')
let { src, options, bind } = parameters
options = options || {} options = options || {}
step = step ?? 200
if (!cl) throw new Error('Cloudinary not initialized') if (!cl) throw new Error('Cloudinary not initialized')
if (!src) throw new Error('Src must be set in use:image') if (!src) throw new Error('Src must be set in use:image')
@ -62,20 +76,32 @@ export function image(node: HTMLImageElement, parameters?: ImageParameters) {
} }
if (!options.crop) options.crop = 'fill' if (!options.crop) options.crop = 'fill'
if (bind instanceof Element) Object.assign(options, getSizeOfElement(bind)) if (bind instanceof Element) Object.assign(options, getSizeOfElement(bind, step))
else if (typeof bind === 'string') { else if (typeof bind === 'string') {
Object.assign(options, getSizeOfElementOrSelector(node, bind)) Object.assign(options, getSizeOfElementOrSelector(node, bind, step))
} else if (typeof bind === 'object') { } else if (typeof bind === 'object') {
if (bind.width) { if (bind.width) {
options.width = getSizeOfElementOrSelector(node, bind.width === true ? node : bind.width).width options.width = getSizeOfElementOrSelector(node, bind.width === true ? node : bind.width, step).width
} }
if (bind.height) { if (bind.height) {
options.height = getSizeOfElementOrSelector(node, bind.height === true ? node : bind.height).height options.height = getSizeOfElementOrSelector(node, bind.height === true ? node : bind.height, step).height
} }
} }
} }
const all: Transformation | Transformation.Options = { ...defaults, ...options } const all: Transformation | Transformation.Options = { ...defaults, ...options }
const attrs: any = cl.imageTag(parameters.src, all).attributes() const attrs: any = cl.imageTag(parameters.src, all).attributes()
node.src = attrs.src const replace = () => (node.src = attrs.src)
if (lazy && typeof IntersectionObserver !== 'undefined') {
const options: IntersectionObserverInit = lazy === true ? { rootMargin: '25%', threshold: 0 } : lazy
new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
observer.disconnect()
replace()
}
}, options).observe(node)
} else {
replace()
}
} }