fix(assets): Don't include asked width in srcset request

This commit is contained in:
Princesseuh 2023-10-10 12:18:41 +02:00
parent fc86fdf0c3
commit bfe8e09ba6
No known key found for this signature in database
GPG key ID: 105BBD6D57F2B0C0
4 changed files with 24 additions and 18 deletions

View file

@ -6,7 +6,9 @@ Add support for generating multiple widths when using the Image component and a
## `srcset` support ## `srcset` support
The current usage is as follow: Two new properties have been added to `Image` and `getImage`: `densities` and `widths`.
These props can be used to generate a `srcset` attribute with multiple sources. For example:
```astro ```astro
--- ---
@ -14,17 +16,13 @@ import { Image } from "astro";
import myImage from "./my-image.jpg"; import myImage from "./my-image.jpg";
--- ---
<Image src={myImage} densities={[2, 3]} alt="My cool image" /> <Image src={myImage} width={myImage.width / 2} densities={[2]} alt="My cool image" />
``` ```
Alternatively to `densities`, `widths` can be used for specific widths. In both cases, according images and the following code will be generated:
```html ```html
<img src="..." srcset="... 2x, ... 3x" alt="My cool image" /> <img src="..." srcset="... 2x, ... 3x" alt="My cool image" />
``` ```
(if `widths` is used the descriptor will be `w` instead of `x`)
## Picture component ## Picture component
The `Picture` component can be used to generate a `<picture>` element with multiple sources. It can be used as follow: The `Picture` component can be used to generate a `<picture>` element with multiple sources. It can be used as follow:
@ -35,7 +33,7 @@ import { Picture } from "astro:assets";
import myImage from "./my-image.jpg"; import myImage from "./my-image.jpg";
--- ---
<Picture src={myImage} formats=["avif", "webp"] alt="My super image in multiple formats!" /> <Picture src={myImage} formats={["avif", "webp"]} alt="My super image in multiple formats!" />
``` ```
The above code will generate the following: The above code will generate the following:
@ -48,4 +46,4 @@ The above code will generate the following:
</picture> </picture>
``` ```
The `Picture` component takes all the same props as the `Image` component, including `densities` and `widths`. The `Picture` component takes all the same props as the `Image` component, including the new `densities` and `widths` properties.

View file

@ -45,7 +45,10 @@ interface SharedServiceProps<T extends Record<string, any> = Record<string, any>
*/ */
getURL: (options: ImageTransform, imageConfig: ImageConfig<T>) => string | Promise<string>; getURL: (options: ImageTransform, imageConfig: ImageConfig<T>) => string | Promise<string>;
/** /**
* TODO: Document * Generate additional `srcset` values for the image.
*
* While in most cases this is exclusively used for `srcset`, it can also be used in a more generic way to generate
* multiple variants of the same image. For instance, you can use this to generate multiple aspect ratios or multiple formats.
*/ */
getSrcSet?: ( getSrcSet?: (
options: ImageTransform, options: ImageTransform,
@ -226,8 +229,9 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
const aspectRatio = targetWidth / targetHeight; const aspectRatio = targetWidth / targetHeight;
const imageWidth = isESMImportedImage(options.src) ? options.src.width : options.width; const imageWidth = isESMImportedImage(options.src) ? options.src.width : options.width;
const maxWidth = options.width ?? imageWidth ?? Infinity; const maxWidth = imageWidth ?? Infinity;
// REFACTOR: Could we merge these two blocks?
if (densities) { if (densities) {
const densityValues = densities.map((density) => { const densityValues = densities.map((density) => {
if (typeof density === 'number') { if (typeof density === 'number') {
@ -244,9 +248,12 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
densityWidths.forEach((width, index) => { densityWidths.forEach((width, index) => {
const maxTargetWidth = Math.min(width, maxWidth); const maxTargetWidth = Math.min(width, maxWidth);
// If the user passed dimensions, we don't want to add it to the srcset
const { width: transformWidth, height: transformHeight, ...rest } = options;
const srcSetValue = { const srcSetValue = {
transform: { transform: {
...options, ...rest,
}, },
descriptor: `${densityValues[index]}x`, descriptor: `${densityValues[index]}x`,
attributes: { attributes: {
@ -254,7 +261,7 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
}, },
}; };
// Only set width and height if they are different from the original image // Only set width and height if they are different from the original image, to avoid duplicated final images
if (maxTargetWidth !== imageWidth) { if (maxTargetWidth !== imageWidth) {
srcSetValue.transform.width = maxTargetWidth; srcSetValue.transform.width = maxTargetWidth;
srcSetValue.transform.height = Math.round(maxTargetWidth / aspectRatio); srcSetValue.transform.height = Math.round(maxTargetWidth / aspectRatio);
@ -270,9 +277,11 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
widths.forEach((width) => { widths.forEach((width) => {
const maxTargetWidth = Math.min(width, maxWidth); const maxTargetWidth = Math.min(width, maxWidth);
const { width: transformWidth, height: transformHeight, ...rest } = options;
const srcSetValue = { const srcSetValue = {
transform: { transform: {
...options, ...rest,
}, },
descriptor: `${width}w`, descriptor: `${width}w`,
attributes: { attributes: {
@ -280,7 +289,6 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
}, },
}; };
// Only set width and height if they are different from the original image
if (maxTargetWidth !== imageWidth) { if (maxTargetWidth !== imageWidth) {
srcSetValue.transform.width = maxTargetWidth; srcSetValue.transform.width = maxTargetWidth;
srcSetValue.transform.height = Math.round(maxTargetWidth / aspectRatio); srcSetValue.transform.height = Math.round(maxTargetWidth / aspectRatio);

View file

@ -50,9 +50,9 @@ const sharpService: LocalImageService = {
// Never resize using both width and height at the same time, prioritizing width. // Never resize using both width and height at the same time, prioritizing width.
if (transform.height && !transform.width) { if (transform.height && !transform.width) {
result.resize({ height: transform.height }); result.resize({ height: Math.round(transform.height) });
} else if (transform.width) { } else if (transform.width) {
result.resize({ width: transform.width }); result.resize({ width: Math.round(transform.width) });
} }
if (transform.format) { if (transform.format) {

View file

@ -77,12 +77,12 @@ const service: LocalImageService = {
if (transform.height && !transform.width) { if (transform.height && !transform.width) {
operations.push({ operations.push({
type: 'resize', type: 'resize',
height: transform.height, height: Math.round(transform.height),
}); });
} else if (transform.width) { } else if (transform.width) {
operations.push({ operations.push({
type: 'resize', type: 'resize',
width: transform.width, width: Math.round(transform.width),
}); });
} }