0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-17 23:11:29 -05:00

feat: add responsive support to picture component

This commit is contained in:
Matt Kane 2024-11-13 11:39:44 +00:00
parent fd68ea9771
commit 8e2379c0c1
3 changed files with 122 additions and 17 deletions

View file

@ -23,14 +23,16 @@ if (typeof props.height === 'string') {
props.height = parseInt(props.height);
}
const { experimentalResponsiveImages } = imageConfig;
const layoutClassMap = {
fixed: 'aim-fi',
responsive: 'aim-re',
'full-width': '',
};
if (experimentalResponsiveImages) {
const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
if (useResponsive) {
// Apply defaults from imageConfig if not provided
props.layout ??= imageConfig.experimentalLayout;
props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
@ -56,12 +58,12 @@ const { style = '', class: className, ...attrs } = { ...additionalAttributes, ..
---
{
experimentalResponsiveImages && props.layout ? (
useResponsive ? (
<img
src={image.src}
{...attrs}
{style}
class={`${layoutClassMap[props.layout ?? imageConfig.experimentalLayout] ?? ''} aim ${className ?? ''}`.trim()}
class={`${layoutClassMap[layout] ?? ''} aim ${className ?? ''}`.trim()}
/>
) : (
<img src={image.src} {...additionalAttributes} {...image.attributes} class={className} />
@ -69,13 +71,12 @@ const { style = '', class: className, ...attrs } = { ...additionalAttributes, ..
}
<style
define:vars={experimentalResponsiveImages &&
props.layout && {
w: image.attributes.width ?? props.width ?? image.options.width,
h: image.attributes.height ?? props.height ?? image.options.height,
fit: cssFitValues.includes(props.fit) && props.fit,
pos: props.position,
}}
define:vars={useResponsive && {
w: image.attributes.width ?? props.width ?? image.options.width,
h: image.attributes.height ?? props.height ?? image.options.height,
fit: cssFitValues.includes(props.fit ?? '') && props.fit,
pos: props.position,
}}
>
/* Shared by all Astro images */
.aim {

View file

@ -1,9 +1,13 @@
---
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
import { type LocalImageProps, type RemoteImageProps, getImage, imageConfig } from 'astro:assets';
import * as mime from 'mrmime';
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
import type { GetImageResult, ImageOutputFormat } from '../dist/types/public/index.js';
import type {
GetImageResult,
ImageOutputFormat,
UnresolvedImageTransform,
} from '../dist/types/public/index.js';
import type { HTMLAttributes } from '../types';
type Props = (LocalImageProps | RemoteImageProps) & {
@ -37,6 +41,23 @@ if (scopedStyleClass) {
pictureAttributes.class = scopedStyleClass;
}
}
const layoutClassMap = {
fixed: 'aim-fi',
responsive: 'aim-re',
'full-width': '',
};
const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
if (useResponsive) {
// Apply defaults from imageConfig if not provided
props.layout ??= imageConfig.experimentalLayout;
props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
props.position ??= imageConfig.experimentalObjectPosition ?? 'center';
}
for (const key in props) {
if (key.startsWith('data-astro-cid')) {
pictureAttributes[key] = props[key];
@ -53,7 +74,7 @@ const optimizedImages: GetImageResult[] = await Promise.all(
format: format,
widths: props.widths,
densities: props.densities,
}),
} as UnresolvedImageTransform),
),
);
@ -71,7 +92,7 @@ const fallbackImage = await getImage({
format: resultFallbackFormat,
widths: props.widths,
densities: props.densities,
});
} as UnresolvedImageTransform);
const imgAdditionalAttributes: HTMLAttributes<'img'> = {};
const sourceAdditionalAttributes: HTMLAttributes<'source'> = {};
@ -84,6 +105,16 @@ if (props.sizes) {
if (fallbackImage.srcSet.values.length > 0) {
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
}
const cssFitValues = ['fill', 'contain', 'cover', 'scale-down'];
const {
class: className,
style,
...attrs
} = {
...imgAdditionalAttributes,
...fallbackImage.attributes,
};
if (import.meta.env.DEV) {
imgAdditionalAttributes['data-image-component'] = 'true';
@ -106,5 +137,43 @@ if (import.meta.env.DEV) {
);
})
}
<img src={fallbackImage.src} {...imgAdditionalAttributes} {...fallbackImage.attributes} />
{
useResponsive ? (
<img
src={fallbackImage.src}
{style}
{...attrs}
class={`${layoutClassMap[layout] ?? ''} aim ${className ?? ''}`.trim()}
/>
) : (
<img src={fallbackImage.src} {...attrs} {style} class={className} />
)
}
</picture>
<style
define:vars={useResponsive && {
w: fallbackImage.attributes.width ?? props.width ?? fallbackImage.options.width,
h: fallbackImage.attributes.height ?? props.height ?? fallbackImage.options.height,
fit: cssFitValues.includes(props.fit ?? '') && props.fit,
pos: props.position,
}}
>
/* Shared by all Astro images */
.aim {
width: 100%;
height: auto;
object-fit: var(--fit);
object-position: var(--pos);
aspect-ratio: var(--w) / var(--h);
}
/* Styles for responsive layout */
.aim-re {
max-width: calc(var(--w) * 1px);
max-height: calc(var(--h) * 1px);
}
/* Styles for fixed layout */
.aim-fi {
width: calc(var(--w) * 1px);
height: calc(var(--h) * 1px);
}
</style>

View file

@ -0,0 +1,35 @@
---
import { Picture } from "astro:assets";
import myImage from "../assets/penguin.jpg";
---
<div id="picture-density-2-format">
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" formats={['avif', 'webp']} />
</div>
<div id="picture-widths">
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" widths={[myImage.width]} sizes="(max-width: 448px) 400px, (max-width: 810px) 750px, 1050px" />
</div>
<div id="picture-fallback">
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" />
</div>
<div id="picture-attributes">
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" class="img-comp" pictureAttributes={{ class: 'picture-comp' }} />
</div>
<div id="picture-mime-types">
<Picture alt="A penguin" src={myImage} formats={['jpg', 'jpeg', 'png', 'avif', 'webp']} />
</div>
<style>
.img-comp {
border: 5px solid blue;
}
.picture-comp {
border: 5px solid red;
display: inline-block;
}
</style>