fix(next/image): empty blur image when animated (#54028)
Partial fix for #54012: do not generate a blur image in the image loader when the image is detected to be animated, rather than returning the *entire* animated image as the blur image.
This commit is contained in:
parent
c5fb04663a
commit
c1fa78bf6c
8 changed files with 47 additions and 12 deletions
|
@ -243,7 +243,7 @@ placeholder = 'empty' // "empty" | "blur" | "data:image/..."
|
|||
|
||||
A placeholder to use while the image is loading. Possible values are `blur`, `empty`, or `data:image/...`. Defaults to `empty`.
|
||||
|
||||
When `blur`, the [`blurDataURL`](#blurdataurl) property will be used as the placeholder. If `src` is an object from a [static import](/docs/app/building-your-application/optimizing/images#local-images) and the imported image is `.jpg`, `.png`, `.webp`, or `.avif`, then `blurDataURL` will be automatically populated.
|
||||
When `blur`, the [`blurDataURL`](#blurdataurl) property will be used as the placeholder. If `src` is an object from a [static import](/docs/app/building-your-application/optimizing/images#local-images) and the imported image is `.jpg`, `.png`, `.webp`, or `.avif`, then `blurDataURL` will be automatically populated, except when the image is detected to be animated.
|
||||
|
||||
For dynamic images, you must provide the [`blurDataURL`](#blurdataurl) property. Solutions such as [Plaiceholder](https://github.com/joe-bell/plaiceholder) can help with `base64` generation.
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ You are attempting use the `next/image` component with `placeholder=blur` proper
|
|||
|
||||
The `blurDataURL` might be missing because you're using a string for `src` instead of a static import.
|
||||
|
||||
Or `blurDataURL` might be missing because the static import is an unsupported image format. Only jpg, png, webp, and avif are supported at this time.
|
||||
Or `blurDataURL` might be missing because the static import is an unsupported image format. Only jpg, png, webp, and avif are supported at this time, though animated images are not supported.
|
||||
|
||||
## Possible Ways to Fix It
|
||||
|
||||
- Add a [`blurDataURL`](/docs/pages/api-reference/components/image#blurdataurl) property, the contents should be a small Data URL to represent the image
|
||||
- Change the [`src`](/docs/pages/api-reference/components/image#src) property to a static import with one of the supported file types: jpg, png, or webp
|
||||
- Change the [`src`](/docs/pages/api-reference/components/image#src) property to a static import with one of the supported file types: jpg, png, webp, or avif (animated images not supported)
|
||||
- Remove the [`placeholder`](/docs/pages/api-reference/components/image#placeholder) property, effectively no blur effect
|
||||
|
|
|
@ -37,7 +37,7 @@ export async function getBlurImage(
|
|||
let blurWidth: number = 0
|
||||
let blurHeight: number = 0
|
||||
|
||||
if (VALID_BLUR_EXT.includes(extension)) {
|
||||
if (VALID_BLUR_EXT.includes(extension) && !isAnimated(content)) {
|
||||
// Shrink the image's largest dimension
|
||||
if (imageSize.width >= imageSize.height) {
|
||||
blurWidth = BLUR_IMG_SIZE
|
||||
|
@ -65,18 +65,15 @@ export async function getBlurImage(
|
|||
blurDataURL = url.href.slice(prefix.length)
|
||||
} else {
|
||||
const resizeImageSpan = tracing('image-resize')
|
||||
const resizedImage = await resizeImageSpan.traceAsyncFn(() => {
|
||||
if (isAnimated(content)) {
|
||||
return content
|
||||
}
|
||||
return optimizeImage({
|
||||
const resizedImage = await resizeImageSpan.traceAsyncFn(() =>
|
||||
optimizeImage({
|
||||
buffer: content,
|
||||
width: blurWidth,
|
||||
height: blurHeight,
|
||||
contentType: `image/${extension}`,
|
||||
quality: BLUR_QUALITY,
|
||||
})
|
||||
})
|
||||
)
|
||||
const blurDataURLSpan = tracing('image-base64-tostring')
|
||||
blurDataURL = blurDataURLSpan.traceFn(
|
||||
() =>
|
||||
|
|
|
@ -807,7 +807,7 @@ export default function Image({
|
|||
- Add a "blurDataURL" property, the contents should be a small Data URL to represent the image
|
||||
- Change the "src" property to a static import with one of the supported file types: ${VALID_BLUR_EXT.join(
|
||||
','
|
||||
)}
|
||||
)} (animated images not supported)
|
||||
- Remove the "placeholder" property, effectively no blur effect
|
||||
Read more: https://nextjs.org/docs/messages/placeholder-blur-data-url`
|
||||
)
|
||||
|
|
|
@ -493,7 +493,7 @@ export function getImgProps(
|
|||
- Add a "blurDataURL" property, the contents should be a small Data URL to represent the image
|
||||
- Change the "src" property to a static import with one of the supported file types: ${VALID_BLUR_EXT.join(
|
||||
','
|
||||
)}
|
||||
)} (animated images not supported)
|
||||
- Remove the "placeholder" property, effectively no blur effect
|
||||
Read more: https://nextjs.org/docs/messages/placeholder-blur-data-url`
|
||||
)
|
||||
|
|
38
test/unit/next-image-loader/get-blur-image.test.ts
Normal file
38
test/unit/next-image-loader/get-blur-image.test.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* eslint-env jest */
|
||||
import { getBlurImage } from 'next/dist/build/webpack/loaders/next-image-loader/blur'
|
||||
import { readFile } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
|
||||
const getImage = (filepath) => readFile(join(__dirname, filepath))
|
||||
|
||||
const tracing = () => ({
|
||||
traceFn: (fn, ...args) => fn(...args),
|
||||
traceAsyncFn: (fn, ...args) => fn(...args),
|
||||
})
|
||||
|
||||
const context = { basePath: '', outputPath: '', isDev: false, tracing }
|
||||
|
||||
describe('getBlurImage', () => {
|
||||
it('should return image for jpg', async () => {
|
||||
const buffer = await getImage('./images/test.jpg')
|
||||
const result = await getBlurImage(
|
||||
buffer,
|
||||
'jpeg',
|
||||
{ width: 400, height: 400 },
|
||||
context
|
||||
)
|
||||
expect(result).toBeObject()
|
||||
expect(result.dataURL).toBeString()
|
||||
})
|
||||
it('should return undefined for animated webp', async () => {
|
||||
const buffer = await getImage('./images/animated.webp')
|
||||
const result = await getBlurImage(
|
||||
buffer,
|
||||
'webp',
|
||||
{ width: 400, height: 400 },
|
||||
context
|
||||
)
|
||||
expect(result).toBeObject()
|
||||
expect(result.dataURL).toBeUndefined()
|
||||
})
|
||||
})
|
BIN
test/unit/next-image-loader/images/animated.webp
Normal file
BIN
test/unit/next-image-loader/images/animated.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
test/unit/next-image-loader/images/test.jpg
Normal file
BIN
test/unit/next-image-loader/images/test.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Loading…
Reference in a new issue