Open monolithed opened 1 year ago
It's a bit amusing to see such warnings over a non-optimized Image tag:
As a temporary workaround: react-lazy-load-image-component + Squoosh + svg-blur-up or the following script:
import fs from 'node:fs';
import {readdir} from 'node:fs/promises';
import {parse, resolve} from 'node:path';
import sharp, {
Metadata,
Sharp
} from 'sharp';
type Options = {
size?: number;
extension?: string;
stdDeviation?: number;
preserveAspectRatio?: 'none' | 'xMidYMid' | 'xMidYMid slice';
};
class BlurImage {
constructor(
protected input: string,
protected options: Options = {}
)
{
this.options = {
stdDeviation: 60,
size: 40,
extension: '.webp',
preserveAspectRatio: 'xMidYMid',
...options
};
}
getSVGTemplate = (
image: Buffer,
{
width,
height,
format
}: Metadata
): Buffer => {
const content = image.toString('base64');
const {
preserveAspectRatio,
stdDeviation
} = this.options;
return Buffer.from(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}">
<filter id="a" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="${stdDeviation}" />
<feComponentTransfer>
<feFuncA type="discrete" tableValues="1 1"/>
</feComponentTransfer>
</filter>
<image height="100%"
width="100%"
x="0"
y="0"
preserveAspectRatio="${preserveAspectRatio}"
filter="url(#a)"
href="data:image/${format};base64,${content}"
/>
</svg>`
);
};
resizeImage = (image: Sharp) => {
const {size} = this.options;
return image.resize(size).toBuffer({resolveWithObject: true});
};
getImage = async (file: string) => {
const image = sharp(file, {sequentialRead: true});
const meta = await image.metadata();
const {data} = await this.resizeImage(image);
return this.getSVGTemplate(data, meta);
}
convert = async (): Promise<void> => {
const files = await this.getFiles(this.input);
for (const file of files) {
const {name, ext, dir} = parse(file);
if (ext !== this.options.extension) {
continue;
}
const output = `${resolve(dir, name)}.svg`;
const image = await this.getImage(file);
fs.writeFileSync(output, image, {
encoding: 'utf8',
flag: 'w'
});
}
};
getFiles = async (directory: string): Promise<string[]> => {
const entries = await readdir(directory, {
withFileTypes: true
});
const files = entries.map((entry) => {
const child = resolve(directory, entry.name);
if (entry.isDirectory()) {
return this.getFiles(child);
} else {
return child;
}
});
const result = await Promise.all(files);
return result.flat();
}
}
const blurImage = new BlurImage('./public');
blurImage.convert(); // converts *.webp images to *.svg
.wrapper {
position: relative;
}
.placeholder {
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: 50% 50%;
background-repeat: no-repeat;
}
.image {
overflow: hidden;
display: block;
position: relative;
object-fit: cover;
object-position: center;
width: 100%;
height: 100%;
}
import React from 'react';
import cn from 'classnames';
import {LazyLoadImage} from 'react-lazy-load-image-component';
import style from './index.module.css';
type Attributes = Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'placeholder' | 'alt'>;
type Props = Attributes & {
src: string;
alt?: string;
placeholder?: boolean;
};
const Image: React.FunctionComponent<Props> = ({
className,
placeholder,
src,
alt,
...props
}) => {
if (!placeholder) {
return (
<img src={src} className={cn([style.image, className])} alt={alt}/>
)
}
const placeholderSrc = src.replace(/[^.]+$/, 'svg');
return (
<LazyLoadImage src={src}
wrapperClassName={style.wrapper}
placeholder={
<img src={placeholderSrc}
className={
cn([
style.placeholder,
style.image,
className
])}
alt={alt}
/>
}
className={cn([
style.image,
className
])}
decoding="async"
width="100%"
height="100%"
alt=""
{...props}
/>
);
};
export {Image};
export type {Props as ImageProps};
<Image src="/test.webp"
placeholder={true}
srcset="x.jpg 480w, y.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
/>
Wonder if sizes="auto"
will help with this problem in future (once more browsers support it):
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
Standalone mode (output: "standalone")
Describe the Bug
I want to generate an
srcset
based on exactly what I passed tosizes
. It's required because the sizes of my images are carefully tailored to match different screen dimensions, and their distortion or arbitrary sizing is unacceptable.Having set the 'sizes' attribute, I encountered the following issues:
1) The values in the
srcset
do not match thesizes
values 2) Only the largest file size is always delivered 3) Thesrcset
generated is larger than the actual file size. 4) In thestandalone
directory, I couldn't find images different from the main one. Should they be generated? 5) I tried to setsrcSet
manually, but it's not allowed. Why?In additional, I am aware of the existence of
image.imageSizes
, but I cannot set it because my project has over 50 pages, each with a unique set of image sizes. I really don't understand the purpose of this option, as each image has unique proportions. This option should be generated for each image individually, rather than universally for all, and certainly not have predefined values exceeding the actual image sizes.Input:
Output:
Expected Behavior
Why does it matter?
1400px
800px
450px
If you pay attention, you'll notice that for a medium-sized screen (800px), a larger image is being used compared to a larger screen (1400px). And this is a common practice on many websites.