Skip to content

Commit

Permalink
feat: add option to generate blurry placeholder
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasdinonolte committed Nov 28, 2023
1 parent 370989e commit 2675bd6
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 4 deletions.
11 changes: 11 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ export function createImageApi (config: Config) {
lastImage.height ||= height
}

// Puts the placeholder on the last entry of the returned images array. This is based
// on the assumption, that in a multi-format scenario the last item will be the one
// that is being used in the image tag inside the picture group (as the included
// example does it).
if (preset.generateBlurryPlaceholder) {
const { args, generate } = last(last(preset.images).srcset)
const lastSharp = await generate(loadImage(resolve(config.root, filename)), args)
const placeholder = await lastSharp.resize(20).png().toBuffer()
lastImage.placeholder = `data:image/png;base64,${placeholder.toString('base64')}`
}

return imagesAttrs
},
}
Expand Down
10 changes: 7 additions & 3 deletions src/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface WidthPresetOptions extends ImageAttrs {
withImage?: ImageGenerator
media?: string
inferDimensions?: boolean
generateBlurryPlaceholder?: boolean
}

export { mimeTypeFor }
Expand All @@ -24,14 +25,15 @@ export function hdPreset (options: WidthPresetOptions) {
const highDensity = widthPreset({ density: 2, media: '(-webkit-min-device-pixel-ratio: 1.5)', ...options })
const desktopWidth = Math.max(...options.widths as any) || 'original'
const desktop = widthPreset({ ...options, widths: [desktopWidth] })
return { attrs: desktop.attrs, images: highDensity.images.concat(desktop.images), inferDimensions: options.inferDimensions }
return { attrs: desktop.attrs, images: highDensity.images.concat(desktop.images), inferDimensions: options.inferDimensions, generateBlurryPlaceholder: options.generateBlurryPlaceholder }
}

export function widthPreset ({ density, widths, formats, resizeOptions, withImage, inferDimensions, ...options }: WidthPresetOptions): ImagePreset {
export function widthPreset ({ density, widths, formats, resizeOptions, withImage, inferDimensions, generateBlurryPlaceholder, ...options }: WidthPresetOptions): ImagePreset {
const [attrs, sourceAttrs] = extractSourceAttrs(options)
return {
attrs,
inferDimensions,
generateBlurryPlaceholder,
images: Object.entries(formats)
.map(([format, formatOptions]) => ({
...sourceAttrs,
Expand Down Expand Up @@ -64,17 +66,19 @@ interface DensityPresetOptions extends ImageAttrs {
withImage?: ImageGenerator
media?: string
inferDimensions?: boolean
generateBlurryPlaceholder?: boolean
}

function multiply (quantity: number, n?: number | undefined) {
return n ? quantity * n : undefined
}

export function densityPreset ({ baseWidth, baseHeight, density, formats, resizeOptions, withImage, inferDimensions, ...options }: DensityPresetOptions): ImagePreset {
export function densityPreset ({ baseWidth, baseHeight, density, formats, resizeOptions, withImage, inferDimensions, generateBlurryPlaceholder, ...options }: DensityPresetOptions): ImagePreset {
const [attrs, sourceAttrs] = extractSourceAttrs(options)
return {
attrs,
inferDimensions,
generateBlurryPlaceholder,
images: Object.entries(formats)
.map(([format, formatOptions]) => ({
...sourceAttrs,
Expand Down
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ export interface ImageFormatOptions {
export type ImageFormats = Partial<ImageFormatOptions>
export type ImageFormat = keyof ImageFormatOptions

export type ImageAttrs = Partial<HTMLImageElement> & { class?: string }
export interface ImageAttrsAddons {
class?: string
placeholder?: string
}

export type ImageAttrs = Partial<HTMLImageElement> & ImageAttrsAddons

export type ImageGeneratorArgs = Record<string, any>
export type ImageGenerator = (image: Image, args: ImageGeneratorArgs) => Image | Promise<Image>
Expand Down Expand Up @@ -56,6 +61,7 @@ export interface ImageSource {

export interface ImagePreset {
attrs?: ImageAttrs
generateBlurryPlaceholder?: boolean
inferDimensions?: boolean
images: ImageSource[]
}
Expand Down
21 changes: 21 additions & 0 deletions tests/api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,24 @@ describe('inferDimensions', () => {
expect(resolved[0]).not.toHaveProperty('height')
})
})

describe('generateBlurryPlaceholder', () => {
test('generate blurry placeholder', async () => {
const resolved = await getResolvedImage(formatPreset({
generateBlurryPlaceholder: true,
formats: {
original: {},
},
}), false)
expect(resolved[0]).toHaveProperty('placeholder', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAG0lEQVQokWP4TwFgGNVMGmAgUT0KGNX8nzQAAJmLzU+UoPoTAAAAAElFTkSuQmCC')
})

test('don’t generate blurry placeholder', async () => {
const resolved = await getResolvedImage(formatPreset({
formats: {
original: {},
},
}), false)
expect(resolved[0]).not.toHaveProperty('placeholder')
})
})
4 changes: 4 additions & 0 deletions tests/presets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ describe('widthPreset', () => {
jpg: { quality: 80 },
},
inferDimensions: true,
generateBlurryPlaceholder: false,
})

expect(preset.attrs).toEqual({ class: 'img', loading: 'lazy' })
expect(preset.images.length).toEqual(2)
expect(preset.inferDimensions).toEqual(true)
expect(preset.generateBlurryPlaceholder).toEqual(false)

const [webpImage, jpegImage] = preset.images

Expand Down Expand Up @@ -79,11 +81,13 @@ describe('densityPreset', () => {
avif: { quality: 80 },
},
inferDimensions: false,
generateBlurryPlaceholder: true,
})

expect(preset.attrs).toEqual({ loading: 'lazy' })
expect(preset.images.length).toEqual(2)
expect(preset.inferDimensions).toEqual(false)
expect(preset.generateBlurryPlaceholder).toEqual(true)

const [webpImage, avifImage] = preset.images

Expand Down

0 comments on commit 2675bd6

Please sign in to comment.