@danielbiegler/vendure-plugin-blurry-image-lazy-loading
Version:
Generates image hashes for displaying blurry previews when loading images on the frontend.
243 lines (186 loc) • 9.39 kB
Markdown

# Vendure Plugin: Blurry Image Lazy Loading
Generates image hashes for displaying blurry previews when loading images on the frontend.
<a href="https://www.npmjs.com/package/@danielbiegler/vendure-plugin-blurry-image-lazy-loading" target="_blank">
<img src="https://badge.fury.io/js/@danielbiegler%2Fvendure-plugin-blurry-image-lazy-loading.svg" alt="npm version badge" height="18">
</a>
## Features
- CustomField on `Asset` Entities containing a hash for your frontend.
- Automatic creation of hashes for new assets via event bus.
- Synchronous creation of hashes for direct consumption.
- Asynchronous creation of hashes via dedicated job queue.
- Out of the box comes with two different hashing strategies and can be extended if need be via a custom `PreviewImageHashStrategy`.
- `ThumbHash` ([Reference](https://github.com/evanw/thumbhash))
- `BlurHash` ([Reference](https://github.com/woltapp/blurhash))
- Suite of end-to-end tests ensuring correctness.
### End-To-End Tests
```
✓ blurry-image-lazy-loading/e2e/thumbhash.e2e-spec.ts (8 tests)
✓ blurry-image-lazy-loading/e2e/blurhash.e2e-spec.ts (8 tests)
✓ blurry-image-lazy-loading/e2e/common.e2e-spec.ts (14 tests)
Test Files 3 passed (3)
Tests 30 passed (30)
```
## Example comparisons
The [ThumbHash Website](https://evanw.github.io/thumbhash/) has an interactive online comparison generator.




## How To: Usage
The plugin adds a couple ways to generate hashes for you to the admin api endpoint.
Your admin-client needs the `CreateAsset` Vendure permission in order to call the following mutations. See [resolver](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/blurry-image-lazy-loading/src/api/admin.resolver.ts).
```gql
extend type Mutation {
"""
Create a preview image hash for one image.
"""
pluginPreviewImageHashCreateImageHash(
input: PluginPreviewImageHashCreateInput!
): PluginPreviewImageHashCreateResult!
"""
Create preview image hashes for a product.
This includes both the product itself and all of its ProductVariant assets.
"""
pluginPreviewImageHashCreateImageHashesForProduct(
input: PluginPreviewImageHashForProductInput!
): PluginPreviewImageHashResult!
"""
Create preview image hashes for an entire collection.
This includes the collection, the contained Product-assets and related ProductVariant-assets.
Due to how large collections can become, you may want to disable the deduplication of asset ids.
If deduplication is enabled, jobs will be created only after gathering all assets first.
If disabled, jobs will be created as the assets are being read.
No deduplication may result in assets being hashed multiple times, but the tradeoff is not having
to hold potentially millions of records in memory and just letting the worker take care of them eventually.
"""
pluginPreviewImageHashCreateImageHashesForCollection(
input: PluginPreviewImageHashForCollectionInput!
): PluginPreviewImageHashResult!
"""
Create preview image hashes for all assets.
This mutation should be handled with extra care since an installation may hold hundreds of thousands of images.
This is mainly useful as a one-time-use utility to initialize all of the assets with hashes after installing the plugin.
"""
pluginPreviewImageHashCreateImageHashesForAllAssets(
input: PluginPreviewImageHashForAllAssetsInput
): PluginPreviewImageHashResult!
}
```
See [api-extensions.ts](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/blurry-image-lazy-loading/src/api/api-extensions.ts) for a complete overview of the graphql extensions and types.
### 1. Add the plugin to your Vendure Config
You can find the package over on [npm](https://www.npmjs.com/package/@danielbiegler/vendure-plugin-blurry-image-lazy-loading) and install it via:
```bash
npm i @danielbiegler/vendure-plugin-blurry-image-lazy-loading
```
The simplest way is relying on the defaults and just adding the plugin with a hashing strategy.
```ts
import { PreviewImageHashPlugin, ThumbHashStrategy } from "@danielbiegler/vendure-plugin-blurry-image-lazy-loading";
export const config: VendureConfig = {
// ...
plugins: [
PreviewImageHashPlugin.init({
hashingStrategy: new ThumbHashStrategy(), // Recommended
}),
],
}
```
It's possible to pass in further configurations into both the plugin and the strategies, for example:
```ts
PreviewImageHashPlugin.init({
enqueueHashingAfterAssetCreation: false,
hashingStrategy: new ThumbHashStrategy({
encoding: "hex",
resizeOptions: {
width: 32,
fit: "contain"
}
}),
})
```
Please refer to the specific [docs](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/blurry-image-lazy-loading/src/types.ts) for how and what you can customize.
### 2. Generate a database migration
This plugin adds a custom field to the `Asset` entity called `previewImageHash`, which requires you to generate a database migration. See Vendure's [migration documentation](https://docs.vendure.io/guides/developer-guide/migrations/) for further guidance.
### 3. Generate hashes
By default the option `enqueueHashingAfterAssetCreation` automatically adds hashing tasks to the dedicated job queue for newly added assets.
For existing assets you can produce hashes either synchronously or via the job queue, of which the latter is recommended for production environments, via the admin API:
```graphql
mutation {
pluginPreviewImageHashCreateImageHash(
input: {
idAsset: "example123",
runSynchronously: true, # False by default
}
) {
# When running asynchronously you get a short status response.
... on PluginPreviewImageHashResult {
code
jobsAddedToQueue
assetsSkipped
message
}
# When running synchronously, you get the Asset directly.
# This is useful for scripts.
... on Asset {
id
name
customFields {
previewImageHash
}
}
}
}
```
### 4. Consume the hashes in your frontend
Now that your assets have hashes you may consume them on your frontend. How and where you consume them exactly is dependent on your setup, but in general it involves the following steps.
1. Retrieve assets, for example:
```graphql
query {
collection(slug: "example") {
productVariants(options: { take: 10 }) {
items {
name
featuredAsset {
preview
width
height
customFields {
previewImageHash
}
}
}
}
}
}
```
2. For example with the `ThumbHashStrategy` and its `BufferEncoding` set to the default `"base64"` you can now decode the hashes with the [provided helper](https://github.com/DanielBiegler/bieglers-vendure-plugins/blob/master/packages/blurry-image-lazy-loading/src/vendors/thumbhash.ts#L374-L384) like so:
```ts
// Node
const buffer = Buffer.from(previewImagehash, "base64");
const dataUrl = thumbHashToDataURL(buffer);
```
```ts
// Browser
// Warning: `atob` can throw! You should make a proper helper method with error handling. Skipping that here for brevity.
const buffer = Uint8Array.from(atob(previewImagehash), (v) => v.charCodeAt(0));
const dataUrl = thumbHashToDataURL(buffer);
```
3. Use the result in your frontend. There are some environment specific implementation details, so we'll use an imaginary react component here for brevity.
```jsx
<MyCustomImgComponent
previewSrc={dataUrl}
src={asset.preview}
width={asset.width}
height={asset.height}
/>
```
## Practical Guides and Resources
### Guides
- [Implementing Low-Quality Image Placeholders (LQIP) in Astro](https://pinelab.studio/blog/implementing-low-quality-image-placeholders-lqip-in-astro/), by Martijn from Pinelab Studio
### Resources
- [React Lazy Load Image Component](https://github.com/Aljullu/react-lazy-load-image-component/), a popular solution for lazy loading comfortably in React projects
---
#### Credits
- Original Banner Photo by [Ray Piedra](https://www.pexels.com/@craytive/) from [Pexels](https://www.pexels.com/photo/beauty-product-in-pink-color-2720447/), edited by [Daniel Biegler](https://www.danielbiegler.de/)
- Lipstick Photo by [Suzy Hazelwood](https://www.pexels.com/@suzyhazelwood/) from [Pexels](https://www.pexels.com/photo/close-up-photo-of-pink-lipstick-and-blush-on-2533266/)
- Camera Photo by [Math](https://www.pexels.com/@madebymath/) from [Pexels](https://www.pexels.com/photo/black-fujifilm-dslr-camera-90946/)