Skip to content

Commit

Permalink
Support image blur placeholders during development (#3234)
Browse files Browse the repository at this point in the history
## Description

This is a quality-of-life change that adds an image placeholder (though
not derived from the original image) during development.

Before:


https://github.com/user-attachments/assets/9b9ea1cf-499b-4411-8209-7bb2dba75a4d



After:


https://github.com/user-attachments/assets/45cde166-ee16-40da-9d35-076ba18169b1




## Checklist
- [x] I have read and understood the [WATcloud
Guidelines](https://cloud.watonomous.ca/docs/community-docs/watcloud/guidelines)
- [x] I have performed a self-review of my code
  • Loading branch information
ben-z authored Sep 23, 2024
1 parent 46f26d7 commit fd3a80e
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 5 deletions.
9 changes: 5 additions & 4 deletions components/picture.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { WATcloudStaticImage } from "@/build/fixtures/images";
import { getImageBlurSvg } from "@/lib/image-blur-svg";
import { shimmer, toBase64 } from "@/lib/utils";

/**
* Renders an image component with multiple sources and an alt text.
Expand All @@ -23,12 +24,12 @@ export default function Picture({
imgClassName?: string;
style?: React.CSSProperties;
}) {
// The blur logic is derived from https://github.com/vercel/next.js/blob/98be3ba23ea65ac5b581999d79a1093f147b46f0/packages/next/src/shared/lib/get-img-props.ts#L626
// The blur logic is derived from https://github.com/vercel/next.js/blob/98be3ba23ea65ac5b581999d79a1093f147b46f0/packages/next/src/shared/lib/get-img-props.ts

// blurDataURL is a base64-encoded string generated by the nextjs image loader
// This is only generated during production builds, so in development, we might see
// no placeholders.
const blurDataURL = image.jpg.blurDataURL || "";
// This is only generated during production builds, and is set to a (broken) URL starting with "/_next"
// during development. In that case, we use a placeholder SVG instead.
const blurDataURL = (image.jpg.blurDataURL || "/_next").startsWith("/_next") ? `data:image/svg+xml;base64,${toBase64(shimmer(image.jpg.width, image.jpg.height))}` : image.jpg.blurDataURL as string;

const backgroundImage = `url("data:image/svg+xml;charset=utf-8,${getImageBlurSvg(
{
Expand Down
23 changes: 22 additions & 1 deletion lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,25 @@ export function maxBy<T>(array: T[], iteratee: (element: T) => number): T | unde

export function minBy<T>(array: T[], iteratee: (element: T) => number): T | undefined {
return maxBy(array, (element) => -iteratee(element));
}
}

// Derived from https://github.com/vercel/next.js/blob/4380d0b79d07637abd50a584b51317eae1297885/examples/image-legacy-component/pages/shimmer.tsx#L4-L16
export const shimmer = (w: number, h: number) => `
<svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g">
<stop stop-color="#333" offset="20%" />
<stop stop-color="#222" offset="50%" />
<stop stop-color="#333" offset="70%" />
</linearGradient>
</defs>
<rect width="${w}" height="${h}" fill="#333" />
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
</svg>`;

// Derived from https://github.com/vercel/next.js/blob/4380d0b79d07637abd50a584b51317eae1297885/examples/image-legacy-component/pages/shimmer.tsx#L18-L21
export const toBase64 = (str: string) =>
typeof window === "undefined"
? Buffer.from(str).toString("base64")
: window.btoa(str);

0 comments on commit fd3a80e

Please sign in to comment.